Android多进程IPC机制——binder原理探索

原文地址:http://blog.csdn.net/verymrq/article/details/72885825

说到进程,刚开始学Android的时候无法理解进程和线程的区别,我的理解是:线程就是工作台,完成一件工艺品需要多道工序(可同时操作此作品,其中就涉及到线程安全),而进程是车间,当工厂越来越大,你需要的工作空间(内存)越来越多,就需要再建一个车间来作业了,进程就是如此。

我们新建一个进程很简单:

<activity
            android:name="com.example.MyProcessActivity"
            android:process=":text.process" >
        </activity>

只需要在activity中加入process属性即可。

但是一旦不在同一个进程中,就无法正常的通信及共享数据,这也就需要用到Android的IPC机制:进程间通信了。其实,就我目前了解,进程间通信有:contenProvider、Messenger、binder、Broadcast等等,归根结底其余几种原理都是binder,不仅如此,其实应用间通讯、及应用与系统通讯都用到了binder,为啥这个binder这么牛逼呢?
其实我在网上看了各路大神都对binder很重视,就在我看了包建强老师这几篇文章后理解更加深刻了:戳我看原文

说到binder当然离不开aidl文件咯。相信我们大多数童鞋接触binder都是这样一个场景:新建一个aidl文件xxInterface.aidl,rebuild之后系统会帮我们生成xxInterface.java,然后在service里新建这个类的内部类stub对象并重写aidl里的方法,在onbind里返回这个对象,再通过ServiceConnection得到,就能调用了。这个流程以及aidl是什么鬼,不懂的童鞋可以自行谷歌/百度关键字,我就不多逼逼了。

这里我想记录的是这个流程背着我们干了些啥,那我就从系统自动生成的文件搞起。这里我新建一个IUserInterface.aidl,只写了一个addUser方法,于是生成以下文件:

public interface IUserInterface extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.example.mybinderpool.IUserInterface {
        private static final java.lang.String DESCRIPTOR = "com.example.mybinderpool.IUserInterface";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.example.mybinderpool.IUserInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.mybinderpool.IUserInterface))) {
                return ((com.example.mybinderpool.IUserInterface) iin);
            }
            return new com.example.mybinderpool.IUserInterface.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_addUser: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.mybinderpool.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.mybinderpool.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.mybinderpool.IUserInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addUser(com.example.mybinderpool.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void addUser(com.example.mybinderpool.User user) throws android.os.RemoteException;
}

是的,我第一次看这玩意也想的是:什么乱七八糟的,不了了之。后来我看了安卓艺术探索对binder的介绍之后,慢慢理清其中的脉络。
首先,我们知道我们在service里要新建一个stub,然后通过Onbind返回

public static abstract class Stub extends android.os.Binder implements com.example.mybinderpool.IUserInterface{
//代码省略
}

嘛,继承了binder,难怪要把这个stub传来传去,传的就是binder对象而已。
传过去之后呢,是不是在connection里干了这么件事:

public void onServiceConnected(ComponentName name, IBinder service) {
            binder = XXInterface.Stub.asInterface(service);
        }

调用了asInterface方法嘛,我们看看这个方法干了些啥:

public static com.example.mybinderpool.IUserInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //先在所在进程看看有没有这个binder
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.mybinderpool.IUserInterface))) {
            //如果有,就直接返回
                return ((com.example.mybinderpool.IUserInterface) iin);
            }
            //没有就返回这个proxy
            return new com.example.mybinderpool.IUserInterface.Stub.Proxy(obj);
        }

所在进程如果有,直接返回了,就不涉及到别的进程了,这里我不管他,看这个proxy,看名字就熟悉,stub内部类嘛,翻译过来就是代理的意思,哦,下意识想到代理模式(不了解的自行搜索关键字),好吧,那我们再看看这个proxy。

private static class Proxy implements com.example.mybinderpool.IUserInterface {
            //remote翻译过来就是远程,这里代理了其他进程的binder
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addUser(com.example.mybinderpool.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
//关键处,调用了远程binder的transact方法。
mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

proxy重写了addUser方法,也就是说刚刚通过asInterface得到的对象调用的就是这个方法咯。在addUser方法里所做的也就是把数据_data及返回_reply这两个Parcel对象(进程间对象必须是parcelable的,应该都懂吧)通过远程对象的transact方法传了出去,通过查阅资料,阅读源码我们知道了transact方法会调用onTransact方法哦:

这里写图片描述

这个onTransact方法好熟悉,等等,我就要想起来了!不就是stub里面的方法嘛,不信?你倒回去看看。

对吧,没骗你,于是我们惊喜的发现:

这里写图片描述

调用了stub的addUser方法!!!这个stub可是你从其他进程里传过来的哦,至于addUser方法也是你自己重写的。

好的,binder简单实现已经梳理完成,我们可以发现,你如果想的话,自己写一个XXinterface就行,甚至可以不需要写aidl文件,当然既然可以简便当然最好咯,自己实现也是Copy。

在此申明一下,本文仅仅是研究binder在Android开发人员的接触范围,至于底层C++实现对于开发人员的我来说暂时接触不到,或许以后感兴趣了倒回去再看看,有兴趣的童鞋也可以自行搜索。

最后:由于Android四大组件都是通过binder实现通信,重要程度不言而喻,特此推荐了解一下,如果我理解透彻了会出续篇,有好文可以在留言区推荐哦,笔者在此感谢了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值