?{

???//通过服务名称获取Binder包装类BinderBean

???BinderBean?getTargetBinder(String?serviceCanonicalName);

???//保留接口暂时为空实现

???IBinder?fetchTargetBinder(String?uri);

???//注册本地的RemoteTransfer

???void?registerRemoteTransfer(int?pid,IBinder?remoteTransferBinder);

???//注册/反注册远程服务

???void?registerRemoteService(String?serviceCanonicalName,String?processName,IBinder?Binder);

???void?unregisterRemoteService(String?serviceCanonicalName);

???//发送事件

???void?publish(in?Event?event);

} 

Dispatcher所在进程可以是主进程也可以用户自定义的进程,为什么要讨论Dispatcher所属进程呢?因为作为组件化通信核心的Center一旦狗带,将导致之前注册服务不可用,所以需要将它放在应用生命周期最长的进程中,通常这个进程是主进程,但对于类似音乐播放器相关的app来说,可能是一个独立的播放器进程,所以框架为我们提供了一个配置项可以显式的声明Dispatcher所在进程。


#主工程的build.gradle添加声明

dispatcher{

????process?":downloader"

} 

[]( )Dispatcher架构图

爱奇艺开源的组件化跨进程通信解决方案

RemoteTransfer上面提到各个进程自己本身也需要管理(缓存)从Dispatcher获取的Binder,防止重复的IPC请求;另外由于事件总线的需求,各个进程需要向Dispatcher进程注册本进程组件管理员,这样当事件pubish后,Dispatcher才能将事件发送给各个进程,这个各个进程管理员就是RemoteTransfer。IRemoteTransfer是一个AIDL接口,RemoteTransfer是它的实现类,RemoteTransfer还实现了IRemoteServiceTransfer接口。这里需要一张类图来帮你理清思路:爱奇艺开源的组件化跨进程通信解决方案


#IRemoteTransfer.aidl

interface?IRemoteTransfer?{

????//①?将Dispatcher代理返回给RemoteTransfer

????oneway?void?registerDispatcher(IBinder?dispatcherBinder);

????oneway?void?unregisterRemoteService(String?serviceCanonicalName);

????oneway?void?notify(in?Event?event);

}

#IRemoteServiceTransfer.java

public?interface?IRemoteServiceTransfer?{

????//②获取远程服务包装

????BinderBean?getRemoteServiceBean(String?serviceCanonicalName);

????//注册/反注册?远程服务

????void?registerStubService(String?serviceCanonicalName,?IBinder?stubBinder);

????void?unregisterStubService(String?serviceCanonicalName);

}

两个问题需要注意: ① 方法的调用方在Dispatcher中,这样就把Dispatcher的远程代理回传给了当前进程,之后注册远程服务就可以通过这个DispatcherProxy完成。② 无论是注册还是获取远程服务,都是不能直接传递Binder的,因为Binder并没有实现Parcelable接口,因此需要将Binder包装在一个实现了Parcelable接口的类中传递,BinderBean就是其中一个包装类。主体逻辑已经讲清楚了,我们正式开始分析功能。

  • 通过ContentProvider方式同步的获取Dispatcher的代理,这个ContentProvider属于Dispatcher进程,且通过插桩的方式织入manifeset文件。

  • 获取远程服务时传递当前进程的Activity或Fragment,并bind预先插桩好的StubService,这个StubService属于远程服务所在进程。

这是整个Andromeda工程最最核心的原理,你是不是快看不懂了,没关系,下面会结合时序图、关系图详细分析实现过程。本地服务本地服务没什么讲的,内部通过维护一个Map关系表,来记录注册服务的名称和实现类。


#?LocalServiceHub

public?class?LocalServiceHub?implements?ILocalServiceHub?{

????private?Map<String,?Object>?serviceMap?=?new?ConcurrentHashMap<>();

????@Override

????public?Object?getLocalService(String?module)?{

????????return?serviceMap.get(module);

????}

????@Override

????public?void?registerService(String?module,?Object?serviceImpl)?{

????????serviceMap.put(module,?serviceImpl);

????}

????@Override

????public?void?unregisterService(String?module)?{

????????serviceMap.remove(module);

????}

} 

