Android中Binder浅析(进程间通信)

一、概述

因为进程间的资源是不能共享的,所以为了保证进程间能达到通信的目的,每个系统都存在自己的IPC(Inter-Process Communication)机制。Android系统中,涉及到进程间的通信底层都是依赖于Binder机制。

二、Binder

  • IPC 原理
    在这里插入图片描述

每个Android进程,只能运行在自己进程所运行的虚拟地址空间,这个虚拟地址空间包含用户空间和内核空间。对于用户空间,不同进程之间彼此不能共享,而内核之间是可以共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核空间来完成底层通信工作的。

  • Binder原理

Binder通信采用C/S架构,从组件层面来说,包含Client、Server、ServiceManager及Binder驱动,其中ServiceManager用于管理系统中的各种服务,架构图如下所示:
在这里插入图片描述

Binder通信的四个角色:

Client进程:使用服务的进程

Server进程:提供服务的进程

ServiceManager进程:ServiceManger的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。

Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数器管理,数据包在进程之间的传递和交互等一系列底层支持。

Binder运行机制:

注册服务:Server进程要先注册Service到ServiceManager,在该过程中Server是客户端,ServiceManager是服务端。

获取服务:Client进程使用某个Service之前,需先想ServiceManager中获取相应的Service。在该过程中Client是客户端,ServiceManager是服务端。

使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。在该过程中Client是客户端,Server是服务端。

图中的Client、Server、ServiceManager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都是通过Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Binder驱动和ServiceManager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需要自定义实现Client,Server端,借助Android平台基本架构便可以直接进行IPC通信。

既然Anrdoid进程间通信需要借助Android平台基本架构,那么Android就需要定制一些通信规则。具体就是:Android系统将一个可远程操作的应用定义为IBinder,在这个接口中定义了可远程调用对象应该具有的属性和方法,在代码中实际使用的Binder也都是继承自IBinder对象。下面我们分析一下IBinder中定义的主要的属性和方法。

public interface IBinder {
    
    
	 //查看binder对应的进程是否存活
    public boolean pingBinder();

    //查看binder是否存活 需要注意的是,可能返回true的过程中,binder已经不可用了
    public boolean isBinderAlive();
    /**
     * 执行一个对象的方法
     * 
     * @param code 需要执行的命令.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * 
     * @param data  传输的命令数据,一定不为空,如果不需要传任何数据,必须创建一个空的序列话对象    
 
     * @param reply 目标binder返回的处理结果,可能为null
     * 
     * @param flags 操作方式, 0 等待 RPC 返回结果 ,  1 单向的命令
     *
     * @return Returns the result from {@link Binder#onTransact}.  A successful call
     * generally returns true; false generally means the transaction code was not
     * understood.
     */
    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
        throws RemoteException;
}

总结的来说,就是IBinder抽象了远程调用的接口,任何一个可远程调用的对象都应该实现这个接口。由于IBinder对象是一个高度抽象的接口,直接使用这个接口对于应用层的开发者而言学习成本太高,因此Android实现了Binder作为IBinder的抽象类,提供了一些默认的本地实现,当开发者需要自定义实现的时候,只需要继承Binder并重写Binder中的protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException方法即可。

三、进程间通信AIDL的使用和解析

AIDL其实就是通过Binder来实现的,因为我们在定义好aidl文件后,studio就会帮我们生成相应的Binder类,所以我们可以通过查看studio生成的代码来了解Binder的工作原理。

首先在main目录下创建aidl文件夹,创建IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.huli.hulitestapplication;

