1. 程式人生 > >一起來聊聊Android基礎之Binder的使用

一起來聊聊Android基礎之Binder的使用

Binder實現了IBinder介面,通常我們不會直接去實現Binder,而是通過aidl工具來定義介面,自動生成相應的Binder的子類。

從Android Framework角度來說,Binder是ServiceManager連線各種Manager(ActivityManager,WindowManager,等等)和相應ManagerService的橋樑。

從Android應用層來說,Binder是客戶端和服務端進行通訊的媒介,通過bindService,服務端會返回一個Binder物件。同過這個Binder物件,客戶端就可以獲取服務端提供的服務或資料。

我們通過一個簡單的AIDL示例來學習Binder的使用。

AIDL示例:
目錄結構


Book.java:
package com.weijie.aidlapplication.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by weijie on 17-3-8.
 */

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

   

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}

Book.aidl:
package com.weijie.aidlapplication.aidl;


parcelable Book;

IBookManager.aidl
// IBookManager.aidl
package com.weijie.aidlapplication.aidl;

// Declare any non-default types here with import statements
import com.weijie.aidlapplication.aidl.Book;
interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

注意: 
1. Android Studio中,新建AIDL檔案:File->New->AIDL 
2. Book.aidl和IBookManager.aidl位於相同的包中,但是在IBookManager中仍然要匯入Book類。

IBookManager.java
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /home/weijie/AndroidStudioProjects/AidlApplication/app/src/main/aidl/com/weijie/aidlapplication/aidl/IBookManager.aidl
 */
package com.weijie.aidlapplication.aidl;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.weijie.aidlapplication.aidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.weijie.aidlapplication.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.weijie.aidlapplication.aidl.IBookManager interface,
 * generating a proxy if needed.
 */
public static com.weijie.aidlapplication.aidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.weijie.aidlapplication.aidl.IBookManager))) {
return ((com.weijie.aidlapplication.aidl.IBookManager)iin);
}
return new com.weijie.aidlapplication.aidl.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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.weijie.aidlapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.weijie.aidlapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.weijie.aidlapplication.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.weijie.aidlapplication.aidl.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 java.util.List<com.weijie.aidlapplication.aidl.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.weijie.aidlapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.weijie.aidlapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.weijie.aidlapplication.aidl.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();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.weijie.aidlapplication.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.weijie.aidlapplication.aidl.Book book) throws android.os.RemoteException;
}

IBookManager這個類是系統自動生成的,AS中點選”Make Project”即可生成。
IBookManager繼承了IInterface介面,同時本身也是一個介面。
所有可以在Binder中傳輸的介面都需要繼承IInterface介面。
IBookManager包括:

聲明瞭getBookList和addBook兩個方法
聲明瞭兩個整型的id分別用於標識這兩個方法
聲明瞭一個內部類Stub,這個Stub就是一個Binder類。當客戶端和服務端位於不同程序時,方法需要走transact過程,這個邏輯由Stub的內部代理類Proxy完成。
DESCRIPTOR
Binder的唯一標示,一般用當前Binder的類名錶示。

asInterface(android.os.IBinder obj)
用於將服務端的Binder物件轉換成客戶端所需的AIDL介面型別的物件。

這種轉換是區分程序的,如果客戶端和服務端位於同一程序,那麼返回的是服務端的Stub物件本身,否則返回的是系統封裝後的Stub.proxy物件。

我們來看這個示例:

public class LocalService extends Service {
    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final IBinder mBinder = new LocalBinder();
}