远程服务 远程服务是框架的核心,对远程服务的操作就是两个,一是注册远程服务,二是获取远程服务。我们先来看服务的注册,时序图如下 ↓爱奇艺开源的组件化跨进程通信解决方案

  1. 客户端通过 registerRemoteService(String serviceCanonicalName, T stubBinder)注册本进程可提供的远程服务,stubBinder即服务实现类。

  2. 调用RemoteTransfer的registerStubService方法。

  3. registerStubService内部先初始化DispatcherProxy,如果为空跳转3.1。3.1-3.2 要实现服务的同步注册,本质上是同步获取DispatcherProxy,这是一次IPC通信,Andromeda的方案是在Dispatcher进程插桩一个ContentProvider,然后返回一个包含DispatcherProxy的Cursor给客户端进程,客户端解析Cursor拿到DispatcherProxy。

  4. RemoteTransfer请求RemoteServiceTransfer帮忙完成真正的注册。

  5. RemoteServiceTransfer通过第3步获取的DispatcherProxy,做一次IPC通信,将Binder传递到Dispatcher进程。

  6. Dispatcher进程请求ServiceDispatcher类帮忙完成服务的注册,其实就是将Binder存储在一个Map当中。

图中蓝色的节点表示注册服务的当前进程,也就是Server进程,红色节点表示Dispatcher进程。整个过程重点在第三步,我们再重点分析一下:


#?RemoteTransfer

private?void?initDispatchProxyLocked()?{

????if?(null?==?dispatcherProxy)?{

????????//从contentprovider取Binder

????????IBinder?dispatcherBinder?=?getIBinderFromProvider();

????????if?(null?!=?dispatcherBinder)?{

????????????//取出后asInterface创建远程代理对象

????????????dispatcherProxy?=?IDispatcher.Stub.asInterface(dispatcherBinder);

????????????registerCurrentTransfer();

????????}

????}

????...

}

private?void?registerCurrentTransfer()?{

????//向Dispatcher注册自己这个进程的RemoteTransfer?Binder

????dispatcherProxy.registerRemoteTransfer(android.os.Process.myPid(),?this.asBinder());

????...

}

private?IBinder?getIBinderFromProvider()?{

????Cursor?cursor?=?null;

????try?{

????????//通过contentprovider拿到cursor

????????cursor?=?context.getContentResolver().query(getDispatcherProviderUri(),?DispatcherProvider.PROJECTION_MAIN,

????????????????null,?null,?null);

????????if?(cursor?==?null)?{

????????????return?null;

????????}

????????return?DispatcherCursor.stripBinder(cursor);

????}?finally?{

????????IOUtils.closeQuietly(cursor);

????}

} 

我们来看这个DispatcherProvider


public?class?DispatcherProvider?extends?ContentProvider?{

????...

????@Override

????public?Cursor?query(Uri?uri,?String[]?projection,?String?selection,?String[]?selectionArgs,?String?sortOrder)?{

????????//将Binder封装到cursor中返回

????????return?DispatcherCursor.generateCursor(Dispatcher.getInstance().asBinder());

????}

} 

接下来我们看服务的获取,同样的先看时序图 ↓爱奇艺开源的组件化跨进程通信解决方案

1. Andromeda入口通过getRemoteService获取远程服务。2-4. 与提升进程优先级有关,我们暂且不讨论。5. 向RemoteTransfer请求获取远程服务的包装bean。6-7. RemoteTransfer请求RemoteServiceTransfer帮忙先从本进程的缓存中查找目标Binder,如果找到直接返回。7.2. 如果没有命中缓存调用getAndSaveIBinder方法,通过方法名可知,获取后会将Binder缓存起来,这就是6-7步读取的缓存。8. RemoteServiceTransfer通过DispatcherProxy发起IPC通信,请求远程服务Binder。9-10. Dispatcher请ServiceDispatcher帮忙查找进程中的服务注册表。11. 回到客户端进程将Binder缓存。12. 将Binder返回给调用方。同样图中蓝色的节点表示获取服务的进程,也就是Client进程,红色节点表示Dispatcher进程。至此,远程服务的注册与获取流程分析结束。

