歡迎訪問我的個人博客,原文鏈接:http://wensibo.top/2017/07/03/Binder/ ,未經(jīng)允許不得轉(zhuǎn)載!

前言

大家好,好久不見,距離上篇文章已經(jīng)有35天之久了,因為身體不舒服害了一場病,不過現(xiàn)在已經(jīng)好多了;另外這個月是考試月,當(dāng)然得花多點時間復(fù)習(xí)功課了;再者這段時間依舊在看書,同時也在研究Android源碼,準(zhǔn)備了不少干貨想與大家一起分享。7月剛到,該放假的也都差不多放假了,該實習(xí)的也已經(jīng)在實習(xí)了,而我。。。還是準(zhǔn)備秋招吧!多看書多打碼多提升自己的眼界。

今天想要和大家一起分享的是Android中的Binder機制,講真這絕對是Android中很深奧的一個點,如果能夠徹底弄懂它,這對初級程序員來說絕對會是一件具有里程碑意義的事件,當(dāng)然我也研究了許久,終于琢磨出點所以然,所以就拿出來和大家一起分享分享。另外這篇文章將會通過一個小實例來講解Binder,大家可以點擊這里,也歡迎大家fork和star。話不多說讓我們開始吧!

IPC

為了弄懂IPC的來龍去脈,我將從以下三個方面為大家來講解,希望對大家理解IPC會有幫助

什么是IPC

IPC是Inter Process Communication的縮寫,其意思就是進(jìn)程間的通信,也就是兩個進(jìn)程之間的通信過程。我們都知道在Android系統(tǒng)中,每個應(yīng)用都運行在一個進(jìn)程上,具有自己的DVM實例,而且進(jìn)程之間是相互隔離的,也就是說各個進(jìn)程之間的數(shù)據(jù)是互相獨立,互不影響的,而如果一個進(jìn)程崩潰了,也不會影響到另一個進(jìn)程。
采取這樣的設(shè)計是有一定道理的,例如這樣的前提下將互相不影響的系統(tǒng)功能分拆到不同的進(jìn)程里面去,有助于提升系統(tǒng)的穩(wěn)定性,畢竟我們都不想自己的應(yīng)用進(jìn)程崩潰會導(dǎo)致整個手機系統(tǒng)的崩潰。
進(jìn)程之間隔離是不錯的選擇,可是如果進(jìn)程之間想要互相通信,進(jìn)行數(shù)據(jù)交互的時候那該怎么辦呢?例如我們在自己的應(yīng)用中想要訪問手機通訊錄中的聯(lián)系人,很顯然這是兩個不同的進(jìn)程,如果Android沒有提供一種進(jìn)程之間交流的機制,那么這種功能將無法實現(xiàn)。
不過由于Android系統(tǒng)使用的是Linux內(nèi)核,而在Linux系統(tǒng)中進(jìn)程之間的交互是有一套機制的,所以Android也借鑒了其中的一些機制,從而形成了Android的IPC機制。
上面只是粗略的講解了IPC是啥,關(guān)于它的使用和原理我將一一為大家呈上。

為什么要用IPC

上一點中我舉了訪問手機通訊錄的例子。但你可能覺得我不需要用到這種功能,那么我就不用管IPC啦!其實不然,IPC在我們的應(yīng)用開發(fā)過程中隨處可見,下面我將舉一個例子來說明他的重要性。
我們在MainActivity中修改一個靜態(tài)變量,接著在另一個進(jìn)程的SecondActivity中去訪問該變量,看看能否讀取已經(jīng)修改過的變量。

1、新建一個Student類,并聲明一個靜態(tài)變量

public class Student {    public static String name="BOB";
}

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

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);
    Student.name = "JACK";
    Log.d("MainActivity:Sname=", Student.name);
}

3、將SecondActivity設(shè)置為新進(jìn)程,并在其onCreate方法中訪問name

<!-- 在清單文件中通過android:process屬性為SecondActivity指定特定的進(jìn)程:com.bob.aidltest:second --><activity     android:name=".SecondActivity"    android:process=":second"></activity>
public class SecondActivity extends AppCompatActivity {    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.second_activity);
        Log.d("SecondActivity:Sname=" , Student.name);
    }
}

4、通過log,可以看到在MainActivity中修改了name的值,但是在SecondActivity中卻無法讀取修改后的值

