美文网首页Android开发Android开发经验谈Android知识
手写Binder实现android中的进程间通信

手写Binder实现android中的进程间通信

作者: GeekStrom | 来源:发表于2018-11-07 15:09 被阅读110次

    在Android系统中,每个应用都运行在一个进程上,具有自己的DVM实例,而且进程之间是相互隔离的,也就是说各个进程之间的数据是互相独立,互不影响的,而如果一个进程崩溃了,也不会影响到另一个进程。
    为什么采取这样的设计呢,比如这样的前提下将互相不影响的系统功能分拆到不同的进程里面去,有助于提升系统的稳定性,毕竟我们都不想自己的应用进程崩溃会导致整个手机系统的崩溃。
    进程之间隔离是不错的选择,可是如果进程之间想要互相通信,进行数据交互的时候那该怎么办呢?例如我们在自己的应用中想要访问手机通讯录中的联系人,很显然这是两个不同的进程,如果Android没有提供一种进程之间交流的机制,那么这种功能将无法实现,不过由于Android系统使用的是Linux内核,而在Linux系统中进程之间的交互是有一套机制的,所以Android也借鉴了其中的一些机制,从而形成了Android的IPC机制,其意思就是进程间的通信,也就是两个进程之间的通信过程。
    下面我们通过小例子看看;

    1、新建一个Person类,并声明一个静态变量:

     public class Person {
         public static String name="张三";
     }
    

    2、在MainActivity的onCreate方法中修改name的值,并打印log

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        Log.d("MainActivity", "原名:" + Person.name);
        person.name = "李四";
        Log.d("MainActivity", "修改后:" + Person.name);
    }
    

    3、将TestActivity设置为新进程,并在其onCreate方法中访问name

    <activity 
    android:name=".TestActivity"
    android:process=":second">
    </activity> 
    
     
    
    public class TestActivity extends AppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
            Log.d("TestActivity" , Person.name);
        }
    }  
    

    运行看看结果:


    1112.png

    TestActivity中获取到了name值并未修改。

    1113.png

    通过以上的例子,大家应该明白了一点:在不同的进程之间访问同一个静态变量是行不通的。其原因是:每一个进程都分配有一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机上访问同一个对象会产生多个副本。例如我们在MainActivity中访问的name的值只会影响当前进程,而对其他进程不会造成影响,所以在TestActivity中访问name时依旧只能访问自己进程中的副本。

    Android解决IPC的方法中有一种是AIDL,它使用的原理就是Binder,只有理解了Binder,我们才算是理解了Android跨进程通信的原理。在这里我会带大家看看Android中有哪一些重要的地方使用到了Binder,接着我们会通过一个实例来了解如何使用Binder。
    Binder在Android中的运用

    说起Binder在Android的使用场景,可以说是无处不在,我列出一些最常见的场景:

    四大组件的生命周期都是使用Binder机制进行管理的,比如AMS,PMS,PMS,
    View的工作原理也使用了Binder
    WindowManager的工作机制同样使用了Binder

    接下来我们通过传统的AIDL实现进程间通讯。
    1、创建Person.java,Person.aidl,IPersonManager.aidl

    public class Person implements Parcelable {
        public String name = "张三";
    
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        public Person(Parcel in) {
            this.name = in.readString();
        }
    
        public static final Creator<Person> CREATOR = new Creator<Person>() {
            @Override
            public Person createFromParcel(Parcel in) {
                return new Person(in);
            }
    
            @Override
            public Person[] newArray(int size) {
                return new Person[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(this.name);
        }
    }
    

    创建完毕之后手动编译项目(Build-->make Project),接着就会在

    app/build/generated/source/aidl/debug/com/binder/IPersonManager.java中看到自动生成的IStudentManager接口
    
    1114.png

    2、分析IPersonManager.java

            public interface IPersonManager extends android.os.IInterface {
        /**
         * 内部类Stub,继承自Binder并且实现了IStudentManager接口,因此他也是一个Binder对象,这个内部类是需要在服务端手动实现的,并且会通过onBind方法返回给客户端
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.binder.IPersonManager {
    
            /**
             * 唯一的binder标示 可以看到就是IPersonManager的全路径名
             */
            private static final java.lang.String DESCRIPTOR = "com.binder.IPersonManager";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * 将服务端的Binder对象转换为客户端的所需的AIDL接口类型的对象,客户端拿到这个对象就可以通过这个对象远程访问服务端的方法
             * Cast an IBinder object into an com.binder.IPersonManager interface,
             * generating a proxy if needed.
             */
            public static com.binder.IPersonManager asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.binder.IPersonManager))) {
                    return ((com.binder.IPersonManager) iin);
                }
                return new com.binder.IPersonManager.Stub.Proxy(obj);
            }
    
            @Override
            public android.os.IBinder asBinder() {
                return this;
            }
    
    
    
            /**
             * 运行在服务端进程的Binder线程池中;当客户端进程发起远程请求时,远程请求会要求系统底层执行回调该方法
             * @param code 客户端进程请求方法标识符。服务端进程会根据该标识确定所请求的目标方法
             * @param data 目标方法的参数,他是客户端进程传进来的,当我们调用addPerson(Person person)方法时,参数就是Person对象
             * @param reply 目标方法执行后的结果,将会返回给客户端,例如当我们调用getPersonList,返回的就是一个Person的列表
             */
            @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_getPersonList: {
                        data.enforceInterface(DESCRIPTOR);
                        java.util.List<com.binder.Person> _result = this.getPersonList();
                        reply.writeNoException();
                        reply.writeTypedList(_result);
                        return true;
                    }
                    case TRANSACTION_addPerson: {
                        data.enforceInterface(DESCRIPTOR);
                        com.binder.Person _arg0;
                        if ((0 != data.readInt())) {
                            _arg0 = com.binder.Person.CREATOR.createFromParcel(data);
                        } else {
                            _arg0 = null;
                        }
                        this.addPerson(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    
            /**
             * 代理的内部类,他实现了IStudentManager接口,这个代理类就是服务端返回给客户端的AIDL接口对象,客户端可以通过这个代理类访问服务端的方法
             */
            private static class Proxy implements com.binder.IPersonManager {
                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;
                }
    
    
                /**
                 *这里我们一共有2个方法 一个getPersonList 一个addPerson 我们就分析一个方法就可以了
                 *并且要知道 这2个方法运行在客户端!!!!!!!!!!!!!!!!
                 *首先就是创建了3个对象_data 输入对象,_reply输出对象,_result返回值对象
                 *然后把参数信息 写入到_data里,接着就调用了transact这个方法 来发送rpc请求,然后接着
                 *当前线程挂起, 服务端的onTransace方法才被调用,调用结束以后 当前线程继续执行,直到
                 *从_reply中取出rpc的返回结果 然后返回_reply的数据
    
                 *所以这里我们就要注意了,客户端发起调用远程请求时,当前客户端的线程就会被挂起了,
                 *所以如果一个远程方法 很耗时,我们客户端就一定不能在ui main线程里在发起这个rpc请求,不然就anr了。
                 * @return
                 * @throws android.os.RemoteException
                 */
                @Override
                public java.util.List<com.binder.Person> getPersonList() throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    java.util.List<com.binder.Person> _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.createTypedArrayList(com.binder.Person.CREATOR);
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
    
                @Override
                public void addPerson(com.binder.Person person) 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 ((person != null)) {
                            _data.writeInt(1);
                            person.writeToParcel(_data, 0);
                        } else {
                            _data.writeInt(0);
                        }
                        mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
            }
    
            static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        }
    
        public java.util.List<com.binder.Person> getPersonList() throws android.os.RemoteException;
    
        public void addPerson(com.binder.Person person) throws android.os.RemoteException;
    }
    

    对于进程间通信来说,具体的流程就分为如下几步:

    1.Client 发起远程调用请求 也就是RPC 到Binder。同时将自己挂起,挂起的原因是要等待RPC调用结束以后返回的结果

    2.Binder 收到RPC请求以后 把参数收集一下,调用transact方法,把RPC请求转发给service端。

    3.service端 收到rpc请求以后 就去线程池里 找一个空闲的线程去走service端的 onTransact方法 ,实际上也就是真正在运行service端的 方法了,等方法运行结束 就把结果 写回到binder中。

    4.Binder 收到返回数据以后 就唤醒原来的Client 线程,返回结果。至此,一次进程间通信 的过程就结束了

    创建RemoteService,模拟服务端;

    public class RemoteService extends Service {
    
    
    //适合用于进程间传输的列表类
    private CopyOnWriteArrayList<Person> mPersonsList = new CopyOnWriteArrayList<>();
    
    
    public final IPersonManager.Stub mBinder = new IPersonManager.Stub() {
    
        @Override
        public List<Person> getPersonList() throws RemoteException {
            return mPersonsList;
        }
    
        @Override
        public void addPerson(Person person) {
            mPersonsList.add(person);
        }
    
    };
    
    
    
    @Override
    public void onCreate() {
        super.onCreate();
        mPersonsList.add(new Person("小明"));
        mPersonsList.add(new Person("小李"));
        mPersonsList.add(new Person("小华"));
        Log.d(RemoteService.class.getSimpleName(), "RemoteService 启动了");
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    

    }

    在客户端绑定Service!

    public class TestActivity extends AppCompatActivity {
    
    public static String TAG = TestActivity.class.getSimpleName();
    
    private IPersonManager mPersonManager;
    
    private ServiceConnection mConnection = new ServiceConnection() {
        //onServiceConnected与onServiceDisconnected都是在主线程中的,所以如果里面如果涉及到服务端的耗时操作那么需要在子线程中进行
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取到IPersonManager对象
    
            mPersonManager = com.binder.IPersonManager.Stub.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mPersonManager = null;
    
        }
    };
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
    
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }
    
    
    /**
     * 在客户端向服务端添加Person
     *
     * @param view
     */
    public void addPerson(View view) {
        if (mPersonManager != null) {
            try {
                Person mPerson1 = new Person("王五");
                mPersonManager.addPerson(mPerson1);
                Log.d(TAG, "添加一位学生:" + mPerson1.name);
                Person mPerson2 = new Person("赵六");
                mPersonManager.addPerson(mPerson2);
                Log.d(TAG, "添加一位学生:" + mPerson2.name);
                Person mPerson3 = new Person("麻子");
                mPersonManager.addPerson(mPerson3);
                Log.d(TAG, "添加一位学生:" + mPerson3.name);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 在客户端向服务端发起查询学生的请求
     *
     * @param view
     */
    public void getPerson(View view) {
        //由于服务端的查询操作是耗时操作,所以客户端需要开启子线程进行工作
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (mPersonManager != null) {
                    try {
                        final List<Person> students = mPersonManager.getPersonList();
    
                        for (int i = 0; i < students.size(); i++) {
                            Log.d(TAG, "从服务器获取到学生:" + students.get(i).name);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    

    }

    添加数据:

    1116.png

    查询服务端数据

    1117.png

    通过上面的案例大概明白,分别是Activity充当客户端,服务端的RemoteService,充当服务管理者的IPersonManager以及充当访问介质的Binder驱动。他们的职责如下:

    RemoteService: 服务提供者,这里面会有许多我们常用的服务,在本 例中提供的服务就是添加学生以及获取学生列表。而在系统中则包括有ActivityService 、 WindowMananger等服务,这些系统服务提供的功能,对四大组件以及Window的工作提供的保障。
    Activity: 服务调用者,一般就是我们的应用,在这里我们通过调用RemoteService的服务来完成工作。
    IPersonManager: 他是负责管理服务的,在其内部通过Map集合来存储Service与Binder的映射关系,这样客户端在向其请求服务的时候就能够返回特定的Binder。
    Binder驱动: 他是IPersonManager连接各种Service的桥梁,同时也是客户端与服务端交流的桥梁。

    连串起来就是客户端(Activity)首先向IPersonManager发送请求RemoteService的服务,IPersonManager查看已经注册在里面的服务的列表,找到相应的服务后,通过Binder驱动将其中的Binder对象返回给客户端,从而完成对服务的请求。

    接下来,我们就可以来尝试着手写一下Binder:
    写一个接口IPersonManager

     public interface IPersonManager extends IInterface {
    
          List<Person> getPersonList() throws RemoteException;
    
         void addPerson(Person person) throws RemoteException;
    
     }
    

    写一个继承自Binder的抽象类PersonManagerImpl并实现IPersonManager接口,

    public abstract class PersonManagerImpl extends Binder implements IPersonManager {
    
    
    //唯一标识用于注册该BInder,用包名+接口名定义
    private static final String DESCRIPTOR = "com.binder.aidl.IPersonManager";
    //getList方法唯一标识
    static final int TRANSACTION_getList = (IBinder.FIRST_CALL_TRANSACTION + 0);
    //add方法唯一标识
    static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 1);
    
    public PersonManagerImpl() {
        //注册该binder
        this.attachInterface(this, DESCRIPTOR);
    }
    
    
    public static IPersonManager asInterface(IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        //查询当前进程
        if (((iin != null) && (iin instanceof PersonManagerImpl))) {
            return (PersonManagerImpl) iin;//当前进程返回IBookManager
        }
        return new Proxy(obj);//非当前进程返回Proxy
    }
    
    
    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
                                 int flags) throws RemoteException {
    
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getList: {
                data.enforceInterface(DESCRIPTOR);
                List<Person> _result = this.getPersonList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_add: {
                data.enforceInterface(DESCRIPTOR);
                Person person;
                if ((0 != data.readInt())) {
                    person = Person.CREATOR.createFromParcel(data);
                } else {
                    person = null;
                }
                this.addPerson(person);
                reply.writeNoException();
                return true;
            }
        }
    
    
        return super.onTransact(code, data, reply, flags);
    }
    
    @Override
    public IBinder asBinder() {
        return this;
    }
    
    
    public static class Proxy implements IPersonManager {
    
        private IBinder mRemote;
    
        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }
    
        public Proxy(IBinder obj) {
            this.mRemote = obj;
        }
    
        @Override
        public List<Person> getPersonList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List<Person> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_getList, data, reply, 0);
                reply.readException();
                result = reply.createTypedArrayList(Person.CREATOR);
            } finally {
                reply.recycle();
                data.recycle();
            }
            return result;
        }
    
        @Override
        public void addPerson(Person person) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if ((person != null)) {
                    data.writeInt(1);
                    person.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_add, data, reply, 0);
                reply.readException();
            } finally {
                reply.recycle();
                data.recycle();
            }
        }
    
        @Override
        public IBinder asBinder() {
            return null;
        }
    }
    

    }

    我们的手写binder 就完成了,然后看看 service 以及客户端 怎么调用。

    public class RemoteService extends Service {
    
        public final PersonManagerImpl mBinder = new PersonManagerImpl() {
    
        @Override
        public List<Person> getPersonList() throws RemoteException {
            return mPersonsList;
        }
    
        @Override
        public void addPerson(Person person) {
            mPersonsList.add(person);
        }
    
    };
    
    
    //适合用于进程间传输的列表类
    private CopyOnWriteArrayList<Person> mPersonsList = new CopyOnWriteArrayList<>();
    
    
    @Override
    public void onCreate() {
        super.onCreate();
        mPersonsList.add(new Person("小明"));
        mPersonsList.add(new Person("小李"));
        mPersonsList.add(new Person("小华"));
        Log.d(RemoteService.class.getSimpleName(), "RemoteService 启动了");
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    

    }

    启动 Service:

     private ServiceConnection mConnection = new ServiceConnection() {
        //onServiceConnected与onServiceDisconnected都是在主线程中的,所以如果里面如果涉及到服务端的耗时操作那么需要在子线程中进行
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取到IPersonManager对象
            mPersonManager = PersonManagerImpl.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mPersonManager = null;
    
        }
    }; 
    

    到这 就基本写完了

    相关文章

      网友评论

        本文标题:手写Binder实现android中的进程间通信

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