1.Binder 介紹
從 Android 層面來說及汉,Binder 就是客戶端和服務(wù)端進(jìn)行通信的媒介山林,當(dāng) bindService
的時(shí)候银受,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的 Binder 對(duì)象奋岁,通過這個(gè) Binder 對(duì)象,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)环础,這里的服務(wù)包括普通服務(wù)和基于 AIDL 的服務(wù)旨指。Binder 主要用于 Service,包括 AIDL 和 Messenger喳整,其實(shí) Messenger 的底層是 AIDL。
2.使用 AIDL
這里主要介紹一下思路裸扶,具體參考一下 Android:學(xué)習(xí)AIDL框都,這一篇文章就夠了(上) 進(jìn)行項(xiàng)目搭建。
-
一呵晨、新建服務(wù)端工程
服務(wù)端代碼:https://github.com/innovatorCL/IPCServer
1.新建 aidl 包魏保,客戶端和服務(wù)器的包名必須一樣。
-
2.新建非基本數(shù)據(jù)類型的數(shù)據(jù)文件摸屠,包括 xx.java 和 xx.aidl 文件谓罗,同時(shí)在 Module 的 gradle 文件中聲明源碼路徑包含 aidl,不然會(huì)報(bào)找不到文件的錯(cuò)誤季二。
sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } }
AIDL包 -
3.新建接口方法 xxx.aidl 文件檩咱。所有需要被客戶端調(diào)用的方法在這個(gè)文件中聲明揭措。
AIDL 定義接口 4.新建一個(gè) Service 作為服務(wù)端,在里面是實(shí)現(xiàn)接口 xxx.aidl 聲明的方法刻蚯,并實(shí)例化成為一個(gè) XXX 對(duì)象绊含,在客戶端 bind 服務(wù)端的時(shí)候返回給客戶端(其實(shí)返回給客戶端的是 Android 生成的 XXX.java 里面的內(nèi)部代理類對(duì)象,XXX.Stub.Proxy炊汹,客戶端通過這個(gè)對(duì)象調(diào)用服務(wù)端 Service 的方法躬充。)
/**
*
* 服務(wù)端的AIDLService.java
*
* 在服務(wù)端實(shí)現(xiàn)AIDL中定義的方法接口的具體邏輯,
* 然后在客戶端調(diào)用這些方法接口讨便,從而達(dá)到跨進(jìn)程通信的目的充甚。
* Created by innovator on 2018/1/18.
*/
public class AIDLService extends Service{
//包含Book的List
private List<Book> mBooks = new ArrayList<>();
//由AIDL生成的BookManager類的代理類
private final BookManager.Stub mBookManager = new BookManager.Stub() {
@Override
public List<Book> getBooks() throws RemoteException {
synchronized (this){
Log.i("TAG","服務(wù)端發(fā)送數(shù)據(jù)給客戶端");
if(mBooks != null){
return mBooks;
}
return new ArrayList<>();
}
}
@Override
public void addBook(Book book) throws RemoteException {
Log.i("TAG","服務(wù)端接收客戶端的數(shù)據(jù)");
synchronized (this) {
if (mBooks == null) {
mBooks = new ArrayList<>();
}
if (book == null) {
Log.e("TAG", "客戶端傳了一個(gè)空的Book對(duì)象");
book = new Book();
}
//因?yàn)?getBook 的參數(shù)的 tag 是 inout,所以服務(wù)端修改了book的參數(shù)霸褒,客戶端的應(yīng)該也會(huì)修改
book.setPrice(6666);
if (!mBooks.contains(book)) {
mBooks.add(book);
}
//打印mBooks列表伴找,觀察客戶端傳過來的值
Log.e("TAG", "服務(wù)端的 addBooks() 方法被調(diào)用 , 打印服務(wù)端收到的數(shù)據(jù) : " + mBooks.toString());
}
}
};
@Override
public void onCreate() {
Book book = new Book();
book.setName("在復(fù)雜的世界做個(gè)明白人");
book.setPrice(2222);
mBooks.add(book);
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG", String.format("服務(wù)端連接上了客戶端,連接的 ntent 是 %s", intent.toString()));
return mBookManager;
}
}
在 Manifest.xml 中聲明該 Service:
<service
android:name=".Service.AIDLService"
android:exported="true">
<intent-filter>
<action android:name="com.innovator.aidl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
-
二、新建客戶端工程
客戶端代碼:https://github.com/innovatorCL/IPCClient
-
1.新建和服務(wù)端同名的 aidl 包傲霸,將服務(wù)端的 aidl 文件全部 copy 過去疆瑰。
AIDL 定義接口 2.在 MainActivity 中連接服務(wù)端 Service ,并獲取服務(wù)端返回的代理對(duì)象昙啄,調(diào)用服務(wù)端的方法傳入或者讀取服務(wù)端的數(shù)據(jù)穆役。
-
/**
*
* IPC 客戶端的 MainActivity.java
*/
public class MainActivity extends Activity {
//由AIDL文件生成的Java類
private BookManager mBookManager;
//標(biāo)志當(dāng)前與服務(wù)端連接狀況的布爾值,false為未連接梳凛,true為連接中
private boolean mBound;
//包含Book對(duì)象的list
private List<Book> mBooks;
private Book book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("TAG","連接上了服務(wù)端");
mBookManager = BookManager.Stub.asInterface(service);
mBound = true;
//獲取代理對(duì)象耿币,調(diào)用服務(wù)端的方法
if(mBookManager != null){
try {
mBooks = mBookManager.getBooks();
Log.i("TAG","獲取到了服務(wù)端數(shù)據(jù):"+mBooks.toString());
}catch (RemoteException e){
e.printStackTrace();
Log.i("TAG","調(diào)用服務(wù)端方法出現(xiàn)異常:"+e.getMessage());
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
Log.i("TAG","斷開服務(wù)端連接");
}
};
/**
* 按鈕的點(diǎn)擊事件,點(diǎn)擊之后調(diào)用服務(wù)端的addBookIn方法
*
* @param v
*/
public void addBook(View v){
if(!mBound){
attemptBindService();
Toast.makeText(this, "當(dāng)前與服務(wù)端處于未連接狀態(tài)韧拒,正在嘗試重連淹接,請(qǐng)稍后再試", Toast.LENGTH_SHORT).show();
return;
}
if(mBookManager == null){
return;
}
book = new Book();
book.setName("認(rèn)知突圍");
book.setPrice(88);
//調(diào)用服務(wù)器端的方法,傳入一個(gè)Book對(duì)象
try{
mBookManager.addBook(book);
Log.i("TAG","調(diào)用服務(wù)端方法傳入一個(gè)Book");
}catch (RemoteException e){
e.printStackTrace();
Log.i("TAG","調(diào)用服務(wù)端方法出現(xiàn)異常:"+e.getMessage());
}
}
/**
* 點(diǎn)擊了 addBook 之后看看客戶端的數(shù)據(jù)有沒有同步被改
* 驗(yàn)證 inout tag 的功能
*
* 確實(shí)是同步更改了 inout 傳入的那個(gè)對(duì)象的值
* @param v
*/
public void getBooks(View v) {
Log.i("TAG","打印 addBooks 之后的數(shù)據(jù) :"+book.toString());
}
/**
* 嘗試連接上服務(wù)器
*/
private void attemptBindService(){
Intent i = new Intent();
i.setAction("com.innovator.aidl");
i.setPackage("com.innovator.ipcserver");
bindService(i,mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStart() {
super.onStart();
if(!mBound){
attemptBindService();
}
}
@Override
protected void onStop() {
super.onStop();
if(mBound){
unbindService(mServiceConnection);
mBound = false;
}
}
}
3. AIDL 工作流程分析
首先介紹幾個(gè)類:
Stub : 生成的 AIDL 的內(nèi)部類叛溢,繼承 Binder, 實(shí)現(xiàn)了 IInterface 接口塑悼;
Proxy : Stub 的內(nèi)部類,實(shí)現(xiàn)了我們自定義的 AIDL 接口楷掉;
BinderProxy : Binder 的內(nèi)部類厢蒜,跨進(jìn)程通信時(shí)傳遞的對(duì)象
-
流程一:進(jìn)程間通信時(shí),Client 端開啟服務(wù) bindService, Server 端 onBind 方法返回 Stub 對(duì)象烹植,通過 Binder 封裝成 BinderProxy 對(duì)象 返回斑鸦,到 Client 端的 onServiceConnected 方法接收到的就是 BinderProxy 對(duì)象,再調(diào)用 BookManager.Stub.asInterface(service) 封裝成 Proxy, BinderProxy 是它的成員變量草雕。
到這里巷屿,Client 端已經(jīng)拿到了 Service 端傳遞過來的 Binder 對(duì)象。
流程二:Client 端調(diào)用方法(例:getBookList)時(shí)墩虹,會(huì)先調(diào)用 Proxy.getBookList, 這里會(huì)調(diào)用 mRemote.transact(這個(gè)是在客戶端進(jìn)程做的事), mRemote 也就是 BinderProxy 對(duì)象嘱巾,實(shí)現(xiàn)跨進(jìn)程的操作, BinderProxy 里面封裝了 Stub 對(duì)象憨琳,所以會(huì)再跨進(jìn)程調(diào)用到 Server 端的 Stub.onTransact(這個(gè)是在服務(wù)端的 Binder 線程池做的事), Stub.onTransact 內(nèi)部調(diào)用 Service 里自己實(shí)現(xiàn)的 getBookList 方法,執(zhí)行完后跨進(jìn)程返回到 BinderProxy 中浓冒,BinderProxy 再跨進(jìn)程返回到 Client 端栽渴,到此一次方法調(diào)用結(jié)束。
補(bǔ)充一張進(jìn)程間方法調(diào)用的圖稳懒。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/innovator/AndroidStudioProjects/IPCServer/app/src/main/aidl/com/innovator/ipcclient/BookManager.aidl
*/
package com.innovator.ipcclient;
//定義方法接口
public interface BookManager extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.innovator.ipcclient.BookManager {
private static final java.lang.String DESCRIPTOR = "com.innovator.ipcclient.BookManager";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.innovator.ipcclient.BookManager interface,
* generating a proxy if needed.
*/
public static com.innovator.ipcclient.BookManager asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.innovator.ipcclient.BookManager))) {
return ((com.innovator.ipcclient.BookManager)iin);
}
return new com.innovator.ipcclient.BookManager.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_getBooks: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.innovator.ipcclient.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.innovator.ipcclient.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.innovator.ipcclient.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
if ((_arg0!=null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.innovator.ipcclient.BookManager {
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;
}
//所有的返回值前都不需要加任何東西闲擦,不管是什么數(shù)據(jù)類型
@Override
public java.util.List<com.innovator.ipcclient.Book> getBooks() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.innovator.ipcclient.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.innovator.ipcclient.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
//傳參時(shí)除了Java基本類型以及String,CharSequence之外的類型
//都需要在前面加上定向tag场梆,具體加什么量需而定
@Override
public void addBook(com.innovator.ipcclient.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();
if ((0!=_reply.readInt())) {
book.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
//所有的返回值前都不需要加任何東西墅冷,不管是什么數(shù)據(jù)類型
public java.util.List<com.innovator.ipcclient.Book> getBooks() throws android.os.RemoteException;
//傳參時(shí)除了Java基本類型以及String,CharSequence之外的類型
//都需要在前面加上定向tag或油,具體加什么量需而定
public void addBook(com.innovator.ipcclient.Book book) throws android.os.RemoteException;
}
詳細(xì)分析參考:
- Android 藝術(shù)開發(fā)探索寞忿,Android 根據(jù) AIDL 文件自動(dòng)生成對(duì)應(yīng)的 java 文件 (P55 —— P60)
- Android:學(xué)習(xí)AIDL,這一篇文章就夠了(下)
4. IPC 應(yīng)用場(chǎng)景
- 騰訊系顶岸、阿里系等大廠的 APP 之間的通信
- 系統(tǒng) APP 和系統(tǒng)服務(wù)之間的通信
- 推送腔彰、IM
- 雙進(jìn)程守護(hù)