通過以上的實驗,大家應(yīng)該明白了一點:在不同的進(jìn)程之間訪問同一個靜態(tài)變量是行不通的。其原因是:每一個進(jìn)程都分配有一個獨立的虛擬機,不同的虛擬機在內(nèi)存分配上有不同的地址空間,這就導(dǎo)致在不同的虛擬機上訪問同一個對象會產(chǎn)生多個副本。例如我們在MainActivity中訪問的name的值只會影響當(dāng)前進(jìn)程,而對其他進(jìn)程不會造成影響,所以在SecondActivity中訪問name時依舊只能訪問自己進(jìn)程中的副本。

Android中解決IPC的方法

上面也講到,為了解決這些跨進(jìn)程的問題,Android沿用了一些Linux的進(jìn)程管理機制,使得進(jìn)程之間能夠進(jìn)行交互,下面我將列出一些常見的IPC方式,需要指出的是本文主要講解Binder機制,所以會注重講解AIDL,其他方式請讀者自行查閱相關(guān)資料。

名稱特點使用場景
Bundle只能傳輸實現(xiàn)了Serializable或者Parcelable接口或者一些Android支持的特殊對象適合用于四大組件之間的進(jìn)程交互
文件不能做到進(jìn)程間的即時通信,并且不適合用于高并發(fā)的場景適合用于SharedPreference以及IO操作
ContentProvider可以訪問較多的數(shù)據(jù),支持一對多的高并發(fā)訪問,因為ContentProvider已經(jīng)自動做好了關(guān)于并發(fā)的機制適合用于一對多的數(shù)據(jù)共享并且需要對數(shù)據(jù)進(jìn)行頻繁的CRUD操作
Socket通過網(wǎng)絡(luò)傳輸字節(jié)流,支持一對多的實時通信,但是實現(xiàn)起來比較復(fù)雜適合用于網(wǎng)絡(luò)數(shù)據(jù)共享
Messenger底層原理是AIDL,只是對其做了封裝,但是不能很好的處理高并發(fā)的場景,并且傳輸?shù)臄?shù)據(jù)只能支持Bundle類型低并發(fā)的一對多的即時通信
AIDL功能強大,使用Binder機制(接下來會講解),支持一對多的高并發(fā)實時通信,但是需要處理好線程同步一對多并且有遠(yuǎn)程進(jìn)程通信的場景

Binder

終于來到這篇文章的重頭戲了,上面講到Android解決IPC的方法中有一種是AIDL,它使用的原理就是Binder,只有理解了Binder,我們才算是理解了Android跨進(jìn)程通信的原理。在這里我會帶大家看看Android中有哪一些重要的地方使用到了Binder,接著我們會通過一個實例來了解如何使用Binder,最后我們會分析Binder的源碼來理解他的工作流程。

Binder在Android中的運用

說起B(yǎng)inder在Android的使用場景,可以說是無處不在,我列出一些最常見的場景:

  • 四大組件的生命周期都是使用Binder機制進(jìn)行管理的

  • View的工作原理也使用了Binder

  • WindowManager的工作機制同樣使用了Binder

以上三個方面只是最常見的場景,但是卻幾乎包括了我們開發(fā)的整個流程。我們開發(fā)的應(yīng)用都離不開四大組件,而四大組件也正是依靠Binder機制運行的;對于我們最常見的View,他是如何顯示的,View又是如何響應(yīng)我們的動作的,這其中也用到了Binder(關(guān)于這些內(nèi)容我會在后續(xù)的文章中為大家分析)??梢哉f了解Binder對于我們的開發(fā)是很有幫助的,那接下來我們就來看看我們該如何使用Binder進(jìn)行進(jìn)程間的通信吧!

如何使用Binder

現(xiàn)在我們需要實現(xiàn)這樣的功能:客戶端與服務(wù)端位于不同的進(jìn)程,客戶端需要向服務(wù)端添加學(xué)生,同時客戶端還可以向服務(wù)端發(fā)起查詢學(xué)生列表的請求。

1、創(chuàng)建Student.java,Student.aidl,IStudentManager.aidl

  • Student.java

