大家好, 我系苍王
[Android] 如何做一个崩溃率少于千分之三噶应用 app-- 章节列表 https://www.jianshu.com/p/94a05b996d78
Google 爸爸, 听说要将一些插件化 hook 系统的变量属性禁用, Android P 之后很可能将会不再有插件化热更新主题变换资源加固等骚操作试图 hook, 你将会看到 NoSuchFieldException 或者 NoSuchMethodException 等错误提示
可见文章 Android P 调用隐藏 API 限制原理 https://link.jianshu.com/?t=https://mp.weixin.qq.com/s?__biz=MzUxODQ3MTk5Mg==&mid=2247483899&idx=1&sn=b471ce18b425696dfc5d9805328f1794&chksm=f98929accefea0ba5ecbf0917b71a5cb223f4d875eb769da35740ddd8d577b4daaba2735479a&mpshare=1&scene=23&srcid=03288AoxG2Q9atEXryA0821D#rd 中对 api 隐藏说明
具体通过 @hide 的注释让属性提示变量不存在
这样就会要求 app 上线前测试更加严谨, 而不是在上线后通过各种修复替换功能等方式, 每周发版的日子, 将不会出现了, 不停歇的加班
RN 技术其原理涉及到 view 的渲染, 暂时并未受到波及
现在国内, 有继续走 RN 的, 各大厂有走类似小程序方向的快应用, 都是使用 js 语法, 写 web 还能拯救一堆程序猿啊
接下来说一下进程通信, 其实任何的进程通信方式, 都可以在组件化开发中使用
Android 中进程间通信的方式
- Aidl
- Messager
- Content provider
- Socket
5. 文件共享
前三个都是基于 binder 机制实现的
本节想要介绍的是使用 aidl 做的进程通信, 单单使用 aidl 进行通信其实并不难原理也有很多文章介绍过, 但是如何设计一个通用的 aidl 通信架构, 就需要考究了
这里介绍的是 ModularizationArchitecture 中使用的 aidl 的通信架构
ModularizationArchitecture 通信架构
这里 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 {
- // 注册进程路由信息到广域路由
- @Override
- public 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);
- }
- // 初始化进程启动
- @Override
- protected 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);
- }
- // 是否使用多进程
- @Override
- public boolean needMultipleProcess() {
- return true;
- }
- }
进程初始化的时候注册 MaProvider 到进程路由中
- public class MainApplicationLogic extends BaseApplicationLogic {
- @Override
- public void onCreate() {
- super.onCreate();
- // 注册 Provider
- LocalRouter.getInstance(mApplication).registerProvider("main",new MainProvider());
- }
- }
为每个 Provider 绑定可以触发的 Action 任务
- public class MainProvider extends MaProvider {
- @Override
- protected 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 索引到 action
- MaAction 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);
- // 包装 response
- routerResponse.mResultString = result.toString();
- routerResponse.mObject = result.getObject();
- Logger.d(TAG, "Process:" + mProcessName + "\nLocal sync end:" + System.currentTimeMillis());
- }
下面是进程内异步通信
进程内异步通信 [图片上传中...(屏幕快照 2018-03-30 下午 12.36.03.png-c9d7e7-1522384627443-0)]
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;
- }
- @Override
- public 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 request
- else {
- // 获取进程 domain
- String domain = routerRequest.getDomain();
- String routerRequestString = routerRequest.toString();
- routerRequest.isIdle.set(true);
- // 检查是不已经绑定了广域路由 WideRouter
- if (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 {
- // 调用连接广域路由 WideRouter
- routerResponse.mIsAsync = true;
- ConnectWideTask task = new ConnectWideTask(routerResponse, domain, routerRequestString);
- routerResponse.mAsyncResponse = getThreadPool().submit(task);
- return routerResponse;
- }
- // 同步调用
- if (!routerResponse.mIsAsync) {
- //aidl 传输给相关进程的 LocalRouterConnectService
- routerResponse.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);
- }
- }
- // 返回 ReouterResponse
- return 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;
- }
- @Override
- public String call() throws Exception {
- Logger.d(TAG, "Process:" + mProcessName + "\nBind wide router start:" + System.currentTimeMillis());
- // 绑定 WideRouterConnectService
- connectWideRouter();
- 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() {
- @Override
- public boolean checkResponseAsync(String domain, String routerRequest) throws RemoteException {
- // 检查是否异步调动
- return WideRouter.getInstance(MaApplication.getMaApplication())
- .answerLocalAsync(domain, routerRequest);
- }
- @Override
- public 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();
- }
- }
- @Override
- public 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 有出了一个框架加上编译时注解的优化版 https://github.com/wutongke/ModularizationArchitecture
来源: https://juejin.im/post/5abf015f6fb9a028c5234dd3