public void onServiceConnected(ComponentName name, IBinder service) {  
    // 1. 同一程序
    mIMyService = ((LocalBinder.LocalBinder)service).getService; 
    // 2. 同一程序或不同程序
    mIMyService = IMyService.Stub.asInterface(service);  


當客戶端和服務端在同一個程序時,可以直接將IBinder介面轉型為本地自定義Binder,然後通過自定義Binder的getService方法拿到IMyService介面例項。
asInterface的方法適用性更廣,如果客戶端和服務端位於同一程序,那麼返回的是服務端的Stub物件本身,否則返回的是系統封裝後的Stub.proxy物件。推薦使用這一種方法。
asBinder
返回當前的Binder物件

onTransact(int code, android.os.Parcel data, android.os.Parcel reply,int flags)
這個方法執行在服務端的Binder執行緒池中,當客戶端發起跨程序請求時,呼叫該方法。
服務端通過code判斷客戶端請求的方法,接著從data中取出目標方法所需的引數(if have),然後執行目標方法。
當目標方法執行完畢後,就向reply中寫入返回值(if have)。
如果此方法返回false,那麼客戶端的請求會失敗。
Proxy#getBookList
這個方法執行在客戶端,它的內部實現是這樣的:

Start
建立輸入型Parcel物件_data
建立輸出型Parcel物件_reply
建立返回值物件List
寫入_data
呼叫transact方法發起RPC(遠端過程呼叫),同時當前執行緒掛起
呼叫服務端的onTransact方法直到RPC過程返回後,當前執行緒繼續執行
從_reply中取資料
End
Proxy#addBook
這個方法的執行過程和getBookList是一樣的,addBook沒有返回值,所以它不需要從_reply中取出返回值。 


注意:

當客戶端發起遠端請求時,由於當前執行緒會被掛起直至服務端程序返回資料,所以如果一個遠端方法是很耗時的,那麼不能在UI執行緒中發起此遠端請求。
由於服務端的Binder方法執行在Binder的執行緒池中,所以Binder方法不管是否耗時都應該採用同步的方式去實現。


AIDL支援的資料型別:

基本資料型別(int,long,char,boolean,double)
String和CharSequence
List:只支援ArrayList,裡面每個元素都必須被AIDL支援
Map:只支援HashMap,裡面的每個元素都必須被AIDL支援,包括key和value
Parcelabel:所有實現了Parcelable介面的物件,必須顯示import進來
AIDL:必須顯示import進來
如果AIDL檔案中用到了自定義的Parcelable物件,那麼必須新建一個和它同名的AIDL檔案,並在其中宣告它為Parcelable型別,如上面的Book.aidl。 
AIDL中除了基本資料型別,其它型別的引數必須標上方向:in,out或者inout。 
AIDL介面中只支援方法,不支援宣告靜態常量,這一點區別於傳統的介面。

BookManagerService.java
public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    // CopyOnWriteArrayList支援併發讀/寫,自動進行執行緒同步
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);

        }
    };
    public BookManagerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
    }

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

}

雖然服務端返回的是CopyOnWriteArrayList,但是在Binder中會按照List的規範去訪問資料並最終形成一個新的ArrayList傳遞給客戶端。

AndroidManifest.xml
註冊BookManagerService

<service
    android:name=".aidl.BookManagerService"
    android:enabled="true"
    android:process=":remote"
    android:exported="true"></service>

客戶端實現
public class MainActivity extends AppCompatActivity {

    public static final String TAG = "weijie";

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            List<Book> list = bookManager.getBookList();
                Log.d(TAG, "onServiceConnected: query book list, list type: " + list.getClass().getCanonicalName());
                Log.d(TAG, "onServiceConnected: query book list: " + list.toString());
                Book newBook = new Book(3, "Android 開發藝術探索");
                Log.d(TAG, "onServiceConnected: add book: " + newBook);
                bookManager.addBook(newBook);
                List<Book> newList = bookManager.getBookList();
                Log.d(TAG, "onServiceConnected: query book list: " + newList.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);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        Intent intent = new Intent(this, BookManagerService.class);
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

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

輸出結果
01-02 09:47:15.101  5504  5504 D weijie  : onServiceConnected: query book list, list type: java.util.ArrayList
01-02 09:47:15.102  5504  5504 D weijie  : onServiceConnected: query book list: [Id = 1, Name = Android, Id = 2, Name = Ios]
01-02 09:47:15.102  5504  5504 D weijie  : onServiceConnected: add book: Id = 3, Name = Android 開發藝術探索
01-02 09:47:15.105  5504  5504 D weijie  : onServiceConnected: query book list: [Id = 1, Name = Android,
-----------------