package com.bob.aidltest.aidl;import android.os.Parcel;import android.os.Parcelable;/** * Created by bob on 17-7-3. * 所有需要在Binder傳遞的數(shù)據(jù)類型都需要實現(xiàn)Parcelable接口 */public class Student implements Parcelable{    public static String name="BOB";    public int s_id;    public String s_name;    public String s_gender;    public Student(Parcel in) {
        s_id = in.readInt();
        s_name = in.readString();
        s_gender = in.readString();

    }    public Student(int s_id, String s_name, String s_gender) {        this.s_id = s_id;        this.s_name = s_name;        this.s_gender = s_gender;
    }    @Override
    public int describeContents() {        return 0;
    }    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(s_id);
        dest.writeString(s_name);
        dest.writeString(s_gender);
    }    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];
        }
    };    @Override
    public String toString() {        return String.format("[StudentID: %s , StudentName: %s , StudentGender: %s]", s_id, s_name, s_gender);
    }
}
  • Student.aidl

// Student1.aidlpackage com.bob.aidltest.aidl;parcelable Student;
  • IStudentManager.aidl

// IStudentManager.aidlpackage com.bob.aidltest.aidl;import com.bob.aidltest.aidl.Student;interface IStudentManager {    List<Student> getStudentList();    void addStudent(in Student student);
}

創(chuàng)建完畢之后手動編譯項目(Build-->ReBuild Project),接著就會在app/build/generated/source/aidl/debug/com/bob/aidltest/aidl/IStudentManager.java中看到自動生成的IStudentManager接口,如下圖:

2、分析IStudentManager.java

先來看看自動生成的代碼:

public interface IStudentManager extends android.os.IInterface{    /** 內(nèi)部類Stub,繼承自Binder并且實現(xiàn)了IStudentManager接口,因此他也是一個Binder對象,這個內(nèi)部類是需要在服務(wù)端手動實現(xiàn)的,并且會通過onBind方法返回給客戶端 */
    public static abstract class Stub extends android.os.Binder implements com.bob.aidltest.aidl.IStudentManager
    {        private static final java.lang.String DESCRIPTOR = "com.bob.aidltest.aidl.IStudentManager";        /** 構(gòu)造方法 */
        public Stub()
        {            this.attachInterface(this, DESCRIPTOR);
        }        /**         * 將服務(wù)端的Binder對象轉(zhuǎn)換為客戶端的所需的AIDL接口類型的對象,客戶端拿到這個對象就可以通過這個對象遠(yuǎn)程訪問服務(wù)端的方法         */
        public static com.bob.aidltest.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.bob.aidltest.aidl.IStudentManager))) {                return ((com.bob.aidltest.aidl.IStudentManager)iin);
            }            return new com.bob.aidltest.aidl.IStudentManager.Stub.Proxy(obj);
        }        @Override public android.os.IBinder asBinder()
        {            return this;
        }        
        /**         * 運行在服務(wù)端進(jìn)程的Binder線程池中;當(dāng)客戶端進(jìn)程發(fā)起遠(yuǎn)程請求時,遠(yuǎn)程請求會要求系統(tǒng)底層執(zhí)行回調(diào)該方法         * @param code 客戶端進(jìn)程請求方法標(biāo)識符。服務(wù)端進(jìn)程會根據(jù)該標(biāo)識確定所請求的目標(biāo)方法         * @param data 目標(biāo)方法的參數(shù),他是客戶端進(jìn)程傳進(jìn)來的,當(dāng)我們調(diào)用addStudent(Student student)方法時,參數(shù)就是Student對象         * @param reply 目標(biāo)方法執(zhí)行后的結(jié)果,將會返回給客戶端,例如當(dāng)我們調(diào)用getStudentList,返回的就是一個Student的列表         */
        @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_getStudentList:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.bob.aidltest.aidl.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);                    return true;
                }                case TRANSACTION_addStudent:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.bob.aidltest.aidl.Student _arg0;                    if ((0!=data.readInt())) {
                        _arg0 = com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);
                    }                    else {
                        _arg0 = null;
                    }                    this.addStudent(_arg0);
                    reply.writeNoException();                    return true;
                }
            }            return super.onTransact(code, data, reply, flags);
        }        /**         * 代理的內(nèi)部類,他實現(xiàn)了IStudentManager接口,這個代理類就是服務(wù)端返回給客戶端的AIDL接口對象,客戶端可以通過這個代理類訪問服務(wù)端的方法         */
        private static class Proxy implements com.bob.aidltest.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 java.util.List<com.bob.aidltest.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.bob.aidltest.aidl.Student> _result;                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);
                }                finally {
http://www.cnblogs.com/ghylzwsb/p/Binder.html