大家好,我系蒼王。
以下是我這個系列的相關文章,有興趣可以參考一下,可以給個喜歡或者關注我的文章。
[Android]如何做一個崩潰率少于千分之三噶應用app--章節列表
Google爸爸,聽說要將一些插件化hook系統的變量屬性禁用,Android P之后很可能將會不再有插件化、熱更新、主題變換、資源加固等騷操作。試圖hook,你將會看到 NoSuchFieldException 或者 NoSuchMethodException 等錯誤提示。
可見文章Android P 調用隱藏API限制原理中對api隱藏說明
具體通過@hide的注釋讓屬性提示變量不存在。
這樣就會要求app上線前測試更加嚴謹,而不是在上線后通過各種修復替換功能等方式,每周發版的日子,將不會出現了,不停歇的加班。
RN技術其原理涉及到view的渲染,暫時并未受到波及。
現在國內,有繼續走RN的,各大廠有走類似小程序方向的快應用,都是使用js語法,寫web還能拯救一堆程序猿啊。
接下來說一下進程通信,其實任何的進程通信方式,都可以在組件化開發中使用。
Android中進程間通信的方式
1.Aidl
2.Messenger
3.Content provider
4.Socket
5.文件共享
前三個都是基于binder機制實現的。
本節想要介紹的是使用aidl做的進程通信,單單使用aidl進行通信其實并不難。原理也有很多文章介紹過,但是如何設計一個通用的aidl通信架構,就需要考究了。
這里介紹的是ModularizationArchitecture中使用的aidl的通信架構。
這里ModularizationArchitecture架構使用了aidl作為路由傳輸的實現。
1.每個需要通信的module,都需要繼承MaProvider類,然后在BaseApplicationLogic啟動的時候注冊。
2.MaAction作為觸發的事件,繼承出來改寫其方法,invoke方法是事件實現。需要在MaProvider中注冊事件。
3.MaActionResult是事件結果回調。
4.LocalRouter是當前進程調用MaAction中的invoke執行方法。
5.WideRouter是跨進程調用時使用,需要在MaApplication啟動的時候注冊將module中的的LocalRouterConnectService。
注冊提供內容
注冊進程路由信息到廣域路由中
public class MyApplication extends MaApplication {//注冊進程路由信息到廣域路由@Overridepublic void initializeAllProcessRouter() {WideRouter.registerLocalRouter("com.spinytech.maindemo",MainRouterConnectService.class);WideRouter.registerLocalRouter("com.spinytech.maindemo:music",MusicRouterConnectService.class);WideRouter.registerLocalRouter("com.spinytech.maindemo:pic",PicRouterConnectService.class);}//初始化進程啟動@Overrideprotected void initializeLogic() {registerApplicationLogic("com.spinytech.maindemo",999, MainApplicationLogic.class);registerApplicationLogic("com.spinytech.maindemo",998, WebApplicationLogic.class);registerApplicationLogic("com.spinytech.maindemo:music",999, MusicApplicationLogic.class);registerApplicationLogic("com.spinytech.maindemo:pic",999, PicApplicationLogic.class);}//是否使用多進程@Overridepublic boolean needMultipleProcess() {return true;}
}
復制代碼
進程初始化的時候注冊MaProvider到進程路由中
public class MainApplicationLogic extends BaseApplicationLogic {@Overridepublic void onCreate() {super.onCreate();//注冊ProviderLocalRouter.getInstance(mApplication).registerProvider("main",new MainProvider());}
}
復制代碼
為每個Provider綁定可以觸發的Action任務
public class MainProvider extends MaProvider {@Overrideprotected void registerActions() {registerAction("sync",new SyncAction());registerAction("async",new AsyncAction());registerAction("attachment",new AttachObjectAction());}
}
復制代碼
下面是進程內同步通信
進程內調用
RouterResponse response = LocalRouter.getInstance(MaApplication.getMaApplication()) //進程中單例LocalRouter.route(MainActivity.this, RouterRequest.obtain(MainActivity.this) //在緩存池中獲取請求.provider("main") //設定provider.action("sync") //設定調用的action.data("1", "Hello") //設定數據.data("2", "World"));
復制代碼
通過注冊的內容找到相應的action,然后調用action中的invoke方法
public RouterResponse route(Context context, @NonNull RouterRequest routerRequest) throws Exception {Logger.d(TAG, "Process:" + mProcessName + "\nLocal route start: " + System.currentTimeMillis());RouterResponse routerResponse = new RouterResponse();// Local request//檢查domain是不是在同一個進程if (mProcessName.equals(routerRequest.getDomain())) {HashMap<String, String> params = new HashMap<>();Object attachment = routerRequest.getAndClearObject();params.putAll(routerRequest.getData());Logger.d(TAG, "Process:" + mProcessName + "\nLocal find action start: " + System.currentTimeMillis());//通過provider索引到actionMaAction targetAction = findRequestAction(routerRequest);routerRequest.isIdle.set(true);Logger.d(TAG, "Process:" + mProcessName + "\nLocal find action end: " + System.currentTimeMillis());routerResponse.mIsAsync = attachment == null ? targetAction.isAsync(context, params) : targetAction.isAsync(context, params, attachment);// Sync result, return the result immediately// 同步調用.if (!routerResponse.mIsAsync) {//調用action的實現MaActionResult result = attachment == null ? targetAction.invoke(context, params) : targetAction.invoke(context, params, attachment);//包裝responserouterResponse.mResultString = result.toString();routerResponse.mObject = result.getObject();Logger.d(TAG, "Process:" + mProcessName + "\nLocal sync end: " + System.currentTimeMillis());}
復制代碼
下面是進程內異步通信
route方法中在mIsAsync設定是否異步
public RouterResponse route(Context context, @NonNull RouterRequest routerRequest) throws Exception {Logger.d(TAG, "Process:" + mProcessName + "\nLocal route start: " + System.currentTimeMillis());RouterResponse routerResponse = new RouterResponse();// Local request//檢查domain是不是在同一個進程if (mProcessName.equals(routerRequest.getDomain())) {...// Sync result, return the result immediately// 同步調用.if (!routerResponse.mIsAsync) {...}// Async result, use the thread pool to execute the task.//異步調用else {//創建異步任務LocalTask task = new LocalTask(routerResponse, params,attachment, context, targetAction);//通過線程池調用routerResponse.mAsyncResponse = getThreadPool().submit(task);}
復制代碼
異步調用任務是使用Callback任務
//使用Future Callable的方式使用線程池private class LocalTask implements Callable<String> {private RouterResponse mResponse;private HashMap<String, String> mRequestData;private Context mContext;private MaAction mAction;private Object mObject;public LocalTask(RouterResponse routerResponse, HashMap<String, String> requestData,Object object, Context context, MaAction maAction) {this.mContext = context;this.mResponse = routerResponse;this.mRequestData = requestData;this.mAction = maAction;this.mObject = object;}@Overridepublic String call() throws Exception {//調用action中的invoke方法MaActionResult result = mObject == null ? mAction.invoke(mContext, mRequestData) : mAction.invoke(mContext, mRequestData, mObject);mResponse.mObject = result.getObject();Logger.d(TAG, "Process:" + mProcessName + "\nLocal async end: " + System.currentTimeMillis());return result.toString();}}
復制代碼
下面是跨進程通信
使用aidl調用廣域的WideRouter
//檢查domain是不是在同一個進程if (mProcessName.equals(routerRequest.getDomain())) {...}// IPC requestelse {//獲取進程domainString domain = routerRequest.getDomain();String routerRequestString = routerRequest.toString();routerRequest.isIdle.set(true);//檢查是不已經綁定了廣域路由WideRouterif (checkWideRouterConnection()) {Logger.d(TAG, "Process:" + mProcessName + "\nWide async check start: " + System.currentTimeMillis());//If you don't need wide async check, use "routerResponse.mIsAsync = false;" replace the next line to improve performance.//檢查是同步還是異步routerResponse.mIsAsync = mWideRouterAIDL.checkResponseAsync(domain, routerRequestString);Logger.d(TAG, "Process:" + mProcessName + "\nWide async check end: " + System.currentTimeMillis());}// Has not connected with the wide router.else {//調用連接廣域路由WideRouterrouterResponse.mIsAsync = true;ConnectWideTask task = new ConnectWideTask(routerResponse, domain, routerRequestString);routerResponse.mAsyncResponse = getThreadPool().submit(task);return routerResponse;}//同步調用if (!routerResponse.mIsAsync) {//aidl傳輸給相關進程的LocalRouterConnectServicerouterResponse.mResultString = mWideRouterAIDL.route(domain, routerRequestString);Logger.d(TAG, "Process:" + mProcessName + "\nWide sync end: " + System.currentTimeMillis());}// Async result, use the thread pool to execute the task.//異步調用else {//設定廣域調用任務WideTask task = new WideTask(domain, routerRequestString);routerResponse.mAsyncResponse = getThreadPool().submit(task);}}//返回ReouterResponsereturn routerResponse;
復制代碼
廣域路由連接檢測,調用aidl連接到WideRouterConnectService
private class ConnectWideTask implements Callable<String> {private RouterResponse mResponse;private String mDomain;private String mRequestString;public ConnectWideTask(RouterResponse routerResponse, String domain, String requestString) {this.mResponse = routerResponse;this.mDomain = domain;this.mRequestString = requestString;}@Overridepublic String call() throws Exception {Logger.d(TAG, "Process:" + mProcessName + "\nBind wide router start: " + System.currentTimeMillis());//綁定WideRouterConnectServiceconnectWideRouter();int time = 0;while (true) {//等待廣域路由綁定完成if (null == mWideRouterAIDL) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}time++;} else {break;}//超過30秒就放棄,拋出錯誤if (time >= 600) {ErrorAction defaultNotFoundAction = new ErrorAction(true, MaActionResult.CODE_CANNOT_BIND_WIDE, "Bind wide router time out. Can not bind wide router.");MaActionResult result = defaultNotFoundAction.invoke(mApplication, new HashMap<String, String>());mResponse.mResultString = result.toString();return result.toString();}}Logger.d(TAG, "Process:" + mProcessName + "\nBind wide router end: " + System.currentTimeMillis());//調用關于路由傳輸到對應的進程路由String result = mWideRouterAIDL.route(mDomain, mRequestString);Logger.d(TAG, "Process:" + mProcessName + "\nWide async end: " + System.currentTimeMillis());return result;}}
復制代碼
WideRouterConnectService對對應的進程路由分發通信,監聽返回。
//廣域路由處理IWideRouterAIDL.Stub stub = new IWideRouterAIDL.Stub() {@Overridepublic boolean checkResponseAsync(String domain, String routerRequest) throws RemoteException {//檢查是否異步調動return WideRouter.getInstance(MaApplication.getMaApplication()).answerLocalAsync(domain, routerRequest);}@Overridepublic String route(String domain, String routerRequest) {try {//廣域路由分發到對應的進程路由中return WideRouter.getInstance(MaApplication.getMaApplication()).route(domain, routerRequest).mResultString;} catch (Exception e) {e.printStackTrace();return new MaActionResult.Builder().code(MaActionResult.CODE_ERROR).msg(e.getMessage()).build().toString();}}@Overridepublic boolean stopRouter(String domain) throws RemoteException {//停止連接進程路由return WideRouter.getInstance(MaApplication.getMaApplication()).disconnectLocalRouter(domain);}};
復制代碼
根據domain分發到對應進程的ILocalRouterAIDL
public RouterResponse route(String domain, String routerRequest) {Logger.d(TAG, "Process:" + PROCESS_NAME + "\nWide route start: " + System.currentTimeMillis());RouterResponse routerResponse = new RouterResponse();//是否已經被要求停止任務if (mIsStopping) {MaActionResult result = new MaActionResult.Builder().code(MaActionResult.CODE_WIDE_STOPPING).msg("Wide router is stopping.").build();routerResponse.mIsAsync = true;routerResponse.mResultString = result.toString();return routerResponse;}//廣域路由不能作為調用對象if (PROCESS_NAME.equals(domain)) {MaActionResult result = new MaActionResult.Builder().code(MaActionResult.CODE_TARGET_IS_WIDE).msg("Domain can not be " + PROCESS_NAME + ".").build();routerResponse.mIsAsync = true;routerResponse.mResultString = result.toString();return routerResponse;}//獲取對應進程路由的對象ILocalRouterAIDL target = mLocalRouterAIDLMap.get(domain);if (null == target) {//是否已經綁定了本地路由,沒有就啟動綁定if (!connectLocalRouter(domain)) {MaActionResult result = new MaActionResult.Builder().code(MaActionResult.CODE_ROUTER_NOT_REGISTER).msg("The " + domain + " has not registered.").build();routerResponse.mIsAsync = false;routerResponse.mResultString = result.toString();Logger.d(TAG, "Process:" + PROCESS_NAME + "\nLocal not register end: " + System.currentTimeMillis());return routerResponse;} else {// Wait to bind the target process connect service, timeout is 30s.Logger.d(TAG, "Process:" + PROCESS_NAME + "\nBind local router start: " + System.currentTimeMillis());int time = 0;//等待完成綁定進程連接while (true) {target = mLocalRouterAIDLMap.get(domain);if (null == target) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}time++;} else {Logger.d(TAG, "Process:" + PROCESS_NAME + "\nBind local router end: " + System.currentTimeMillis());break;}//設定30s超時if (time >= 600) {MaActionResult result = new MaActionResult.Builder().code(MaActionResult.CODE_CANNOT_BIND_LOCAL).msg("Can not bind " + domain + ", time out.").build();routerResponse.mResultString = result.toString();return routerResponse;}}}}try {Logger.d(TAG, "Process:" + PROCESS_NAME + "\nWide target start: " + System.currentTimeMillis());//對應進程調用返回String resultString = target.route(routerRequest);routerResponse.mResultString = resultString;Logger.d(TAG, "Process:" + PROCESS_NAME + "\nWide route end: " + System.currentTimeMillis());} catch (RemoteException e) {e.printStackTrace();MaActionResult result = new MaActionResult.Builder().code(MaActionResult.CODE_REMOTE_EXCEPTION).msg(e.getMessage()).build();routerResponse.mResultString = result.toString();return routerResponse;}return routerResponse;}
復制代碼
基本原理就介紹到這里了。
1.aidl是google為Android進程通信提供的方式,使用了代理模式,其內部集成了IBinder,使用了Binder的方式通信,已經成為套路的規則,維護成本低。
2.當序列化后的數據單元過大時,就會出問題,報出android.os.TransactionTooLargeException。其數據量限制為1M
3.原理上說就是binder只拷貝一次,使用虛擬內存和物理內存頁映射,比socket高效,也安全。
4.這里介紹的框架,其中是通過本地路由和廣域路由間的傳送和切換來完成。只能交流變量和調用方法,無法通過aidl獲取資源。本地路由能力并未有ARouter使用的方便,進程內對無法提供獲取Fragment View等資源獲取,可以考慮拓展。但是此本地和廣域路由設計非常優秀。
5.wutongke有出了一個框架加上編譯時注解的優化版github.com/wutongke/Mo…
下一節將會繼續介紹Messenger進程通信框架,敬請期待。