Binder线程栈复用(binder线程池)
前言
Binder驱动有很多小的细节,目的就是提升Binder通信的效率。比较典型的是两个机制,因为没有官方名词,我对这两种机制起个名字:"线程栈复用"和"远程转本地"。前者是为了减少线程消耗,后者是为了减少跨进程次数。这篇文章就是介绍"线程栈复用",以后我们再讲"远程转本地"。
一、假设一个场景
进程A在UI线程发起一次Binder通信到进程B的服务B,在服务B中再次发起Binder通信到进程A的服务A,请问整个过程会牵涉到几个线程,按照常理理解应该有三个线程:
1.进程A UI线程 2.进程B Binde线程 3.进程A Binder线程 第一次Binder通信:进程A UI线程——>进程B Binde线程 第二次Binder通信:进程B Binder线程——>进程A Binder线程。
二、写个Demo
那事实上真的是会用到三个线程吗?我们写的Demo验证一下2.1 进程A定义一个AIDL
interface IServiceA { void sendMsg(String msg); }复制代码
关键代码
public class MainActivity extends AppCompatActivity { private ServiceA mServiceA = new ServiceA(); public class ServiceA extends IServiceA.Stub { @Override public void sendMsg(String msg) throws RemoteException { Log.v("KobeWang", "get msg : " + msg, new Exception("KobeWang")); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获得Service B的服务 Intent intent = new Intent(this, ServerB.class); this.bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IServiceB serviceB = IServiceB.Stub.asInterface(service); try { //给ServiceB发送msg,并将ServiceA发给ServiceB Log.v("KobeWang", "send msg start: " + "hello ServiceB"); serviceB.sendMsg(mServiceA, "hello ServiceB"); Log.v("KobeWang", "send msg end: " + "hello ServiceB"); } catch (Exception e) { } } @Override public void onServiceDisconnected(ComponentName name) { } }, Context.BIND_AUTO_CREATE); } }复制代码
2.2 进程B定义一个AIDL
interface IServiceB { void sendMsg(IBinder binder, String msg); }复制代码
关键代码
public class ServerB extends Service { @Override public IBinder onBind(Intent intent) { return new ServiceB(); } public class ServiceB extends IServiceB.Stub { @Override public void sendMsg(IBinder binder, String msg) throws RemoteException { Log.v("KobeWang", "get msg : " + msg); if (binder != null) { IServiceA serviceA = IServiceA.Stub.asInterface(binder); Log.v("KobeWang", "send msg start: " + "hello ServiceA"); serviceA.sendMsg("hello ServiceB"); Log.v("KobeWang", "send msg end: " + "hello ServiceA"); } } } }复制代码
由于demo写一个module中,别忘了将ServceB运行在另外一个进程,否则就会触发另一个机制:"远程转本地"
<service android:name=".ServerB" android:exported="true" android:process=":server"> </service>复制代码
三、运行结果
//运行在进程A的UI线程,开始ServiceB的Binder通信,休眠等返回 02-29 14:33:26.559 27948 27948 V KobeWang: send msg start: hello ServiceB //运行在进程B的Binder线程 02-29 14:33:26.560 28006 28029 V KobeWang: get msg : hello ServiceB //运行在进程B的Binder线程 02-29 14:33:26.561 28006 28029 V KobeWang: send msg start: hello ServiceA //运行在进程A的UI线程 02-29 14:33:26.565 27948 27948 V KobeWang: get msg : hello ServiceA //运行在进程B的Binder线程 02-29 14:33:26.566 28006 28029 V KobeWang: send msg end: hello ServiceA //运行在进程A的UI线程,ServiceB的Binder通信结束 02-29 14:33:26.566 27948 27948 V KobeWang: send msg end: hello ServiceB复制代码
看到结果,我们简化一下
//运行在进程A的UI线程,开始ServiceB的Binder通信,休眠等返回 02-29 14:33:26.559 27948 27948 V KobeWang: send msg start: hello ServiceB //运行在进程A的UI线程,响应ServiceA 02-29 14:33:26.565 27948 27948 V KobeWang: get msg : hello ServiceA //运行在进程A的UI线程,ServiceB的Binder通信结束 02-29 14:33:26.566 27948 27948 V KobeWang: send msg end: hello ServiceB复制代码
我们可以发现,明明进程A的UI线程正在等待ServiceB的返回结果,处于休眠的状态,竟然有空闲去响应进程B发起的ServiceA的Binder调用。
我们把ServiceeA的get msg时候堆栈打出来看看。
KobeWang: java.lang.Exception: KobeWang //这个线程莫名的被Binder驱动唤醒去响应ServiceA的Binder请求 V KobeWang: at com.kobe.binderlock.MainActivity$ServiceA.sendMsg(MainActivity.java:21) V KobeWang: at com.kobe.binderlock.IServiceA$Stub.onTransact(IServiceA.java:61) V KobeWang: at android.os.Binder.execTransactInternal(Binder.java:1035) V KobeWang: at android.os.Binder.execTransact(Binder.java:1008) //这下面是serviceB.sendMsg(mServiceA, "hello ServiceB");调用之后,休眠在Binder驱动 V KobeWang: at android.os.BinderProxy.transactNative(Native Method) V KobeWang: at android.os.BinderProxy.transact(BinderProxy.java:510) V KobeWang: at com.kobe.binderlock.IServiceB$Stub$Proxy.sendMsg(IServiceB.java:96) V KobeWang: at com.kobe.binderlock.MainActivity$1.onServiceConnected(MainActivity.java:38) V KobeWang: at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1948) V KobeWang: at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1980) V KobeWang: at android.os.Handler.handleCallback(Handler.java:883) V KobeWang: at android.os.Handler.dispatchMessage(Handler.java:100) V KobeWang: at android.os.Looper.loop(Looper.java:214) V KobeWang: at android.app.ActivityThread.main(ActivityThread.java:7501) V KobeWang: at java.lang.reflect.Method.invoke(Native Method) V KobeWang: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) V KobeWang: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)复制代码
看完堆栈就应该明白这一切都是Binder驱动搞的鬼,Binder驱动发现反正进程A的UI线程为了等ServiceB的结果休眠中,既然ServiceB又要请求进程A的ServiceA,与其采用进程A的Binder线程响应,还不如直接用进程A休眠的UI线程响应,这样子进程A的线程使用就从2个减少为1个
总结
这个机制除了这种两个进程互相Binder调用的情况,就算是3个进程,4个进程,5个进程,甚至n个进程产生嵌套的Binder调用,也一样可以发挥作用,发挥作用的规则如下图描述:
当进程D发起Binder调用到进程B的时候,进程D会向后遍历整个Binder调用关系。检查是否已经有进程B参与,如果已经进程B参与了,直接唤醒进程B中参与本次Binder嵌套调用中休眠的线程,响应进程D对进程B的Binder调用
作者:传道士
链接:https://juejin.cn/post/7047030343534116895