Android 进程间通信 Binder IPC

Android 整体架构

image

从下往上依次为

  • 内核层:Linux 内核和各类硬件设备的驱动,这里需要注意的是,Binder IPC 驱动也是在这一层实现
  • 硬件抽象层:封装「内核层」硬件驱动,提供可供「系统服务层」调用的统一硬件接口
  • 系统服务层:提供核心服务,并且提供可供「应用程序框架层」调用的接口
  • Binder IPC 层:作为「系统服务层」与「应用程序框架层」的 IPC 桥梁,互相传递接口调用的数据,实现跨进层的通讯
  • 应用程序框架层:这一层可以理解为 Android SDK,提供四大组件,View 绘制体系等平时开发中用到的基础部件

内核层与硬件抽象层均用 C/C++ 实现,系统服务层是以 Java 实现,硬件抽象层编译为 so 文件,以 JNI 的形式供系统服务层使用。

系统服务层中的服务随系统的启动而启动,每一个服务均运行在一个独立进程中,所以本质上来说就是运行在一个独立进程的 Dalvik 虚拟机中。开发者的 APP 运行在一个新的独立进程空间,如何调用到系统服务层中的接口呢?IPC(Inter-Process Communication),进程间通讯。
每一个系统服务在应用层序框架层都有一个 Manager 与之对应,方便开发者调用其相关的功能。关系如下:


image

进程间通信

进程间通信的几种方式:管道、消息队列、共享内存、socket套接字、Binder等。

  • 其中管道和消息队列采用存储-转发方式,即数据线通发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。

  • 共享内存虽然无需拷贝,但控制复杂,难以使用。
    socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程通信和本机进程间的低速通信。

  • Binder通过内存映射的方式,使数据只需在内存进行一次读写过程。

内存映射,就是讲用户控件的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,相反内核空间对这段区域的修改也可以直接反映在用户空间。对于内核空间<->用户空间两者之间需要大量数据传输灯操作的话,效率是非常高的。

利用Binder 进行IPC 进程通信

image
  • Binder IPC 属于 C/S 架构,包括 Client、Driver、Server 三个部分

  • Client 可以手动调用 Driver 的 transact 接口,也可以通过 AIDL 生成的 Proxy 调用

  • Server 中会启动一个「线程池」来处理 Client 的调用请求,处理完成后将结果返回给 Driver,Driver 再返回给 Client

实现 Binder IPC 有两种方式:手动实现 和 利用AIDL实现。

1、 继承 IBinder,手动实现 IPC

Service

public class NoAidlService extends Service {

    public static final int TRANSATION_ADD = 0x001;
    public static final String DESCRIPTOR = "NoAidlService";
    private Binder mNoAidlBinder = new NoAidlBinder();
    public NoAidlService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return  mNoAidlBinder;
    }



    private class NoAidlBinder extends Binder{
        @Override
        protected boolean onTransact(int code, Parcel data,  Parcel reply, int flags) throws RemoteException {
            switch (code){
                case TRANSATION_ADD:
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();

                    int _arg1 = data.readInt();
                    int _result = _arg0+_arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);

                    return true;
            }

            return super.onTransact(code, data, reply, flags);
        }
    }
}

client :

   
    private ServiceConnection mNoAidlConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();

            try {
                int _result;
                _data.writeInterfaceToken("NoAidlService");
                _data.writeInt(100);
                _data.writeInt(200);
                service.transact(NoAidlService.TRANSATION_ADD,_data,_reply,0);
                _reply.readException();
                _result = _reply.readInt();
                Log.d(TAG,"_result:"+_result);

            }catch (Exception e){

            }finally {
                _reply.recycle();
                _data.recycle();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    
     /**
     * 调用 NoAIDL
     */
    public void testNoADILService(){
        Intent intent = new Intent(this,NoAidlService.class);
        bindService(intent,mNoAidlConnection, Context.BIND_AUTO_CREATE);
    }

manifest.xml

    

        

2、利用AIDL 实现IPC

AIDL 是不是过时 实现了对 ”手动实现IPC“ 的一种封装,简化了程序员的操作。

Android 进程间通信 Binder IPC_第1张图片
image
(1)无自定义类型的 IPC

在main目录下 与java同级建立aidl文件夹,在相同的包名下建立aidl文件

PlusAidlInterface.aidl

// PlusAidlInterface.aidl
package com.example.mybinder;

import com.example.mybinder.User;
// Declare any non-default types here with import statements

interface PlusAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

     int plus(int a,int b);

    String evalate(in User user);
}

