Binder是Android的一個類,它實現了IBinder接口。從IPC角度來講,Binder是Android中的一種跨進程通訊方式;從Android應用層來講,Binder是客戶端和服務端進行通訊的媒介,當bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,經過這個Binder對象,客戶端就能夠獲取服務端提供的服務或者數據,這裏的服務包括普通服務和基於AIDL的服務。java
在Android開發中,Binder主要用在Service中,包括AIDL(Android Interface Defination Language)和Messenger,其中普通Service中的Binder不涉及進程間通訊,因此較爲簡單,沒法觸及Binder的核心,而Messenger的底層實際上是AIDL,因此這裏選擇AIDL來分析Binder的工做機制。android
新建三個文件,Book.java、Book.aidl和IBookManager.aidl,代碼以下: Book.javabash
package com.example.runningh.myapplication.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
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];
}
};
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
}
複製代碼
Book .java是一個表示圖書信息的類,實現了Parcelable接口。服務器
Book.aidl:app
package com.example.runningh.myapplication.aidl;
parcelable Book;
複製代碼
Book.aidl是Book類在AIDL中的聲明。ide
IBookManager.aidl:ui
package com.example.runningh.myapplication.aidl;
import com.example.runningh.myapplication.aidl.Book;
import com.example.runningh.myapplication.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
複製代碼
IBookManager.adil是咱們定義的一個接口,裏面有兩個方法:getBookList和addBook,其中getBookList用於從遠程服務端獲取圖書列表,而addBook用於往圖書列表中添加一本書。this
注意:在IBookManager.aidl中要手動導入Book類,不支持自動導入,這是AIDL的特殊之處。spa
經過執行build操做後,系統爲IBookManager.aidl生成的Binder類,在gen目錄下的source文件夾下可找到IBookManager.java類。線程
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/RunningH/Documents/TencentGame/ThirdParty/ThemeSkinning/MyApplication2/app/src/main/aidl/com/example/runningh/myapplication/aidl/IBookManager.aidl
*/
package com.example.runningh.myapplication.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.example.runningh.myapplication.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.runningh.myapplication.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.runningh.myapplication.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.runningh.myapplication.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.example.runningh.myapplication.aidl.IBookManager))) {
return ((com.example.runningh.myapplication.aidl.IBookManager)iin);
}
return new com.example.runningh.myapplication.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.example.runningh.myapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.example.runningh.myapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.runningh.myapplication.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.example.runningh.myapplication.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.example.runningh.myapplication.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.example.runningh.myapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.runningh.myapplication.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.example.runningh.myapplication.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.runningh.myapplication.aidl.Book book) throws android.os.RemoteException;
}
複製代碼
首先這個類聲明瞭兩個方法getBookList和addBook,顯然這就是咱們在IBookMananger.aidl中所聲明的方法,同時它還聲明瞭兩個整型的id分別用於標識這兩個方法,這兩個id用於標識在transact過程當中客戶端所請求的究竟是哪一個方法。接着,它聲明瞭一個內部類Stub,這個Stub就是一個Binder類,當客戶端和服務端都位於同一個進程時,方法調用不會跨進程的transact過程,而當二者位於不一樣進程時,方法調用須要走transact過程,這個邏輯由Stub的內部代理Proxy來完成。
咱們看一下怎麼使用這個類,經過調用過程來熟悉其中的方法。
咱們新建一個工程並在新建BookManagerActivity、BookManagerService並將BookManagerService放在另一個進程中(在manifest中聲明)。在BookManagerActivity經過BindService的方式和BookManagerService綁定。
package com.example.runningh.myapplication.aidl;
import android.app.Activity;
import android.app.ListActivity;
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.annotation.Nullable;
import android.util.Log;
import com.example.runningh.myapplication.R;
import java.util.List;
/**
* Created by RunningH on 2017/11/26.
*/
public class BookManagerActivity extends Activity {
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager iBookManager = IBookManager.Stub.asInterface(service); //注意這裏調用了Stub的asInterface方法,經過服務端的IBinder對象找到
try {
List<Book> list = iBookManager.getBookList();
Log.i("ABC", "query book list, list type:" + list.getClass().getCanonicalName());
Log.i("ABC", "query book list:" + list.toString());
Book newBook = new Book(3, "HTML");
iBookManager.addBook(newBook);
Log.i("ABC", "add book:" + newBook);
List<Book> newList = iBookManager.getBookList();
Log.i("ABC", "query book list:" + newList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
複製代碼
BookManagerService.java:
package com.example.runningh.myapplication.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by RunningH on 2017/11/26.
*/
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
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);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "IOS"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
複製代碼
public static com.example.runningh.myapplication.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.example.runningh.myapplication.aidl.IBookManager))) {
return ((com.example.runningh.myapplication.aidl.IBookManager)iin);
}
return new com.example.runningh.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
複製代碼
private static class Proxy implements com.example.runningh.myapplication.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.example.runningh.myapplication.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.example.runningh.myapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.runningh.myapplication.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();
}
}
}
複製代碼
@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.example.runningh.myapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.example.runningh.myapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.runningh.myapplication.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
複製代碼
總結:上述就是Binder的工做機制,咱們須要注意的是當客戶端發起遠程請求時,因爲當前線程會被掛起直至服務器端進程返回數據,因此若是一個遠程方法是很耗時的,那麼不能在UI線程中發起遠程請求;其次因爲服務端的Binder方法運行在Binder的線程池中,因此Binder方法無論是否耗時都是應該採用同步的方法去實現,由於它已經運行在一個線程中了。
參考:這篇文章是對《Android開發藝術探索》中IPC機制一章中的Binder內容進行的總結。這本書的做者任玉剛是一個大牛,這本書也是很通俗易懂,對Android開發的進階有很是大的幫助。