interface IMyAidlInterface {
    
    
    /**
     * 这个方法是默认生成的,告诉我们能用的基本的数据类型有哪些
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String getName();

    int multiply(int left, int right);
}

然后Rebuild project后,新建一个Service,里面创建Binder类继承IMyAidlInterface.Stub,在Service的onBind方法中返回自己的Binder类

public class MyAIDLService extends Service {
    
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
    
    
        return new MyAIDLBinder();
    }

    class MyAIDLBinder extends IMyAidlInterface.Stub {
    
    

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
    

        }

        @Override
        public String getName() throws RemoteException {
    
    
            return "MyAIDLBinder";
        }

        @Override
        public int multiply(int left, int right) throws RemoteException {
    
    
            return left * right;
        }
    }
}

可以看到我们的MyAIDLBinder继承的是IMyAidlInterface.Stub,这个类就是我们新建完aidl文件,Rebuild project后系统帮我们生成好的Binder类

//studio帮我们生成了一个继承android.os.IInterface的IMyAidlInterface接口,
//所有在Binder中传输的接口都必须实现IInterface接口,
//IMyAidlInterface接口定义了我们在AIDL文件中定义的方法,
//然后还有个静态内部抽象类Stub
public interface IMyAidlInterface extends android.os.IInterface {
    
    
    /**
     * Local-side IPC implementation stub class.
     * Stub继承了android.os.Binder 并实现了IMyAidlInterface接口
     */
    public static abstract class Stub extends android.os.Binder implements com.huli.hulitestapplication.IMyAidlInterface {
    
    
        private static final java.lang.String DESCRIPTOR = "com.huli.hulitestapplication.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
    
    
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 该方法用于将服务端的Binder对象转换为客户端所需要的接口对象,
         * 该过程区分进程,如果进程相同,就返回服务端Stub对象本身,
         * 否则就返回封装后的Stub.Proxy对象
         */
        public static com.huli.hulitestapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
    
    
            if ((obj == null)) {
    
    
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.huli.hulitestapplication.IMyAidlInterface))) {
    
    
                return ((com.huli.hulitestapplication.IMyAidlInterface) iin);
            }
            return new com.huli.hulitestapplication.IMyAidlInterface.Stub.Proxy(obj);
        }

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

		 /**
         * 该方法是运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层封装后会交给此方法来处理,通过code来区分客户端请求的方法
         */
        @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_basicTypes: {
    
    
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getName: {
    
    
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_multiply: {
    
    
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.multiply(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

		/**
		* 该代理类主要用于返回给客户端一个服务代理,跟服务本身的方法一一对应,当客户端发起请求时,_data就会写入参数,然后调用transact发起RPC请求,当前线程会被挂起,然后服务端的onTransact方法就会被调用,当RPC过程返回后,当前线程就会继续执行,并从_reply取出返回值(如果有的话),并返回
		*/
        private static class Proxy implements com.huli.hulitestapplication.IMyAidlInterface {
    
    
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
    
    
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
    
    
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
    
    
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.lang.String getName() throws android.os.RemoteException {
    
    
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
    
    
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
    
    
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public int multiply(int left, int right) throws android.os.RemoteException {
    
    
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
    
    
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(left);
                    _data.writeInt(right);
                    mRemote.transact(Stub.TRANSACTION_multiply, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
    
    
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_multiply = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public java.lang.String getName() throws android.os.RemoteException;

    public com.huli.hulitestapplication.beans.Person getPerson() throws android.os.RemoteException;

    public int multiply(int left, int right) throws android.os.RemoteException;
}

分析完studio帮我们生成的Binder之后,我们大概知道AIDL原理,定义好AIDL文件只是方便studio帮我们生成所需要的Binder类,AIDL并不是必须的文件,因为这个Binder类我们可以手写出来。所以最终要的还是Binder的知识点,其他一些IPC方式都是通过Binder来实现的,比如Messager,Bundle,ContentProvider,只是他们封装的方式不一样而已。

上面服务定义好之后,需要在服务端清单文件中注册

<service
    android:name=".services.MyAIDLService"
    android:exported="true">
    <intent-filter>               
    	<action android:name="com.huli.hulitestapplication.services.MyAIDLService" />   
    </intent-filter>
</service>

客户端使用

Intent bindIntent = new Intent();
                    //Android 5.0出来后,其中有个特性就是Service Intent must be explitict。本地调用使用显示调用解决,但是aidl跨进程访问service时候确实需要使用隐式调用。
                    bindIntent.setAction("com.huli.hulitestapplication.services.MyAIDLService");
                    //解决方法是设置package
                    bindIntent.setPackage("com.huli.hulitestapplication");
                    bindService(bindIntent, new ServiceConnection() {
    
    
                        @Override
                        public void onServiceConnected(ComponentName name, IBinder service) {
    
    
                         //这里其实拿到的是生成的Proxy对象
                            mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
                             ToastUtil.toastAbove(this, mIMyAidlInterface.getName());
                        }

                        @Override
                        public void onServiceDisconnected(ComponentName name) {
    
    

                        }
                    }, BIND_AUTO_CREATE);

补充:上面aidl只用的基本数据类型,如果需要用自定义对象,需要以下步骤

1、 自定义对象(比如Person)需要实现Parcelable
2、 在aidl目录下创建跟Person.java所在包名一样的目录,创建Person.aidl放置在该目录下
3、 Person.aidl更改代码为

// Person.aidl  该类所在的包名需要跟Person.java所在的包名一致
package com.huli.hulitestapplication.beans;
parcelable Person;

4、 IMyAidlInterface就可以写方法Person getPerson();,但是需要导入包import com.huli.hulitestapplication.beans.Person;

四、系统服务使用解析

我们通常跨进程调用系统服务部分代码如下:

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);

注册服务:在Android开机启动的过程中,Android会初始化各种系统的Service,并将这些Service向ServiceManager注册,这些都是系统自动完成的。

获取服务:客户端首先向ServiceManager查询得到具体的Service引用,通常是Service的代理对象,然后进行一些操作。

使用服务:通过这个引用向具体的服务端发送请求,服务端执行完成后就返回,即上面的第6行的WindowManager的addView函数,将触发远程调用,调用的是运行在SystemServer进程中的WindowManager的addView函数。

具体流程为:

  1. client通过ServiceManager获得一个Server的代理接口,对Server进行调用, 代理接口中定义的方法与server中定义的方法是一一对应的
  2. client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成Parcel对象
  3. 代理接口将Parcel发送给内核中的binder driver
  4. server会读取binder driver中的请求数据,如果是发送给自己的,解析Parcel对象,处理并将结果返回
  5. 整个的调用过程是一个同步过程,在server处理的时候,client会被block住

五、总结

从应用层来说,Binder是客户端和服务端之间通信的媒介。从Framework层来说,Binder是ServiceManager链接各种Manager和ManagerSevice的桥梁。所以Binder在Android中作为独有的IPC方式,如果我们能够更好的理解它,对我们的开发工作会非常有益。

猜你喜欢

转载自blog.csdn.net/qq_33666539/article/details/84632761
今日推荐