美文网首页
[031]Binder线程栈复用

[031]Binder线程栈复用

作者: 王小二的技术栈 | 来源:发表于2020-02-29 15:29 被阅读0次

    前言

    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调用

    彩蛋

    其实一般来说对于普通工程师,了解清楚这个规则就够了,以后开发过程中注意一下这种Binder的嵌套调用即可。Binder驱动是如何实现线程栈复用?我清楚背后实现的原理,我还没有准备好如何通俗易懂地讲出来,需要提前准备的知识太多,有兴趣的朋友可以看《Android系统源代码情景分析》。

    相关文章

      网友评论

          本文标题:[031]Binder线程栈复用

          本文链接:https://www.haomeiwen.com/subject/esephhtx.html