进程优先级上面提到在获取远程服务时,框架做了提升进程优先级的事情。通常情况下使用远程服务的端(简称Client端)处于前台进程,而Server端进程已经注册完毕,往往处于后台。为了提升Server端的稳定性,最好能将Server端的进程优先级与Client保持接近,否则容易出现被LMK(Low Memory Killer)回收的情况。那如何提升Server端进程的优先级呢?这里的做法是用前台的UI组件(Activity/Fragment/View)bind一个Server端预先插桩好的Service。整套流程最终通过AMS的updateOomAdjLocked方法实现。爱奇艺开源的组件化跨进程通信解决方案

回到Andromeda实现,这个预先插桩的Service如下:


public?class?CommuStubService?extends?Service?{

????public?CommuStubService()?{}

????@Override

????public?IBinder?onBind(Intent?intent)?{

????????return?new?ICommuStub.Stub()?{

????????????@Override

????????????public?void?commu(Bundle?args)?throws?RemoteException?{

????????????????//do?nothing?now

????????????}

????????};

????}

????@Override

????public?int?onStartCommand(Intent?intent,?int?flags,?int?startId)?{

????????//这样可以使Service所在进程的保活效果好一点

????????return?Service.START_STICKY;

????}

????public?static?class?CommuStubService0?extends?CommuStubService?{}

????public?static?class?CommuStubService1?extends?CommuStubService?{}

????public?static?class?CommuStubService2?extends?CommuStubService?{}

????...

????public?static?class?CommuStubService14?extends?CommuStubService?{}

} 

复制代码可见框架预置了15个Service供进程使用,也就是最多支持15个进程,这绝大数场景下足够了;另外维护了一个进程名和Service名称的映射表,否则怎么知道应该bind那个Service,这个映射表也是在编译阶段插桩完成的。 这个service的bind过程发生在上一章节获取远程服务时,流程如下图:爱奇艺开源的组件化跨进程通信解决方案

图中模块根据所在进程分为三部分:

  1. 蓝色表示Client进程,发起获取远程服务请求。

  2. 浅灰色表示Server进程,它事先将服务注册到Dispatcher中。

  3. 紫色表示Dispatcher进程,内部缓存了各个进程的服务的Binder对象。

我们重点关注的是蓝色模块ConnectionManager部分,实际上当Client向Dispatcher请求远程服务之后,会立即通过ConnectionManager绑定这个远程服务所在进程的插桩的StubService,如此一来就提升了Server所在进程的优先级。至此bind操作已经完成了,那何时unbind呢?显然是当UI组件销毁时,因为此时已不在前台,需要降低进程优先级。如此一来就需要监听UI组件的生命周期,在onDestroy时进行unbind操作。这就是图中RemoteManager做的事情,它内部维护了前台组件的生命周期。Andromeda提供了几种with方法,用于获取对应RemoteManager:


public?static?IRemoteManager?with(android.app.Fragment?fragment)?{return?getRetriever().get(fragment);}

public?static?IRemoteManager?with(Fragment?fragment)?{return?getRetriever().get(fragment);}

public?static?IRemoteManager?with(FragmentActivity?fragmentActivity)?{return?getRetriever().get(fragmentActivity);}

public?static?IRemoteManager?with(Activity?activity)?{return?getRetriever().get(activity);}

public?static?IRemoteManager?with(Context?context)?{return?getRetriever().get(context);}

public?static?IRemoteManager?with(View?view)?{return?getRetriever().get(view);} </pre>

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件,需要的小伙伴可以点击我的腾讯文档免费获取!

爱奇艺开源的组件化跨进程通信解决方案

爱奇艺开源的组件化跨进程通信解决方案

爱奇艺开源的组件化跨进程通信解决方案