前言:IPC機制必然會碰到AIDL,網上相關的博客很多,但總不如自己走一邊來的印象深刻。
預備的所有文件
關於如何使用AIDL,網上很多也比較簡單,不贅述了。
Book.aidl
// Book.aidl
package com.lct.zyw.serviceaidl;
parcelable Book;
Book.java
package com.lct.zyw.serviceaidl;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by zhangyawen on 2016/9/14.
*/
public class Book implements Parcelable{
private int id;
private String name;
public Book() {
}
public Book(int id, String name) {
this.id = id;
this.name = name;
}
protected Book(Parcel in) {
id = in.readInt();
name = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Describe the kinds of special objects contained in this Parcelable's
* marshalled representation.
*
* @return a bitmask indicating the set of special object types marshalled
* by the Parcelable.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
/**
* 用於AIDL接口方法中Book類型的參數的tag爲out,inout。
* 如果該方法改成空實現,則服務端對參數的任何修改不會改變客戶端傳入的參數,其實最關鍵的是搞懂IBookManagerInterface.java的流程,就能知其所以然了。
* @param source
*/
public void readFromParcel(Parcel source) {
id = source.readInt();
name = source.readString();
}
@Override
public String toString() {
return "Book { id: "+this.id+", name: "+this.name+"}";
}
}
IBookManagerinterface.aidl
// IBookManagerInterface.aidl
// android studio 不會自動生成對應aidl文件的java文件,具體做法是在aidl文件下Ctrl+ F9(Make Project)
// build成功之後在對應項目文件路徑下的\build\generated\source\aidl\debug中就會生成aidl文件的對應的java文件
package com.lct.zyw.serviceaidl;
//需要手動導入(即使在同一個包下)
import com.lct.zyw.serviceaidl.Book;
interface IBookManagerInterface {
void doSomething(int x,int y);
List<Book> getBooks();
//關於定向Tag,在對應的java文件中需要深入分析
Book addBookIn(in Book book);
Book addBookOut(out Book book);
Book addBookInout(inout Book book);
}
分析IBookManagerinteraface.java
網上很多時從服務端代碼往客戶端代碼分析的,我是從客戶端代碼往服務端代碼分析的,其實都差不多,個人習慣而已
IBookManagerInterface.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\ServiceAIDL\\serviceprot\\src\\main\\aidl\\com\\lct\\zyw\\serviceaidl\\IBookManagerInterface.aidl
*/
/* *
* Binder的框架:
* 1.Android Binder框架分爲服務器接口、Binder驅動、以及客戶端接口;簡單想一下,需要提供一個全局服務,
* 那麼全局服務那端即是服務器接口,任何程序即客戶端接口,它們之間通過一個Binder驅動訪問。
* 2.服務器端接口:實際上是Binder類的對象,該對象一旦創建,內部則會啓動一個隱藏線程,會接收Binder驅動發送的消息,
* 收到消息後,會執行Binder對象中的onTransact()函數,並按照該函數的參數執行不同的服務器端代碼。
* 3.Binder驅動:該對象也爲Binder類的實例,客戶端通過該對象訪問遠程服務。
* 4.客戶端接口:獲得Binder驅動,調用其transact()發送消息至服務器
* */
package com.lct.zyw.serviceaidl;
/**
* 1.接口的繼承仍然用[extends]關鍵字
* 2.所有可以在Binder中傳輸的接口都需要繼承IInterface接口
* 3.雖然該Java文件時根據對應的aidl文件由android studio自動生成的,在理解細節實現之後,就可以不依賴aidl文件,
* 直接手寫IBookManagerInterface.java,甚至將該java文件中的代碼拆分到服務端和客戶端中去。
*
*/
public interface IBookManagerInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* 聲明瞭一個Stub的靜態抽象內部類,繼承了Binder,即使一個Binder的子類,實現IBookManagerInterface接口
* 說明:1.抽象類實現接口,則接口中的抽象方法可以不在抽象方法中實現,交由繼承該抽象類的實現類(非抽象類)來實現,此時就必須實現。
* 2.若抽象類中實現了接口中的抽象方法,那麼實現類(繼承自抽象類)就可以不實現,也可以實現(方法的重寫)。
*/
public static abstract class Stub extends android.os.Binder implements com.lct.zyw.serviceaidl.IBookManagerInterface {
//Binder類(Stub)的唯一標識,一般以類名錶示
private static final java.lang.String DESCRIPTOR = "com.lct.zyw.serviceaidl.IBookManagerInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lct.zyw.serviceaidl.IBookManagerInterface interface,
* generating a proxy if needed.
* 將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象
* obj:客戶端綁定服務端成功後,ServiceConnection中的onServiceConnected方法被調用,返回的service(Ibinder接口)
*/
public static com.lct.zyw.serviceaidl.IBookManagerInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//通過標識,檢驗obj是否屬於本進程(客戶端)中的Ibinder
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lct.zyw.serviceaidl.IBookManagerInterface))) {
return ((com.lct.zyw.serviceaidl.IBookManagerInterface) iin);
}
//進程間通信最終返回客戶端的是Stub類中的內部代理類Proxy對象,那麼在客戶端的任何服務端方法調用都是Proxy類中對應方法的調用。
return new com.lct.zyw.serviceaidl.IBookManagerInterface.Stub.Proxy(obj);
}
/**
* IInterface接口中的方法 一個Binder類返回一個Ibinder接口(Binder實現了Ibinder接口),
* 這其實是一種向上轉型。
* @return
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 該方法時通過Proxy類中的transact方法發起的
* @param code 方法ID:和Proxy類中的transact方法中的一一對應
* @param data 客戶端傳遞過來的參數
* @param reply 服務器返回回去的值
* @param flags 標明是否有返回值,0爲有(雙向),1爲沒有(單向)
* @return
* @throws android.os.RemoteException
*/
@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_doSomething: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.doSomething(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_getBooks: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lct.zyw.serviceaidl.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBookIn: {
//與客戶端的writeInterfaceToken對用,標識遠程服務的名稱
data.enforceInterface(DESCRIPTOR);
//將data轉換成Book類
com.lct.zyw.serviceaidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//調用服務端 new IBookManagerInterface.Stub()實現類的具體方法
com.lct.zyw.serviceaidl.Book _result = this.addBookIn(_arg0);
//將返回值_result轉換成 reply,返回給客戶端
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addBookOut: {
data.enforceInterface(DESCRIPTOR);
com.lct.zyw.serviceaidl.Book _arg0;
/**
* 相比tag爲in,服務端僅僅自己new一個Book類的對象,參數_data並沒有對book對象進行賦值操作
* 而tag爲in的方法是有賦值操作的
*/
_arg0 = new com.lct.zyw.serviceaidl.Book();
com.lct.zyw.serviceaidl.Book _result = this.addBookOut(_arg0);
reply.writeNoException();
/**
* 此處將服務端返回的_result(Book)賦值給reply(Parcel)(不管定向Tag是什麼,操作都是一樣的,不解釋)
*/
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
/**
* Note:
* 相比tag爲in,此處代碼是多出來的,這裏是將服務端的入參_arg0賦值給reply,那就說服務端方法的入參就是返回值,
* 如果服務端方法中不對入參_arg0做任何操作,那麼返回的將是一個新new出來的Book對象,需注意。
*/
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addBookInout: {
data.enforceInterface(DESCRIPTOR);
com.lct.zyw.serviceaidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
com.lct.zyw.serviceaidl.Book _result = this.addBookInout(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
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);
}
/**
* 客戶端綁定服務端成功後,執行的直接方法都是內部代理Proxy類中對應的方法
*/
private static class Proxy implements com.lct.zyw.serviceaidl.IBookManagerInterface {
private android.os.IBinder mRemote;
//mRemote被賦值:客戶端的傳進來的service(Ibinder接口類型)
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 doSomething(int x, int y) 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.writeInt(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_doSomething, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<com.lct.zyw.serviceaidl.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.lct.zyw.serviceaidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lct.zyw.serviceaidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
/**
* _data: 輸入型Parcel對象
* _reply: 輸出型Parcel對象
* _result: 返回值對象(類型根據返回值類型而定)
*/
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lct.zyw.serviceaidl.Book _result;
try {
//服務器端的enforceInterfac對應
_data.writeInterfaceToken(DESCRIPTOR);
//將寫入參數到_data中去(此處也證明了關於進程間通信的自定義類型的參數對象(Book),必須是實現Parcelable接口的)
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
/**
* transact(int code, Parcel data, Parcel reply, int flags)方法對應的參數及自身說明
*
* code: 方法ID,唯一性,一一對應,服務端onTransact方法會根據客戶端傳過來的方法ID來確定調用具體的方法
* data: 用來存儲客戶端傳遞給服務端的Parcel類型的數據
* reply: 用來存儲客戶端傳回給客戶端的Parcel類型的數據
* flags: 是否有返回值:爲 0 表示數據可以雙向流通,即 _reply 流可以正常的攜帶數據回來,
* 爲 1 的話那麼數據將只能單向流通,從服務端回來的 _reply 流將不攜帶任何數據
* Note:默認爲0,不解釋。
*
* 調用該方法來發起RPC(遠程過程調用)請求,同時當前線程掛起;然後服務端的onTransact方法會被調用,
* 直到RPC過程返回後,當前線程繼續執行
* Note:此處提到了線程掛起,說明RPC請求很可能是耗時操作,那麼在客戶端調用服務端的方法時,最好避免直接在UI
* 線程中直接調用,以免引起ANR,另起線程更安全
*/
mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
//_reply接受到服務端的數據,轉換成返回值的數據類型(Book)返回給客戶端
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
/**
* Recycle 機制(Note: 並不是java虛擬機的垃圾回收機制)
* 簡釋:
* 當一個對象不再使用時把它儲藏起來,不讓虛擬機回收,需要的時候再從倉庫裏拿出來重新使用,
* 這就避免了對象被回收後再重分配的過程。對於在應用的生命週期內(或者在循環中)需要頻繁
* 創建的對象來說這個機制特別實用,可以顯著減少對象創建的次數,從而減少 GC 的運行時間。
* 運用得當便可改善應用的性能。唯一的不足只是需要手動爲廢棄對象調用 recycle 方法。
* 文/TiouLims(簡書作者)
* 原文鏈接:http://www.jianshu.com/p/5cba251c7fd9
*/
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 關於定向Tag(in out inout)的分析(Note:在aidl文件中方法的傳入參數設置定向Tag,最根本的還是android studio會根據Tag
* 在對應java代碼中方法的實現上有區別)
* 該方法的傳入參數的定向tag在aidl文件中定義爲out。
* @param book
* @return
* @throws android.os.RemoteException
*/
@Override
public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
/**
* _data: 輸入型Parcel對象
* _reply: 輸出型Parcel對象
* _result: 返回值對象(類型根據返回值類型而定)
*/
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lct.zyw.serviceaidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
/**
* 相比tag爲in,該方法的入參book並沒有轉化成_data,通過transcat方法傳遞給服務端,那就是說,參數定向tag爲out的方法
* 服務端將不接受的_data參數(Ontransact方法中說明)
*/
mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
_reply.readException();
/**
* 此處將服務端返回的_reply轉化成Book對象,並返回給客戶端(不管定向Tag是什麼,操作都是一樣的,不解釋)
*/
if ((0 != _reply.readInt())) {
_result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
/**
* 相比tag爲in,客戶端的入參book被重新賦值,關鍵在Book.java中需要額外的實現readFromParcel方法,
* 那麼客戶端的入參book到底會有什麼變化,主要看readFromParcel方法的具體實現:
* 1.參照Book.java中的構造函數Book(Parcel in)實現,那麼入參將和返回值相同
* 2.readFromParcel是空實現,那麼入參沒有任何變化
*/
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 定向Tag爲inout,則方法實現是同時擁有in和out的並集效果,相對的底層的開銷也會比較大,沒有必要就不要這麼做了。
* @param book
* @return
* @throws android.os.RemoteException
*/
@Override
public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lct.zyw.serviceaidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//在aidl中定義的方法都會在此得到唯一的ID
static final int TRANSACTION_doSomething = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBookIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_addBookOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_addBookInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
}
public void doSomething(int x, int y) throws android.os.RemoteException;
public java.util.List<com.lct.zyw.serviceaidl.Book> getBooks() throws android.os.RemoteException;
public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
}
擴展鏈接:對Binder的淺顯分析及AIDL的使用
結語:好記性不如爛筆頭,寫下來,方便以後查找。