大家好,我系苍王。
以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。
[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表
Google爸爸,听说要将一些插件化hook系统的变量属性禁用,Android P之后很可能将会不再有插件化、热更新、主题变换、资源加固等骚操作。试图hook,你将会看到 NoSuchFieldException 或者 NoSuchMethodException 等错误提示。
可见文章Android P 调用隐藏API限制原理中对api隐藏说明
具体通过@hide的注释让属性提示变量不存在。
这样就会要求app上线前测试更加严谨,而不是在上线后通过各种修复替换功能等方式,每周发版的日子,将不会出现了,不停歇的加班。
RN技术其原理涉及到view的渲染,暂时并未受到波及。
现在国内,有继续走RN的,各大厂有走类似小程序方向的快应用,都是使用js语法,写web还能拯救一堆程序猿啊。
接下来说一下进程通信,其实任何的进程通信方式,都可以在组件化开发中使用。
Android中进程间通信的方式
1.Aidl
2.Messager
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 {
//注册进程路由信息到广域路由
@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());
}
下面是进程内异步通信
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
下一节将会继续介绍Messager进程通信框架,敬请期待。