前言
我们首先先看下这个效果是怎么样的,如果有人见过百度音箱或者是小米音箱,就应该对这个会清楚一些,也就是图示中我们是自定义了一个下拉状态栏,并没有使用系统原生的,那么我们就可以根据需求实现我们自定义的下拉状态栏 UI,比如音乐通知,时间、天气显示信息等等。不过这种需要自定义的情况一般是针对于 Android 定制化的地方会用到的多一些,但本文更多的是牵扯的是如何将这个下拉状态栏运行在独立进程中,以及进程之间的通信,这样的话即使下拉状态栏崩溃了,也不会影响主进程。
需求
按照如图示中,我们定义了几个需求如下:
- 主进程和下拉状态栏进程之间的通信
- 主进程中可以获取下拉状态栏进程中的状态栏的状态(是否可以下拉,是否完全展开)
- 在主进程中控制状态栏收起、展开、是否允许下拉控制
- 在主进程中监听下拉状态栏进程的状态栏收起和展开事件
建立 Binder 连接池
首先这里先回忆一下 AIDL 的使用流程:首先创建一个 Service 和一个 AIDL 接口,接着创建一个类继承自 AIDL 接口中的 Stub 类并实现 Stud 中的抽象方法,在 Service 的 onBind 方法中返回这个类的对象,然后客户端就可以绑定服务端的 Service,建立连接后就可以访问远程服务端的方法了
如果是按照这种使用方式的话,一个 AIDL 的具体的实现类通过一个 Service 在 onBinder 方法中返回给客户端调用,那么如果是存在很多 AIDL 文件的话,那就要创建很多 Service ,并在 onBInder 方法中返回 Binder 对象,显然这样是不合理的。所以我们这里需要引入 Binder 连接池,只维护一个 Service 就可以了,通过 Binder 连接池去查询具体的 Binder 对象,具体连接池的实践可以看这里 Binder 连接池
创建 AIDl 文件
创建 Binder 连接池
1 2 3 4
interface IBinderPool { // 连接池, 根据对应的 code 返回具体的 binder 对象 IBinder queryBinder( int binderCode); }
状态的监听回调
1 2 3 4
interface IStatusBarCallback { void opened(); void closed(); }
状态栏的管理
1 2 3 4 5 6 7
interface IStatusBarManager { void open(); void close(); void setEnableDropDown(boolean isDropDown); void addStatusBarListener(in IStatusBarCallback callback); void removeStatusBarListener(in IStatusBarCallback callback); }
状态栏状态获取
1 2 3 4 5 6
interface IStatusBarStatus { // 是否可以下拉 boolean isDropDown(); // 是否完全展开 boolean isWholeOpened(); }
创建状态栏进程服务
这里需要建立状态栏进程的服务,然后将 Binder 连接池的 IBinder 对象返回,这样通过连接池就可以获取到其他的 AIDL 具体实现了,然后在我们的服务中开启状态栏,看如下代码:
|
|
Binder 连接实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
public static class BinderPoolImpl extends IBinderPool.Stub { public BinderPoolImpl() { super(); } @Override public IBinder queryBinder(int binderCode) throws RemoteException { IBinder iBinder = null; // 根据code,返回具体的binder对象 switch (binderCode) { case BINDER_MANAGER: iBinder = new StatusBarManagerImpl(); break; case BINDER_STATUS: iBinder = new StatusBarStatusImpl(); break; default: break; } return iBinder; } }
接着看下 StatusBarManagerImpl 和 StatusBarStatusImpl 具体实现类,里面都做了什么
|
|
可以发现 StatusBarManagerImpl 这个类,主要做了对状态栏是否可以下拉的控制,和状态栏的收起和关闭,设置监听等操作,不过这里需要说明的是,继承自 stub 的类中,都是运行在 Binder 线程池中运行的,也就是非主线程中,要避免调用 UI 等相关操作。
在上述中,我们还调用了一个 StatusBarStatusManager 类,看一下这个类是如何实现的:
|
|
StatusBarStatusManager 这个类中,主要做了往 remoteCallbackList 中注册和解注册的监听,和设置监听回调,remoteCallbackList 是系统专门提供用于删除跨进程 listener 的接口,RemoteCallbackList 是一个泛型,支持管理任意的 Aidl 接口,RemoteCallbackList 的原理是内部有一个 Map
解除注册的源码如下:
|
|
|
|
StatusBarStatusImpl 类会比较简单,主要是做了一些状态栏的状态获取
然后再配置多进程
1 2 3 4 5 6
<application> <service android:exported="true" android:name=".StatusBarService" android:process=":statusBar" /> </application>
提供给外部调用的类,并获取相关的 AIDL 实现类给客户端调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
class StatusBarViewManager { internal var statusBarView: StatusBarView? = null var statusBarManager: IStatusBarManager? = null var statusBarStatus: IStatusBarStatus? = null @WorkerThread fun init(context: Context) { val binderPool = BinderPool.getInstance(context.applicationContext) // 通过 code 实例不同的 IBinder 对象 val queryBinder = binderPool.queryBinder(BinderPool.BINDER_MANAGER) // asInterface 返回给客户端调用的 AIDL 对象 statusBarManager = IStatusBarManager.Stub.asInterface(queryBinder) // asInterface 返回给客户端调用的 AIDL 对象 statusBarStatus = IStatusBarStatus.Stub.asInterface(binderPool.queryBinder(BinderPool.BINDER_STATUS)) } companion object { val instance: StatusBarViewManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { StatusBarViewManager() } } }
最后使用如下
|
|
最后相关的多进程通信逻辑就是这些了,感兴趣的话可以看下源码
源码地址:https://github.com/midFang/blogSource/tree/main/ProcessBinderWindow