前言
AIDL其實是屬于IPC方面的東西,而Android的IPC是一個比較大的內(nèi)容秉沼,主要涉及到Android的跨進程通信的內(nèi)容桶雀,此篇文章會先介紹一下Android的IPC。
Android IPC簡介
IPC是Inter-Process Communication的縮寫唬复,意思就是進程間通信或者跨進程的通信矗积,就是兩個進程之間交換數(shù)據(jù)的過程。說到進程敞咧,就不得不說到進程和線程這兩個東西棘捣,這兩個是完全不一樣的概念,簡單描述就是休建,一個進程可以包含多個線程乍恐,也就是一對多的關(guān)系。進程一般指一個執(zhí)行單元测砂,線程是CPU調(diào)度的最小單元茵烈。在Android里面,進程里可以只有一個線程砌些,即主線程呜投,在Android里面也可以叫作UI線程,一般在這個線程里面都是進行UI更新的操作存璃,而耗時的操作都是交給子線程去進行仑荐,這樣可以防止Android的ANR狀態(tài)。
Android的多進程模式
再講IPC之前我們先來講講Android的多進程模式纵东,在Android中多進程是值一個應用中存在多個進程的情況粘招,所以這里暫時不討論兩個應用之間的情況。在Android使用多進程的只有一種方法篮迎,那就是給四大組件在AndroidMenifest中指定android:process屬性男图。參數(shù)為你要自定義的包名示姿,如果沒有指定進程的話甜橱,那么就會運行在默認的進程中逊笆。
<activity android:name=".MainActivity"
android:process=":remote">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
但是,看起來使用多進程模式很簡單岂傲,但其實是會造成一些問題的难裆,比如:
- 靜態(tài)成員和單例模式完全失效。
- 線程同步機制完全失效镊掖。
- SharedPreferences的可靠性下降乃戈。
- Application會多次創(chuàng)建。
雖然多進程會帶來很多問題亩进,但是不能因為就這樣而不去正視它們症虑,為了解決這個問題,系統(tǒng)提供了很多跨進程通信的方法归薛,雖說不能直接的共享內(nèi)存谍憔,但是通過跨進程通信我們還是可以實現(xiàn)數(shù)據(jù)的交互,實現(xiàn)跨進程的通信方式有很多種方法主籍,我們后續(xù)會進行講解习贫。
AIDL的使用
剛剛也說過了,跨進程的通信方式有很多千元,比如通過BInder來進行跨進程通信苫昌,還有ContentProvider,它天生就是支持跨進程訪問的幸海,此外也可以使用Bundle祟身,Messager等等方式,還有一種方式就是AIDL物独,AIDL 意思即 Android Interface Definition Language袜硫,翻譯過來就是Android接口定義語言,是用于定義服務器和客戶端通信接口的一種描述語言议纯,可以拿來生成用于IPC的代碼父款。從某種意義上說AIDL其實是一個模板,因為在使用過程中瞻凤,實際起作用的并不是AIDL文件憨攒,而是據(jù)此而生成的一個IInterface的實例代碼。
而剛剛說的像Messager之類的底層其實就是AIDL肝集,只是Messager做了一些封裝。還有一個就是Binder蛛壳,它也是Android里跨進程通信里的一種機制杏瞻,而AIDL是對Binder的封裝所刀。因為要實現(xiàn)IBinder來支持遠程調(diào)用,應從Binder類派生一個類捞挥。而使用AIDL浮创,它會自動生成Binder的派生類,可以直接用砌函,那么怎么用AIDL進行跨進程通信呢
我們這里舉一個例子斩披,創(chuàng)建一個App,我們稱之為服務端讹俊,然后先創(chuàng)建一個一個AIDL文件垦沉,然后自定義一個方法,創(chuàng)建后記住要build一下仍劈,build之后AIDL就會快速生成對應的java文件了厕倍。
interface IAppServiceRemoteBinder {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
//定義一個set方法
void setData(String data);
}
這里說明一下,AIDL支持的數(shù)據(jù)類型有以下這些:
- 基本數(shù)據(jù)類型(int贩疙、long讹弯、char、boolean屋群、double)
- String和CharSequence
- List和Map集合
- 集合內(nèi)元素必須是AIDL支持的數(shù)據(jù)類型
- 服務端具體使用的集合必須是ArrayList和HashMap
- Parcelable:實現(xiàn)了Parcelable接口的對象
- AIDL本身接口也可以在AIDL文件使用闸婴。
接著我們創(chuàng)建一個服務,因為其實AIDL就是基于Binder芍躏,所以我們就直接在onBind()
里面進行返回邪乍。
public class AppService extends Service {
private String data = "默認數(shù)據(jù)";
private boolean running = false;
public AppService() {
}
@Override
public IBinder onBind(Intent intent) {
//我們這里直接返回這個接口
return new IAppServiceRemoteBinder.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void setData(String data) throws RemoteException {
//客戶端傳過來的值賦值給data
AppService.this.data = data;
}
};
}
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "service started");
//創(chuàng)建一個測試線程,把客戶端傳過來的數(shù)據(jù)進行循環(huán)輸出对竣。
new Thread() {
@Override
public void run() {
super.run();
running = true;
while (running) {
try {
Thread.sleep(1000);
Log.d("TAG", data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("TAG", "service onDestroy");
running = false;
}
}
接著我們創(chuàng)建一個客戶端庇楞,來遠程啟動這個服務,首先我們要把之前創(chuàng)建的那個AIDL文件給復制過來否纬,這里需要注意的是吕晌,包名的路徑是要相同的,然后記住要build临燃,然后設置一下布局睛驳。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 啟動外部服務" />
<Button
android:id="@+id/btn_stop_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 停止外部服務" />
<Button
android:id="@+id/btn_bind_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 綁定外部服務" />
<Button
android:id="@+id/btn_unbind_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 解除外部服務" />
<Button
android:id="@+id/btn_edit_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 發(fā)送值給服務端" />
</LinearLayout>
然后在MainActivity里輸入以下代碼。
//要實現(xiàn)ServiceConnection方法
public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
private Button btnStart;
private Button btnStop;
private Button btnBind;
private Button btnUnBind;
private Button btnEdit;
private Intent serviceIntent;
private IAppServiceRemoteBinder binder = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent();
//這里設置intent的包名膜廊,第一個參數(shù)為要啟動app的包名乏沸,第二個為服務包名
serviceIntent.setComponent(new ComponentName("com.ju.startservicefromanotherapp", "com.ju.startservicefromanotherapp.AppService"));
btnStart = findViewById(R.id.btn_start_service);
btnStart.setOnClickListener(this);
btnStop = findViewById(R.id.btn_stop_service);
btnStop.setOnClickListener(this);
btnBind = findViewById(R.id.btn_bind_service);
btnBind.setOnClickListener(this);
btnUnBind = findViewById(R.id.btn_unbind_service);
btnUnBind.setOnClickListener(this);
btnEdit = findViewById(R.id.btn_edit_service);
btnEdit.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start_service:
//遠程啟動服務
startService(serviceIntent);
break;
case R.id.btn_stop_service:
//遠程開始服務
stopService(serviceIntent);
break;
case R.id.btn_bind_service:
//遠程綁定服務
bindService(serviceIntent, this, Context.BIND_AUTO_CREATE);
break;
case R.id.btn_unbind_service:
//遠程解綁服務
unbindService(this);
binder = null;
break;
case R.id.btn_edit_service:
//這里給服務端傳值設置
if (binder != null) {
try {
binder.setData("hello");
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("TAG", "Bind Service");
Log.d("TAG", "IBinder:" + service);
//這里進行實例化接口,不能強轉(zhuǎn)爪瓜。
binder = IAppServiceRemoteBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
這里我們通過IAppServiceRemoteBinder.Stub.asInterface()
方法來獲取到Service里的Binder,然后就可以直接調(diào)用Binder里的setData()
方法了蹬跃。最后把這啟動這個App,點擊綁定外部服務按鈕铆铆,服務就會運行起來了蝶缀,然后點擊發(fā)送給值客戶端的按鈕丹喻,就會遠程發(fā)送一個值給服務端進行接收了。
如果說傳輸?shù)氖且粋€Object對象的話翁都,就需要對Object進行序列化操作碍论,Java的序列化是繼承Serialzable接口,而Android也有一個自己的序列化荐吵,就是繼承Parcelable接口骑冗。
參考
Android中AIDL的理解和使用
[任玉剛] Android開發(fā)藝術(shù)探索