AIDL是 Android Interface definition language的縮寫,一看就明白,它是一種Android內部進程通信接口的描述語言,通過它我們可以定義進程間的通信接口。
client app可通過aidl調用直接喚醒server app,類似于intent啟動另一個app的activity,相比而言,在5.0以后,broadcast已經無法喚醒另一個未啟動過的app。
AIDL的使用
最終效果
client調用aidl接口向server打招呼,server回調client的callback實現回應招呼。
一般是這樣用的,client遠程調用server端提供的service接口執行任務,server端回調,異步通知client端任務執行的狀態。
客戶端:
MainActivity
public class MainActivity extends Activity {
private Button bindBtn;
private Button greetBtn;
private Button unbindBtn;
private int count = 0;
private IGreetBinder iGreetBinder;
private Handler handler = new Handler();
private IGreetCallback mCallback = new IGreetCallback.Stub() {
@Override
public void greetBack(final Person someone) throws RemoteException {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"greet back"+someone,Toast.LENGTH_LONG).show();
}
});
}
};
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("ServiceConnection", "onServiceConnected() called");
iGreetBinder = IGreetBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//This is called when the connection with the service has been unexpectedly disconnected,
//that is, its process crashed. Because it is running in our same process, we should never see this happen.
Log.i("ServiceConnection", "onServiceDisconnected() called");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindBtn = (Button) findViewById(R.id.bindBtn);
bindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.AIDLService");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
bindBtn.setEnabled(false);
greetBtn.setEnabled(true);
unbindBtn.setEnabled(true);
}
});
greetBtn = (Button) findViewById(R.id.greetBtn);
greetBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
count++;
String retVal = iGreetBinder.greet(new Person(count,"person"+count),mCallback);
Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
}
}
});
unbindBtn = (Button) findViewById(R.id.unbindBtn);
unbindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
bindBtn.setEnabled(true);
greetBtn.setEnabled(false);
unbindBtn.setEnabled(false);
}
});
}
}
build.gradle
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
服務端:
AIDLService
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IGreetBinder.Stub stub = new IGreetBinder.Stub() {
@Override
public String greet(final Person someone, final IGreetCallback iGreetCallback) throws RemoteException {
Log.i(TAG, "greet() called");
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
iGreetCallback.greetBack(someone);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
return "hello, " + someone;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() called");
return true;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() called");
}
}
AndroidManifest.xml
<service android:name=".AIDLService">
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
// 需定義action,提供給客戶端調起;
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
build.gradle
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
AIDL部分
服務端和客戶端共用aidl部分。
Person.java
public class Person implements Parcelable {
public int age;
public String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
}
public void readFromParcel(Parcel in){
name = in.readString();
age = in.readInt();
}
protected Person(Parcel in) {
age = in.readInt();
name = in.readString();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public String toString() {
return age + ":" + name;
}
}
傳遞Person需要實現Parcelable接口。Aidl支持基本數據類型(int、long、char 等)String 和 CharSequence,List:只支持ArrayList,里面的每個元素都必須被AIDL支持。,Map: 只支持HashMap, 里面的每個元素都必須被AIDL支持。Parcelable: 所有實現了Parcelable接口的對象。
Parcelable 接口
Parcelable 可以是 android 系統將 objectes 分解成可以被進程處理的原始種類必須提供一個名為CREATOR的static final屬性 該屬性需要實現android.os.Parcelable.Creator接口,writeToParcel 注意寫入變量和讀取變量的順序應該一致,不然得不到正確的結果,readFromParcel 注意讀取變量和寫入變量的順序應該一致,不然得不到正確的結果。
Person.aidl
package com.test.aidldemo;
parcelable Person;
IGreetBinder.aidl
package com.test.aidldemo;
import com.test.aidldemo.IGreetCallback;
import com.test.aidldemo.Person;
interface IGreetBinder {
String greet(inout Person person,IGreetCallback cb);
}
添加服務端對客戶端的回調。
IGreetCallback.aidl
package com.test.aidldemo;
import com.test.aidldemo.Person;
interface IGreetCallback {
void greetBack(inout Person person);
}
使用Person類型參數時,需要提供方向指示符,其包括in、out、和inout。in表示由客戶端設置,out表示由服務端設置,inout表示客戶端和服務端都設置了該值。如不設置方向指示符,將報錯,基本類型int、String等無需設置方向指示符。
源碼
client端:https://github.com/woshizmxin/AidlClientDemo
server端:https://github.com/woshizmxin/AidlServerDemo
在該demo運行在5.0以上設備上,會報錯
** IllegalArgumentException: Service Intent must be explicit**
解決辦法參考: Service Intent must be explicit的解決方法
使用Messager
如果想做app進程間通信,aidl寫起來還是比較麻煩的,meassager則相當于是一個簡化版本。它基于Message,相信大家都很熟悉,不需要編寫aidl文件。
AIDL和Messager區別:
- Messenger本質也是AIDL,只是進行了封裝,開發的時候不用再寫.aidl文件。
結合我自身的使用,因為不用去寫.aidl文件,相比起來,Messenger使用起來十分簡單。但前面也說了,Messenger本質上也是AIDL,故在底層進程間通信這一塊,兩者的效率應該是一樣的。 - 在service端,Messenger處理client端的請求是單線程的,而AIDL是多線程的。使用AIDL的時候,service端每收到一個client端的請求時,就會啟動一個線程(非主線程)去執行相應的操作。而Messenger,service收到的請求是放在Handler的MessageQueue里面,Handler大家都用過,它需要綁定一個Thread,然后不斷poll message執行相關操作,這個過程是同步執行的。
當然是有辦法解決的,在server端,Messenger的Handler可以只當作一個轉發器,不處理請求,只轉發請求到相應的處理線程(多是相應的HandlerThread),這樣也可以達到異步的效果。 - client的方法,使用AIDL獲取返回值是同步的,而Messenger是異步的。Messenger只提供了一個方法進行進程間通信,就是send(Message msg)方法,發送的是一個Message,沒有返回值,要拿到返回值,需要把client的Messenger作為msg.replyTo參數傳遞過去,service端處理完之后,在調用客戶端的Messenger的send(Message msg)方法把返回值傳遞回client,這個過程是異步的,而AIDL你可以自己指定方法,指定返回值,它獲取返回值是同步的。
具體使用請移步:
Android 基于Message的進程間通信 Messenger完全解析
參考文章
Android中使用AIDL時的跨進程回調—Server回調Client
Service Intent must be explicit的解決方法
Android 基于Message的進程間通信 Messenger完全解析