1. 程式人生 > >Android Binder機制AIDL例項介紹

Android Binder機制AIDL例項介紹

相關文章推薦:

前言

我們知道,Android的底層是使用Linux核心執行的,而Linux為了保證系統的穩定性,使用的是程序隔離的機制,也就是讓不同的程序執行在不同的虛擬空間,使得不同程序之間無法共享資料,防止資料的篡改。關於多程序帶下進行開發的介紹上面連結中已經講的很清楚了。但是,有時候我們也會遇到不同程序間需要通訊的情況,那麼,這時候就需要使用Binder機制來建立通訊。所以,Binder是Android系統程序間通訊(IPC)方式之一,用於跨程序間的方法呼叫。

但是,安卓中已經有非常多的通訊模型了,比如管道、報文佇列、插口等等。

那為什麼還要選擇Binder呢?

因為高效。Binder實現通訊的時候,只需要拷貝1次資料,而其他的比如socket需要拷貝2次,這樣也節省了手機記憶體。

一、什麼是Binder機制?

剛剛說到了Binder機制是用來實現多程序間通訊的,解決了Linux使用程序隔離機制所帶來的問題。那麼,我們一定很好奇,這個Binder機制到底是到底是什麼呢?從字面上理解binder是"粘結劑"的意思,那麼google的工程師為什會以"粘結劑"來命名binder呢?這是因為binder是基於一種Client-Server通訊方式,也就是我們所說的CS架構,在這個架構中存在著4個元件,分別是Client、Server、Service Manager和Binder驅動程式,binder這個"粘結劑"正是將這四個元件連線在一起。其中Client、Server執行在兩個不同的程序,他們進行跨程序呼叫需要binder驅動作基礎設施,Service Manager管理binder Server。

提到機制兩字,大家不覺得熟悉嗎? 對,沒錯,就是 “handler機制”,它用來實現多執行緒之間通訊的,這個我們並不陌生。由handle物件、message物件、messagequeen物件和looper物件,這四個物件組成,相互配合、不同分工,共同完成了一場多執行緒通訊的精彩把戲。   

說到這,應該會有人會明白我所表達的意思了。所以說,Binder機制其實和Handler機制一樣,其實也是一場由四個角色物件合作共同完成的一場通訊把戲,不同是,它比handle更牛逼一點,它是在多程序情況下完成的通訊。好了,下面用熱烈的掌聲歡迎Binder劇組的成員們出場吧。

  • server:提供服務的物件 ;
  • client:需要獲取服務的物件;
  • serviceManager:服務的管理者,本質上也是一個server;
  • Binder驅動:C/S 聯絡的中介

一,服務註冊。每個情報員都通過對接暗號確認身份。即通過Binder驅動將自身註冊到serviceManager中。

二,Binder驅動和Server通訊。Binder驅動為了和server進行通訊,它做了兩件事: 

  1. 為每一個server在核心空間裡建立一個binder節點,為每一個節點分配一個大於0的控制代碼。
  2. 把server的名字和控制代碼一起傳送到serviceManager中。即服務註冊。

三,Binder驅動和client通訊。client要想和server通訊,首先要知道server的名字。在知道了服務的名字以後,client會把這個名字和一個控制代碼為0的值封裝成一個數據包,傳送給Binder驅動;Binder驅動一看,這個控制代碼為0,它就會把這個資料包傳送給serviceManager。serviceManager在接收到這個資料包以後,就會根據名字來尋找它裡面有沒有client想要的server,如果有,那麼它就會把這個server的大於0的控制代碼傳送給Binder驅動。Binder驅動再把這個控制代碼傳送給client,這樣client就拿到了目標server的控制代碼。最後,client就用這個目標server的控制代碼外加server的名字,再封裝成一個數據包,傳送給Binder驅動,驅動再發送給serviceManager,這樣就可以找到對應的server實現通訊了。

小結:

  • 所有的server註冊到serviceManager中; 
  • Binder驅動為server建立節點,分配控制代碼; 
  • client想要什麼服務就通過Binder驅動拿到對應服務的控制代碼,用控制代碼去serviceManager中去尋找對應的服務,當然獲取控制代碼,獲取服務都是要通過Binder驅動的,client不能直接對server進行訪問,因為Linux核心有程序隔離機制啊,這樣應該很清楚了吧。

二、AIDL例項初步瞭解binder機制

假設一個場景:在圖書館,有兩個程序。A程序是圖書管理員,B程序向A程序新增圖書。當B程序收到A程序新增的圖書時,通知A程序,圖書已經新增成功。瞭解場景之後,我們通過aidl來實現這個需求。下文貼出binder服務端與客戶端的程式碼。


