安卓的IPC機制之系列之Binder

Binder介紹

Binder是安卓中的一種誇進程通信的方式,在安卓源碼中Binder繼承了IBinder類。

public class Binder implements IBinder {

Binder在安卓的frameWork層和應用的體現不同,

  • FrameWork角度
    Binder是ServiceManager與安卓中各種Manager和相應的ManagerService連接的橋樑。
  • 應用層
    Binder是客戶端和服務端進行通信的媒介,服務包括普通服務和基於AIDL的服務,
    在四大組件Service使用中,當我們調用

    bindService(intent, conn, BIND_AUTO_CREATE);

    在ServiceConnnetion的實現中會返回一個Binder對象,通過這個Binder對象可以調用服務端的方法和數據

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
    

Binder工作機制之AIDL

在平常的安卓開發中,Binder主要用在Service中,包括AIDL和Messenger,普通的Service是不涉及進程間通信的。Messenger底層實現是AIDL,因此主要看下安卓中AIDL的實現具體瞭解Binder的工作機制。

  • 創建AIDL

    新建一個Student文件,和Student.aidl及IStudentManager.aidl文件。

    Student類必須繼承Parcelable

    package com.example.lijingya.aidldemo.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    
    public class Student implements Parcelable {
    
    private String name;
    private int age;
    
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    protected Student(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }
    
    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }
    
        @Override
        public Student[] newArray(int size) {
            return new Student[ size ];
        }
    };
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public int describeContents() {
        return 0;
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
        }
    }
    

    Student.aidl文件

    package com.example.lijingya.aidldemo.aidl;
    
    // Declare any non-default types here with import statements
    
    parcelable Student ;

    IStudentManager.aidl

    package com.example.lijingya.aidldemo.aidl;
    import com.example.lijingya.aidldemo.aidl.Student;
    // Declare any non-default types here with import statements
    
    interface IStudentManager {
    
    void addStudent(in Student stu);
    
    List<Student>getStudentList();
    }

    在IStudentManager中添加了一個添加學生和獲取所有學生的方法

    編譯之後會在 app\build\generated\source\aidl目錄下生成IStudentManager.java文件。該類的內容如下:

    public interface IStudentManager extends android.os.IInterface {
    
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.lijingya.aidldemo.aidl.IStudentManager {
    
        private static final java.lang.String DESCRIPTOR = "com.example.lijingya.aidldemo.aidl.IStudentManager";
    
        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        /**
         * Cast an IBinder object into an com.example.lijingya.aidldemo.aidl.IStudentManager interface,
         * generating a proxy if needed.
         */
        public static com.example.lijingya.aidldemo.aidl.IStudentManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.lijingya.aidldemo.aidl.IStudentManager))) {
                return ((com.example.lijingya.aidldemo.aidl.IStudentManager) iin);
            }
            return new com.example.lijingya.aidldemo.aidl.IStudentManager.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_addStudent: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.lijingya.aidldemo.aidl.Student _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.lijingya.aidldemo.aidl.Student.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addStudent(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getStudentList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.lijingya.aidldemo.aidl.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        private static class Proxy implements com.example.lijingya.aidldemo.aidl.IStudentManager {
    
            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 addStudent(com.example.lijingya.aidldemo.aidl.Student stu) 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 ((stu != null)) {
                        _data.writeInt(1);
                        stu.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
    
            @Override
            public java.util.List<com.example.lijingya.aidldemo.aidl.Student> getStudentList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.lijingya.aidldemo.aidl.Student> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.lijingya.aidldemo.aidl.Student.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    
        static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    
    public void addStudent(com.example.lijingya.aidldemo.aidl.Student stu) throws android.os.RemoteException;
    
    public java.util.List<com.example.lijingya.aidldemo.aidl.Student> getStudentList() throws android.os.RemoteException;
    }
    

    IStudentManager繼承IInterface,IInterface是一個接口 有一個返回IBinder的方法

    public IBinder asBinder();

    有一個抽象內部類Stub繼承Binder並實現了IStudentManager,Stub就是一個Binder類,

    DESCRIPTOR

    Binder類的唯一標識

    asInterface

    將服務端的Binder類轉化成AIDL接口,若客戶端和服務端在一個進程中,返回服務端的接口對象,否則返回Stub.Proxy對象

    onTransact

    運行在服務端的Binder線程池中,當進行誇進程請求時,服務端會通過code判斷調用的那個方法,會從data中取出目標方法的參數。然後執行目標方法

      case TRANSACTION_addStudent: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.lijingya.aidldemo.aidl.Student _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.lijingya.aidldemo.aidl.Student.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addStudent(_arg0);
                    reply.writeNoException();
                    return true;
                }

    當目標方法沒有參數時,執行目標方法,然後將返回值寫入reply中

     case TRANSACTION_getStudentList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.lijingya.aidldemo.aidl.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
    Proxy的addStudent
       @Override
            public void addStudent(com.example.lijingya.aidldemo.aidl.Student stu) 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 ((stu != null)) {
                        _data.writeInt(1);
                        stu.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }  

    該方法運行在客戶端,當客戶端調用該方法時,首先創建所需的輸入型Parcel對象_data和輸出型對象_reply,接着調用遠程的transact方法發起RPC(遠程過程調用)請求,同時此線程掛起,服務端接着調用onTransact方法,直到此過程結束

       public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

    ###### Proxy的getStudentList

      @Override
            public java.util.List<com.example.lijingya.aidldemo.aidl.Student> getStudentList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.lijingya.aidldemo.aidl.Student> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.lijingya.aidldemo.aidl.Student.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

    getStudentList此方法與上面的方法過程基本一致,當線程掛起,直到RPC結束,從_reply中取出返回值。

跨進程AIDL的使用

新建Service

創建一個遠程的Binder並在onBinder中返回,由於該方法是執行在BInder的線程池中,多個客戶端訪問的時候會存在併發的情況,CopyOnWriteArrayList是支持併發的讀/寫。因而使用它處理線程同步的問題。並在Service的oncreate中創建兩個新的學生對象爲了實現跨進程的通信方式在清單文件中設置Service的process屬性將該Service設置在單獨的一個進程中。

public class StudentService extends Service {

    CopyOnWriteArrayList<Student> list = new CopyOnWriteArrayList<>();

    private Binder binder = new IStudentManager.Stub() {
        @Override
        public void addStudent(Student stu) throws RemoteException {
            list.add(stu);
        }

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return list;
        }
    };

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

    @Override
    public void onCreate() {
        super.onCreate();
        list.add(new Student("jack", 12));
        list.add(new Student("rose", 10));

    }
}

清單文件

  <service
            android:name=".StudentService"
            android:process=":remote" />
Activity中的使用

在Activity中綁定該服務,並在ServiceConnection中將Binder對象轉化成AIDL的接口對象,並調用服務端的方法。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IStudentManager iStudentManager = IStudentManager.Stub.asInterface(service);
            try {
                List<Student> studentList = iStudentManager.getStudentList();
                Student student = new Student("tom", 14);
                studentList.add(student);
                Log.d(TAG, studentList.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, StudentService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}

Binder總結

在跨進程通信中,客戶端發起遠程請求,在Binder的Stub.Proxy的包裝對象中,將參數寫入_data中,並且當前線程掛起,然後會調用遠程transact方法,接着會調用服務端的onTransact方法,這個過程是在Binder的線程池中執行的。然後等RPC過程執行完成,將結果寫入reply中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章