建立aidl文件后,点击AS“同步”按钮,会在build/generated/source/aidl/ 目录下自动生成aidl对应的代码。

建立AIDLservice.java

public class AIDLService extends Service {

    public AIDLService(){

    }

    private final PlusAidlInterface.Stub mBinder = new PlusAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public int plus(int a, int b) throws RemoteException {
            return (a+b);
        }

    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }


}

manifest.xml

 

        

client 调用代码


    private PlusAidlInterface mPlus;
    private ServiceConnection mAIDLConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
                mPlus = PlusAidlInterface.Stub.asInterface(service);
                try {
                    int result = mPlus.plus(100,200);
                    Log.d(TAG,"_result 100+200 = "+result);

                    User user = new User();
                    user.name = "nancy";
                    user.age = "19";
                    String evalate = mPlus.evalate(user);
                    Log.d(TAG,"evalate:"+evalate);

                }catch (Exception e){

                }finally {

                }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    
    
    public void testAIDL(){
        Intent intent = new Intent(this,AIDLService.class);
        bindService(intent,mAIDLConnection, Context.BIND_AUTO_CREATE);
    }


(2)有自定义数据类型的 Binder IPC
  • 自定义数据类型必须实现Parcelable 接口,可序列化。

User.java

public class User implements Parcelable{


    public String name = "feifei";
    public String age = "18";
    public String value ="";

    public User(){

    }
    protected User(Parcel in) {
        name = in.readString();
        age = in.readString();
        value = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(age);
        dest.writeString(value);
    }

//    @Override
//    public void readFromParcel(Parcel dest) {
//        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
//        value = dest.readString();
//        age = dest.readString();
//        name = dest.readString();
//    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}
  • 每个自定义类型必须 创建一个同名的aidl文件与之对应。用来生命该自定义数据类型

User.aidl

// User.aidl
package com.example.mybinder;

parcelable User
  • 自定义数据类型 User.java 最好和aidl放到同一个目录下。同时修改build.gradle的sourceset 是AS 能够找到User.java
apply plugin: 'com.android.application'

android {
    compileSdkVersion 27


    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

    defaultConfig {
        applicationId "com.example.mybinder"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}
  • PlusAidlInterface.aidl 中补充 String evalate(in User user) 接口声明。
// PlusAidlInterface.aidl
package com.example.mybinder;

import com.example.mybinder.User;
// Declare any non-default types here with import statements

interface PlusAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

     int plus(int a,int b);

    String evalate(in User user);
}
  • AIDLService 中补充 public String evalate(User user) 接口的实现
public class AIDLService extends Service {

    public AIDLService(){

    }

    private final PlusAidlInterface.Stub mBinder = new PlusAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public int plus(int a, int b) throws RemoteException {
            return (a+b);
        }

        @Override
        public String evalate(User user){
            return  user.name+",age:"+user.age+":good";
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }


}
  • 调用 evalate()方法
    public void testAIDL(){
        Intent intent = new Intent(this,AIDLService.class);
        bindService(intent,mAIDLConnection, Context.BIND_AUTO_CREATE);
    }


    private PlusAidlInterface mPlus;
    private ServiceConnection mAIDLConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
                mPlus = PlusAidlInterface.Stub.asInterface(service);
                try {
                 
                    String evalate = mPlus.evalate(user);
                    Log.d(TAG,"evalate:"+evalate);

                }catch (Exception e){

                }finally {

                }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
注意AIDL中自定数据类型 有数据流向的概念

-in tag 表示数据只能由客户端流向服务端
服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动

  • out tag 表示数据只能由服务端流向客户端
    服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;

-inout tag 则表示数据可在服务端与客户端之间双向流通。

服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动.

    void setBookPrice(in Book book , int price)
    void setBookName(in Book book , String name)
    void addBookIn(in Book book);
    void addBookOut(out Book book);
    void addBookInout(inout Book book);

注意:
-java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。

-要想定义out 或 inout的数据类型,还必须实现readFromParcel 接口才行。

 public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        value = dest.readString();
        age = dest.readString();
        name = dest.readString();
    }

你可能感兴趣的:(Android 进程间通信 Binder IPC)