1、binder服務端
使用一個遠端Service模擬程序A,提供binder服務端。

package com.shine.binderdemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.concurrent.CopyOnWriteArrayList;

public class MyService extends Service {

    private CopyOnWriteArrayList<Book> booklist;
    private IOnBookAddListener mListener;

    private IBinder mBinder = new IBookManager.Stub() {

        @Override
        public void addBook(Book book) throws RemoteException {
            booklist.add(book);
            if (mListener != null) {
                mListener.onBookadd(book);
            }
        }

        @Override
        public void registerListener(IOnBookAddListener listener) throws RemoteException {
            mListener = listener;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        booklist = new CopyOnWriteArrayList();
    }


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

在AndroidManifest.xml中通過process屬性使MyService 執行在其他程序。
有一點我們需要格外注意:我們看到在用來存放新增書籍的booklist是一個CopyOnWriteArrayList。為什麼在這要用CopyOnWriteArrayList呢?這是因為binder服務端在接收到客戶端的請求訪問時都會在一個單獨的執行緒中處理這個請求,所以會出現執行緒安全問題,在這個地方通過CopyOnWriteArrayList來避免出現的執行緒安全問題,這個過程在子執行緒中完成也可以通過客戶端的程式碼來驗證。


2、binder客戶端
在activity中繫結服務來模擬程序B,通過binder跨程序呼叫A的新增圖書的方法,實現binder客戶端。

package com.shine.binderdemo;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private IBookManager mBookManager;

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

    public void add(View view) {
        Book book = new Book();
        book.setBookId(1);
        book.setBookName("《第一行程式碼》");
        try {
            mBookManager.addBook(book);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

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

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private IOnBookAddListener mListener = new IOnBookAddListener.Stub() {
        @Override
        public void onBookadd(final Book book) throws RemoteException {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, book.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }

    };
}

首先來驗證服務端中mBinder 的addBook(Book book)方法確實是在一個子執行緒中進行的。

private IOnBookAddListener mListener = new IOnBookAddListener.Stub() {
        @Override
        public void onBookadd(final Book book) throws RemoteException {
            //方法回撥在子執行緒中
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, book.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }

    };

在onBookadd(final Book book) 回撥中通過runOnUiThread(new Runnable())將執行緒切回主執行緒然後才能Toast提示。這也從側面證明了binder服務端處理客戶端的請求時會在一個子執行緒中處理。這裡我們還有一個地方要注意,雖然binder服務會在一個子執行緒中處理客戶端的請求,但是客戶端請求時卻不會新開一個執行緒,從上面的程式碼我們可能還看不出什麼,如果將服務端的新增圖書的程式碼設定為耗時操作,執行程式,點選新增圖書可能就會出現ANR。(這裡就不驗證了)所以在確定服務端處理請求時是耗時的操作的時候。有必要新開一個執行緒去請求。
上面說了這麼多,總結一句話:binder服務在處理客戶端的請求時是在一個獨立的執行緒中完成的,而客戶端請求處理,不會新開一個執行緒,如果是耗時操作,則可能出現ANR。

三、分析Binder如何來進行跨程序間的呼叫?

首先來看,在activity中點選新增圖書會呼叫add(View view),然後呼叫mBookManager.addBook(book);就能成功的往MyService的bookList中新增一本書,這是為什麼呢?我們的activity和service明明處於兩個程序,確好像在同一個程序中直接持有了服務端實現addBook(Book book)方法的mBinder 的引用,通過這個引用就呼叫了MyService 所在程序的方法。要解釋這個問題,我們自然而然的要分析activity中的mBookManager是怎麼被賦值的:

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //mBookManager 在這裡賦值
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

看到這裡mBookManager = IBookManager.Stub.asInterface(service);我們可能會糊塗,這個IBookManager.Stub到底是個什麼東西,為什麼呼叫它的asInterface(service)方法就能得到mBinder 的引用?接下來我們就來分析IBookManager.Stub是個啥,它的asInterface(service)到底有什麼奧祕。

package com.shine.binderdemo;

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.shine.binderdemo.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.shine.binderdemo.IBookManager";

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

        /**
         * Cast an IBinder object into an com.shine.binderdemo.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.shine.binderdemo.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.shine.binderdemo.IBookManager))) {
                return ((com.shine.binderdemo.IBookManager) iin);
            }
            return new com.shine.binderdemo.IBookManager.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_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.shine.binderdemo.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.IOnBookAddListener _arg0;
                    _arg0 = com.shine.binderdemo.IOnBookAddListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.shine.binderdemo.IBookManager {
            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 addBook(com.shine.binderdemo.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void registerListener(com.shine.binderdemo.IOnBookAddListener listener) 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.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addBook(com.shine.binderdemo.Book book) throws android.os.RemoteException;

    public void registerListener(com.shine.binderdemo.IOnBookAddListener listener) throws android.os.RemoteException;
}

上面直接貼出了IBookManager.aidl編譯之後生成的IBookManager.java的程式碼。這個程式碼初看之下很長,不容易讀懂,下面我貼一張as裡面關於這個類的結構圖。

通過這個結構圖大概可以看出Stub是IBookManager的一個內部類,有一個asInterface(android.os.IBinder obj)方法,這也是上面activity中給mBookManager 賦值中用到的一個方法,我們待會再來分析。Stub內部又有一個內部類Proxy ,從名字上來看它應該是一個代理類,那麼它到底代理的那個類呢?

public static com.shine.binderdemo.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.shine.binderdemo.IBookManager))) {
                return ((com.shine.binderdemo.IBookManager) iin);
            }
            //將IBinder型別的obj通過構造方法傳入Proxy
            return new com.shine.binderdemo.IBookManager.Stub.Proxy(obj);
 }

Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

看完上面兩段程式碼應該可以看出Proxy代理的就是一個IBinder型別的obj,到這裡我們明白了mBookManager原來就是一個Proxy代理類。熟悉代理模式的話,我們知道代理類只是持有一個真實類的引用,真正功能都是由這個真實類實現的。在這個IBookManager.Stub.Proxy裡面,真實類是什麼呢?

private static class Proxy implements com.shine.binderdemo.IBookManager {
            //mRemote是這個代理中的真實物件
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

           ......

            @Override
            public void addBook(com.shine.binderdemo.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //mBookManager的addBook(Book book)方法實際上是呼叫 mRemote.transact(...)方法
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
           ......
}

這段程式碼的註釋已經很明確的IBinder型別的mRemote是這個類的真實引用。
mBookManager.addBook(Book book)方法最後會呼叫mRemote.transact(...)方法,那麼這個mRemote是個啥呢?mRemote是如何傳入Proxy呢?
我們在之前的binder機制的概念時說過,binder機制涉及到4個元件,binder server ,binder client,binder核心驅動,Service Manager。在上面的情景中我們只分析了binder server 和binder client,接下來binder核心驅動(對於binder驅動,在下面只會提到它的作用,不涉及具體的程式碼,具體的程式碼分析我也不懂)就要出場了,而對於Service Manager,這個例子中卻不會直接涉及,在activity的啟動過程中會出現,到時候再分析,敬請期待。。。
有了binder驅動介入,就可以解決mRemote到底是個啥了。先看下MyService的onBinder(...)方法

@Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
private IBinder mBinder = new IBookManager.Stub() {

        @Override
        public void addBook(Book book) throws RemoteException {
            booklist.add(book);
            if (mListener != null) {
                mListener.onBookadd(book);
            }
        }

        @Override
        public void registerListener(IOnBookAddListener listener) throws RemoteException {
            mListener = listener;
        }
    };

這個mBinder是一個IBookManager.Stub()型別的變數,而IBookManager.Stub()繼承Binder,所以mBinder是一個Binder型別的物件。這個binder型別的物件實際上就是binder服務端,在binder服務端開啟的時候,同時會在binder核心建一個mRemote的binder物件,我們在上面提到的mRemote其實就是binder核心裡面的mRemote binder物件。實際在binder程序間呼叫的時候必須要考慮的問題就是如何獲取binder核心mRemote 物件。這個例子採用的是Service做binder服務端,而binderService中google的工程師已經為我們實現好了。在ServiceConnection裡面有個回撥方法可以獲取binder核心的mRemote,如下:

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通過獲取到的service(mRemote)生成IBookManager.Stub.Proxy物件
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

這樣我們就獲取到了binder核心的mRemote物件,同時也傳入了IBookManager.Stub.Proxy,接著就可以使用mRemote來進行跨執行緒的呼叫了。
接下來看下mBookManager到底是怎樣實現addBook(Book,book)方法的,上面已經分析了會呼叫到IBookManager.Stub.Proxy.addBook(Book book)。

@Override
            public void addBook(com.shine.binderdemo.Book book) throws android.os.RemoteException {
                 //_data表示傳送binder服務端的資料,這些資料需要通過Parcel (包裹)進行傳遞
                android.os.Parcel _data = android.os.Parcel.obtain();
               //_reply 表示binder服務端相應的資料,這些資料同樣需要通過Parcel (包裹)進行傳遞
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                   //用來做安全檢查,binder服務端會處理這個資料
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        //Parcel (包裹)不僅可以傳遞基本型別的資料還可以傳遞物件,但是物件必須實現Parcelable介面
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                     //呼叫binder核心的mRemote物件往binder服務端傳送資訊
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

程式碼中註釋已經很詳細了,就不解釋了,接著看下面,我們說過mRemote實際上是一個binder型別的物件, mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);就會呼叫到Binder的transact(...)方法中

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);
        }
        //呼叫binder服務端的onTransact(...)中
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

其中transact(...)有四個引數,分別是 code,data,reply,flag。
code:整形的一個識別碼,客戶端傳入,用於區分服務端執行哪個方法。
data:客戶端傳入的Parcel型別的引數。
reply:服務端返回的結果值,也是Parcel型別資料。
flag:整形的一個標記,用於標記是否是否有返回值,0表示有返回值,1表示沒有。
接著再次涉及到binder核心驅動,具體的細節我也不太懂,直接的結論是流程會進入到binder服務端的IBookManager.Stub的onTransact(...)中:

@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;
                }
                //根據transact第一個引數來確定請求的是哪個方法
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.Book _arg0;
                    if ((0 != data.readInt())) {//如果客戶端傳遞是有非基本型別的資料從data中取出
                        _arg0 = com.shine.binderdemo.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //取出來的資料傳到MyService的mBinder的addBook(...)
                    this.addBook(_arg0);
                    //由於addBook(...)沒有返回值,所以不需要通過reply返回結果
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.IOnBookAddListener _arg0;
                    _arg0 = com.shine.binderdemo.IOnBookAddListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

通過上面從activity的addBool(Book book)開始,我們一步一步的分析了binder客戶端如何呼叫binder服務端的方法,這個過程在binder核心驅動的基礎上實現,讓我們感覺好像是呼叫本地(同一個程序)方法一樣,實質上底層為我們做了大量的工作。這樣基於aidl實現的Binder跨程序呼叫就大概談完了,對binder跨程序呼叫也應該有了一定的瞭解。
#######binder小結
跨程序呼叫的關鍵點在於如何獲得服務端的binder物件在核心裡面的引用(如上面分析的mRemote)。
一般來說有兩種途徑,其一是通過Service Manager,這篇文章沒有直接涉及Service Manager,但是在底層原始碼裡面這種情況很常見,在Activity啟動過程中用到的ActivityManagerService就是通過這種方式在客戶端得到服務端的binder物件在核心裡面的引用,我們以後再分析。其二是通過已經建立好的binder連線來獲取這個引用。如上面的例子中用到的一樣。

private IOnBookAddListener mListener = new IOnBookAddListener.Stub() {
        @Override
        public void onBookadd(final Book book) throws RemoteException {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, book.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }

    };

這是一個實現了介面方法的Binder服務,通過已經建立好的binde連線mBookManager傳遞給Myservice所在程序。

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                //通過已經建立好的連線傳送服務端binder的引用
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

有一點需要注意,在此時activity所在的程序就成為了我們通常所說的binder服務端,而MyService則是binder客戶端。這就是第二種獲取binder服務引用的方式。再多談一點,其實在這個例子中,兩個建立的binder連線都是通過已將建立好的連線來傳遞的,除了mListener 這個binder引用的獲得,mBinder也是這種情況。這裡就不再詳細討論了,如果感興趣可以學習一下bindService的原始碼,就肯定能發現這一點。最後貼上一張例子用到的uml類圖:

四、總結

這篇文章是學習技術以來的第一篇文章,很久以前就在謀劃了,但是由於一些事情和水平的關係拖了很久,但是總算是寫完了。在寫作的過程中還是遇到了很多困難的,有時候知道的東西你不一定能寫出來,需要的知識也不在一個層面。有些知識可能以為懂了,但是真正到了寫的時候,還是發現不會。我覺得寫技術部落格是提升技術的一個很好的途徑,希望以後我能保持。對binder有了概念之後,接下來會繼續把Activity啟動過程及介面繪製流程解析寫完。先前的打算是在這篇文章裡面再介紹一下handler相關知識的,畢竟在activity啟動的過程中handler也同樣重要,但是寫binder的時候,發現知識還是有很多缺陷,只能留待下一次了。



——————
參考連結:https://www.jianshu.com/p/c98c6ca47c4d