一瑞筐、AIDL是什么块蚌?
如果你想深入學習基于Binder通信的知識纱控,請看我之前的文章:
AIDL(Android Interface Define Language) 是IPC進程間通信方式的一種.用于生成可以在Android設(shè)備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼唾那。
AIDL規(guī)定了兩個進程之間如何使用Binder來通信,AIDL文件就是接口和數(shù)據(jù)聲明。Android Studio利用我們寫好的AIDL文件生成Java文件,真正在打包成apk文件時會把aidl文件生成的java代碼打包進去,不會打包AIDL文件,那么很好理解鹊汛,Android Studio利用我們寫好的AIDL文件生成基于Binder進程通信java代碼至耻。binder進程通信是C/S和代理模式實現(xiàn)的。ADIL文件生成的java文件中可以看出來。
一句話總結(jié):
Android Studio利用我們寫好的AIDL文件生成基于Binder進程通信java代碼。生成的java代碼會被打包到apk文件逸吵,AIDL文件只是一個模具而已韩脑。
基于Binder的AIDL數(shù)據(jù)通信流程圖:
二进苍、AIDL通信支持的數(shù)據(jù)傳送類型
2.1 Android Binder支持傳送數(shù)據(jù)類型
進程通信就是交換數(shù)據(jù)沈贝,AIDL通信傳送支持的類型其實就是Binder支持的市俊,AIDL并不是支持所有的數(shù)據(jù)類型,java中有很多數(shù)據(jù)類型昭躺,java基本的數(shù)據(jù)類型帝洪,集合數(shù)據(jù)類型龙助,還有我們用戶自己定義的數(shù)據(jù)類型。那么AIDL支持哪些列?
共 4 種:
- Java 的基本數(shù)據(jù)類型
- List 和 Map
元素必須是 AIDL 支持的數(shù)據(jù)類型
Server 端具體的類里則必須是 ArrayList 或者 HashMap - 其他 AIDL 生成的接口
- 實現(xiàn) Parcelable 的實體
2.2 Android序列化方式解析
其中List和Map集合裝的實體也是要實現(xiàn)Parcelable 接口的蝗蛙,或者直接傳遞一個實現(xiàn)了Parcelable 接口的實體類。Parcelable 是Android自帶的序列化接口,那么相比Java自帶序列化接口有什么異同列?
1. Serializable(Java自帶):
Serializable是序列化的意思惊畏,表示將一個對象轉(zhuǎn)換成可存儲或可傳輸?shù)臓顟B(tài)浪讳。序列化后的對象可以在網(wǎng)絡(luò)上進行傳輸,也可以存儲到本地。實現(xiàn)方式很簡單,只需要實現(xiàn)Serializable就可以了。
2. Parcelable(android 專用):
Serializable是序列化的意思,表示將一個對象轉(zhuǎn)換成可存儲或可傳輸?shù)臓顟B(tài)。序列化后的對象可以在網(wǎng)絡(luò)上進行傳輸,也可以存儲到本地。實現(xiàn)方式復雜寂玲,需要復寫兩個方法彰檬,分別是describeContents和writeToParcel景图,實例化靜態(tài)內(nèi)部對象CREATOR,實現(xiàn)接口Parcelable.Creator 。這兩個Android Studio可以幫我智能實現(xiàn),另外左右的變量都需要實現(xiàn)set和get方法,Android Studio的快捷方式也是很方便實現(xiàn)的額。
選擇序列化方法的原則:
1) 在使用內(nèi)存的時候,Parcelable比Serializable性能高,所以推薦使用Parcelable。
2) Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC。
3) Parcelable不能使用在要將數(shù)據(jù)存儲在磁盤上的情況乍恐,因為Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下呜投。盡管Serializable效率低點纵东,但此時還是建議使用Serializable 甜橱。
注意:
需要在多個部件(Activity或Service)之間通過Intent傳遞一些數(shù)據(jù),簡單類型(如:數(shù)字、字符串)的可以直接放入Intent。復雜類型必須實現(xiàn)Parcelable接口。需要傳輸?shù)膶崿F(xiàn)了Parcelable接口的實體類,在Client端和Server端都需要用一個.aidl文件上聲明一下。
可以先看一下我們需要傳輸?shù)囊呀?jīng)實現(xiàn)了Parcelable接口的實體類:
package com.server.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/**
* Created by dawish on 2017/8/22.
* Binder傳遞實體類數(shù)據(jù)必須實現(xiàn)Parcelable接口,也可以更高效地在內(nèi)存中傳輸
*/
public class ServiceData implements Parcelable {
public String id;
public String name;
public String price;
public String type;
public ServiceData( ) {
}
/**
* 讀數(shù)據(jù)恢復
* 給createFromParcel里面用,IDE自動生成
* @param in
*/
protected ServiceData(Parcel in) {
id = in.readString();
name = in.readString();
price = in.readString();
type = in.readString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
/**
* IDE自動生成
* 讀取接口憨攒,目的是要從Parcel中構(gòu)造一個實現(xiàn)了Parcelable的類的實例處理杏瞻。
* 因為實現(xiàn)類在這里還是不可知的忧吟,所以需要用到模板的方式煌抒,繼承類名通過模板
* 參數(shù)傳入。
* 為了能夠?qū)崿F(xiàn)模板參數(shù)的傳入,這里定義Creator嵌入接口,內(nèi)含兩個接口函數(shù)
* 分別返回單個和多個繼承類實例。
*/
public static final Creator<ServiceData> CREATOR = new Creator<ServiceData>() {
@Override
public ServiceData createFromParcel(Parcel in) {
return new ServiceData(in);
}
@Override
public ServiceData[] newArray(int size) {
Log.i("danxx", "newArray size--->"+size);
return new ServiceData[size];
}
};
/**
* 內(nèi)容描述接口,基本不用管,IDE自動生成
*/
@Override
public int describeContents() {
return 0;
}
/**
* 寫入接口函數(shù),打包,IDE自動生成
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeString(price);
dest.writeString(type);
}
}
三、aidl文件編寫
3.1 aidl文件編寫注意事項
Android Studio寫aidl文件還不是很智能的,寫aidl文件就像是寫txt文本文件,沒有智能提示的赊瞬,所以很很關(guān)鍵的是里面的package 包名和import導包都要十分小心谤绳,不能寫錯了,Android Studio里面會有一個aidl文件夾,新建的aidl文件都在這個文件夾里面,這個文件夾里面還可以建立包名。包名跟java實體類的包名也要完全一致,不然也會報錯∩嘞猓總結(jié)需要注意的兩點:
3.1.1 Android Studio aidl文件夾里的包名要跟對應java實體類包名一致
如果不一致會報錯,無法找到對應的java類
3.1.2 aidl文件里面package 包名和import導包都要跟java實體類包名一致
不一致也是會報錯的卖擅,導包不對直接無法識別類
3.1.3 注意細節(jié)
1.接口名和aidl文件名相同。
2.接口和方法前不用加訪問權(quán)限修飾符public,private,protected等,也不能用final,static。
3.Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),
使用這些類型時不需要import聲明。對于List和Map中的元素類型必須是Aidl支持的類型。
如果使用自定義類型作為參數(shù)或返回值彻秆,自定義類型必須實現(xiàn)Parcelable接口扎附。
4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中香伴,應該顯式import,即便在該類和定義的包在同一個包中。
5.在aidl文件中所有非Java基本類型(需要實現(xiàn)Parcelable接口)參數(shù)必須加上in、out改橘、inout標記碌识,
以指明參數(shù)是輸入?yún)?shù)胖烛、輸出參數(shù)還是輸入輸出參數(shù)。
6.Java原始類型默認的標記為in,不能為其它標記。
注意:
我們想一下aidl文件最后都是要變成java文件的,文件的命名和導包的包名都要跟實際java類一模一樣。不然生成的java類里面也是有錯誤的,那么有可能導致直接無法生成R.java類,那么我們項目中在使用id的時候,R類都是找不到的。在我們的apk打包aidl工具在處理aidl文件時也是無法通過的疯兼。Android apk打包請看我之前的文章:Android apk打包流程
3.2編寫AIDL文件
主要的aidl文件姨裸,會根據(jù)此aidl文件生成基于binder夸進程通信的java代碼:
// MyAIDLService.aidl
//生產(chǎn)的MyAIDLService.java類會使用這個包名
package com.server.service.aidl;
//導入數(shù)據(jù)javabean類售淡,注意包名不要錯
import com.server.service.data.ServiceData;
// Declare any non-default types here with import statements
interface MyAIDLService {
/**
* 除了基本數(shù)據(jù)類型汤纸,其他類型的參數(shù)都需要標上方向類型:in(輸入), out(輸出), inout(輸入輸出)
*/
void addData(in ServiceData data);
List<ServiceData> getDataList();
}
主要里面使用到的java數(shù)據(jù)類抖部,這個累必須實現(xiàn)Parcelable接口俯萎,首先看一下用aidl文件聲明一下這個java類:
// 實體類包名撇眯,要跟實際的ServiceData.java包名一致
package com.server.service.data;
//實現(xiàn)了Parcelable序列化接口的實體類
parcelable ServiceData;
實現(xiàn)Parcelable接口的java數(shù)據(jù)類:
package com.server.service.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/**
* Created by dawish on 2017/8/22.
*/
public class ServiceData implements Parcelable {
public String id;
public String name;
public String price;
public String type;
public ServiceData( ) {
}
/**
* 讀數(shù)據(jù)恢復
* 系統(tǒng)自動添加,給createFromParcel里面用
* @param in
*/
protected ServiceData(Parcel in) {
id = in.readString();
name = in.readString();
price = in.readString();
type = in.readString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
/**
* IDE自動生成
* 讀取接口梆砸,目的是要從Parcel中構(gòu)造一個實現(xiàn)了Parcelable的類的實例處理。
* 因為實現(xiàn)類在這里還是不可知的,所以需要用到模板的方式杨耙,繼承類名通過模板
* 參數(shù)傳入。
* 為了能夠?qū)崿F(xiàn)模板參數(shù)的傳入,這里定義Creator嵌入接口,內(nèi)含兩個接口函數(shù)
* 分別返回單個和多個繼承類實例。
*/
public static final Creator<ServiceData> CREATOR = new Creator<ServiceData>() {
@Override
public ServiceData createFromParcel(Parcel in) {
return new ServiceData(in);
}
@Override
public ServiceData[] newArray(int size) {
Log.i("danxx", "newArray size--->"+size);
return new ServiceData[size];
}
};
/**
* 內(nèi)容描述接口彭则,基本不用管,IDE自動生成
*/
@Override
public int describeContents() {
return 0;
}
/**
* 寫入接口函數(shù)芬萍,打包,IDE自動生成
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeString(price);
dest.writeString(type);
}
}
相關(guān)的解釋已經(jīng)在代碼中了漫蛔,我這里就不解釋了毯盈。
上面的兩個aidl文件和一個java數(shù)據(jù)類是Server端和Client端共有的 脑奠,上面寫好的aidl文件和用到的java數(shù)據(jù)類都復制一份在Server端和Client端秒咨,這些是進程通信的鏈接器陡厘。
接下來就是便攜Server端的Service和Client的服務(wù)連接和請求代碼了:
3.2.1 Server端代碼編寫
public class AidlService extends Service {
public static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return remoteBinder;
}
/**
* 本地service用法 揉抵;
*/
/***/
private List<ServiceData> mData = new ArrayList<>();
/**
* 遠程service需要使用AIDL來通訊戏罢,其實也是基于Binder所意,只是Google規(guī)定了寫法
*/
MyAIDLService.Stub remoteBinder = new MyAIDLService.Stub() {
@Override
public void addData(ServiceData data) throws RemoteException {
mData.add(data);
}
@Override
public List<ServiceData> getDataList() throws RemoteException {
return mData;
}
};
}
Server的service需要在當前項目中的AndroidManifest.xml文件中聲明一下:
<service android:name=".service.binder.BinderService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.danxx.binderService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
需要提供intent-filter舶治,這樣方便遠程的Client端連接我們的服務(wù)。
3.2.2 Client端代碼編寫
Client需要注意就是遠程的Server端的Service進程是有可能被殺死的承绸,我們需要在Client端監(jiān)聽遠程的Binder是不是被殺死了界轩,使用DeathRecipient,遠程的Binder被殺死會被回調(diào):
//遠程服務(wù)連接成功后監(jiān)聽遠程的Binder死亡回調(diào)
new ServiceConnection() {
/**
* service連接成功后的回調(diào)
* @param name
* @param service 通訊接口
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
showMessage("-----遠程服務(wù)鏈接成功-----", R.color.praised_num);
isBound = true;
myAIDLService = MyAIDLService.Stub.asInterface(service); //遠程service寫法
try {
//設(shè)置死亡代理
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 監(jiān)聽Binder是否死亡
*/
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (myAIDLService == null) {
return;
}
//死亡后解除綁定
myAIDLService.asBinder().unlinkToDeath(mDeathRecipient, 0);
myAIDLService = null;
//重新綁定
doBindService();
showMessage("--DeathRecipient后重鏈遠程服務(wù)--", R.color.red_wine);
}
};
Client的完整代碼:
public class ActivityAidlClient extends AppCompatActivity {
@BindView(R.id.bind_service)
public Button bindService;
@BindView(R.id.unbind_service)
public Button unbindService;
@BindView(R.id.addData)
public Button addData;
@BindView(R.id.getData)
public Button getData;
@BindView(R.id.txt)
TextView tvInfo;
@BindView(R.id.scrollView)
ScrollView scrollView;
/**服務(wù)是否連接*/
public boolean isBound = false;
/**遠程service使用*/
public MyAIDLService myAIDLService;
private SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
/**
* ServiceConnection is Interface for monitoring the state of an application service
* ServiceConnection是一個觀察程序service的回調(diào)接口
*/
ServiceConnection serviceConnection = new ServiceConnection() {
/**
* service連接成功后的回調(diào)
* @param name
* @param service 通訊接口
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
showMessage("-----遠程服務(wù)鏈接成功-----", R.color.praised_num);
isBound = true;
myAIDLService = MyAIDLService.Stub.asInterface(service); //遠程service寫法
try {
//設(shè)置死亡代理
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
myAIDLService = null;
showMessage("-----遠程服務(wù)已斷開連接-----", R.color.red_deep);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl_client);
ButterKnife.bind(ActivityAidlClient.this);
showMessage("-----遠程服務(wù)等待連接-----", R.color.green_pure);
}
/**
* Service 的兩種啟動方法和區(qū)別
Service的生命周期方法onCreate, onStart, onDestroy
有兩種方式啟動一個Service,他們對Service生命周期的影響是不一樣的祖搓。
1 通過startService
Service會經(jīng)歷 onCreate -> onStart
stopService的時候直接onDestroy
如果是調(diào)用者自己直接退出而沒有調(diào)用stopService的話,Service會一直在后臺運行摘仅。下次調(diào)用者再起來可以stopService掏击。
2 通過bindService
Service只會運行onCreate, 這個時候服務(wù)的調(diào)用者和服務(wù)綁定在一起
調(diào)用者退出了,Srevice就會調(diào)用onUnbind->onDestroyed所謂綁定在一起就共存亡了亚侠。并且這種方式還可以使得
*
*/
@OnClick({R.id.bind_service, R.id.unbind_service,R.id.addData,R.id.getData})
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
if (!isBound) {
//用Intent匹配的方式綁定service
doBindService();
}
break;
case R.id.unbind_service:
if(isBound && myAIDLService != null && serviceConnection!=null) {
unbindService(serviceConnection);
isBound = false;
showMessage("-----主動斷開遠程連接-----", R.color.red_deep);
}
break;
case R.id.addData:
addData();
break;
case R.id.getData:
getData();
break;
default:
break;
}
}
private void addData(){
if(isBound){
ServiceData serviceData = new ServiceData();
try {
int size = myAIDLService.getDataList().size();
serviceData.setName("no-"+size);
serviceData.setId(String.valueOf(size));
serviceData.setPrice(String.valueOf(size+2));
serviceData.setType(String.valueOf(size%8));
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Name:"+serviceData.getName());
stringBuffer.append(" Id:"+serviceData.getId());
stringBuffer.append(" Type:"+serviceData.getType());
stringBuffer.append(" Price:"+serviceData.getPrice());
showMessage(stringBuffer.toString(), R.color.blue_color);
myAIDLService.addData(serviceData);
} catch (RemoteException e) {
e.printStackTrace();
}
}else {
Toast.makeText(this,"服務(wù)還未連接!", Toast.LENGTH_SHORT).show();
}
}
private void getData(){
if(isBound){
try {
int size = myAIDLService.getDataList().size();
// tvInfo.setText("Data size: "+ size);
showMessage("Data size: "+ size, R.color.blue_color);
} catch (RemoteException e) {
e.printStackTrace();
}
}else {
Toast.makeText(this,"服務(wù)還未連接颗管!", Toast.LENGTH_SHORT).show();
}
}
/**
* 顯示文字
*
* @param info 提示信息
*/
private void showMessage(final String info, final int color) {
tvInfo.post(new Runnable() {
@Override
public void run() {
int startPos = stringBuilder.length();
stringBuilder.append("\n"+info);
tvInfo.setText(DisplayUtil.changeTextColor(ActivityAidlClient.this, stringBuilder, color, startPos));
}
});
tvInfo.postDelayed(new Runnable() {
@Override
public void run() {
if(null != scrollView) scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
},100);
}
protected void showToast(String msg){
Toast.makeText(ActivityAidlClient.this, msg,Toast.LENGTH_SHORT).show();
}
//客戶端使用死亡代理姨涡,可以重啟service
//http://blog.csdn.net/liuyi1207164339/article/details/51706585
//服務(wù)端使用死亡回調(diào)回收數(shù)據(jù)
//http://www.cnblogs.com/punkisnotdead/p/5158016.html
//死亡通知原理分析
//http://light3moon.com/2015/01/28/Android%20Binder%20%E5%88%86%E6%9E%90%E2%80%94%E2%80%94%E6%AD%BB%E4%BA%A1%E9%80%9A%E7%9F%A5[DeathRecipient]/
/**
* 監(jiān)聽Binder是否死亡
*/
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (myAIDLService == null) {
return;
}
//死亡后解除綁定
myAIDLService.asBinder().unlinkToDeath(mDeathRecipient, 0);
myAIDLService = null;
//重新綁定
doBindService();
showMessage("--DeathRecipient后重鏈遠程服務(wù)--", R.color.red_wine);
}
};
private void doBindService(){
showMessage("-----開始鏈接遠程服務(wù)-----", R.color.light_yellow);
Intent intent = new Intent();
intent.setAction("com.danxx.aidlService");
intent.setPackage("com.server");
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
}
對應的XML文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#f2f2f2"
android:layout_margin="8dp">
<TextView
android:id="@+id/txt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="22sp"
android:gravity="left"
android:padding="8dp"/>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<Button
android:id="@+id/bind_service"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:background="@drawable/btn_selector_done"
android:layout_marginRight="4dp"
android:text="Bind Service" />
<Button
android:id="@+id/unbind_service"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/btn_selector_done"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:layout_marginLeft="4dp"
android:text="Unbind Service" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<Button
android:id="@+id/addData"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:background="@drawable/btn_selector_done"
android:layout_marginRight="4dp"
android:text="add data" />
<Button
android:id="@+id/getData"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:background="@drawable/btn_selector_done"
android:textAllCaps="false"
android:layout_marginLeft="4dp"
android:text="get data" />
</LinearLayout>
</LinearLayout>
四距潘、AIDL生成的Java代碼分析
代碼中的幾個方法:
1稽犁、DESCRIPTION
Binderd的唯一標識震鹉,一般用當前的類名表示。
2、asInterface(android.os.IBinder obj)
用于將服務(wù)端的Binder對象轉(zhuǎn)換為客戶端需要的AIDL接口類型的對象忽妒,轉(zhuǎn)換區(qū)分進程决侈,客戶端服務(wù)端位于同一進程,返回服務(wù)端的 //Stub對象本身;否則返回的是系統(tǒng)的封裝后的Stub.proxy對象。
3痰娱、asBInder
返回Binder對象
4、onTransact
此方法運行在服務(wù)端中的Binder線程池中晴股,當客戶端發(fā)起跨進程請求時怎诫,遠程請求會通過系統(tǒng)底層封裝后交由此方法處理。
5狗唉、Proxy#add
此 方法運行在客戶端东亦,當客戶端遠程調(diào)用此方法時,它的內(nèi)部實現(xiàn)是這樣的:首先創(chuàng)建該方法所需要的輸入型Parcel對象_data座菠、輸出型Parcel對象 _reple和返回值對象_result,然后將該方法的參數(shù)信息寫入_data中;接著調(diào)用transact方法來發(fā)RPC請求,同時當前線程掛起额嘿;然 后服務(wù)端的onTransact方法會被調(diào)用帐我,直到RPC過程返回后,當前線程繼續(xù)執(zhí)行日戈,并從_reply中取出RPC過程返回的結(jié)果,寫入 _result中酌住。
AIDL文件生成java文件詳細解釋:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\GitHubCode\\AndReservoir\\Server\\src\\main\\aidl\\com\\server\\service\\aidl\\MyAIDLService.aidl
*/
package com.server.service.aidl;
// Declare any non-default types here with import statements
//為了讓大家看的更清楚 我把生成的binder代碼 給拷貝到另外一個工程下面了逞带,并且用ide 給他format
public interface MyAIDLService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* 并且這個接口里 有一個靜態(tài)的抽象類Stub(注意這個名字是固定的 永遠都是Stub 不會是其他)
* 并且這個Stub是Binder的子類簿废,并且實現(xiàn)了 MyAIDLService 這個接口
* 這是Server服務(wù)端真是對象迂烁,具備真正的功能。
*/
public static abstract class Stub extends android.os.Binder implements com.server.service.aidl.MyAIDLService {
/**
* 這個東西就是唯一的binder標示 可以看到就是IPersonManager的全路徑名
* SM是DNS Binder驅(qū)動是路由器塞关,這個就是類似一個ip地址。
*/
private static final java.lang.String DESCRIPTOR = "com.server.service.aidl.MyAIDLService";
/**
* Construct the stub at attach it to the interface.
* 這個就是Stub的構(gòu)造方法,供服務(wù)端調(diào)用憋他,創(chuàng)建一個Binder類
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.server.service.aidl.MyAIDLService interface,
* generating a proxy if needed.
*這個方法 其實就做了一件事,如果是同一個進程,那么就返回Stub對象本身
* 如果不是同一個進程瓤鼻,就返回Stub.Proxy這個代理對象了
*/
public static com.server.service.aidl.MyAIDLService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//如果是同1個進程沃粗,也就是說進程內(nèi)通信的話 我們就返回括號內(nèi)里的對象
if (((iin != null) && (iin instanceof com.server.service.aidl.MyAIDLService))) {
return ((com.server.service.aidl.MyAIDLService) iin);
}
//如果不是同一進程疤祭,是2個進程之間相互通信悲柱,那我們就得返回這個Stub.Proxy 看上去叫Stub 代理的對象了
return new com.server.service.aidl.MyAIDLService.Stub.Proxy(obj);
}
//返回當前對象 服務(wù)端調(diào)用
@Override
public android.os.IBinder asBinder() {
return this;
}
//只有在多進程通信的時候 才會調(diào)用這個方法 ,同一個進程是不會調(diào)用的砸逊。
//首先 我們要明白 這個方法 一般情況下 都是返回true的崖叫,也只有返回true的時候才有意義,如果返回false了 就代表這個方法執(zhí)行失敗淆攻,
//所以我們通常是用這個方法來做權(quán)限認證的蝉娜,其實也很好理解狐榔,既然是多進程通信楣颠,那么我們服務(wù)端的進程當然不希望誰都能過來調(diào)用
//所以權(quán)限認證是必須的侧馅,關(guān)于權(quán)限認證的代碼 以后我再講 先略過攀例。
//除此之外 ,onTransact 這個方法 就是運行在Binder線程池中的蔚舀,一般就是客戶端發(fā)起請求缅叠,然后android底層代碼把這個客戶端發(fā)起的
//請求 封裝成3個參數(shù) 來調(diào)用這個onTransact方法悯森,第一個參數(shù)code 就代表客戶端想要調(diào)用服務(wù)端 方法的 標志位。
//其實也很好理解 服務(wù)端可能有n個方法 每個方法 都有一個對應的int值來代表朋贬,這個code就是這個int值削解,用來標示客戶端想調(diào)用的服務(wù)端的方法
//data就是方法參數(shù)唉铜,reply就是方法返回值讼撒。都很好理解
//其實隱藏了很重要的一點丽惭,這個方法既然是運行在binder線程池中的担映,所以在這個方法里面調(diào)用的服務(wù)器方法也是運行在Binder線程池中的朋魔,
//所以我們要記得 如果你的服務(wù)端程序 有可能和多個客戶端相聯(lián)的話,你方法里使用的那些參數(shù) 必須要是支持異步的移国,否則的話
//值就會錯亂了!這點一定要記孜镅印!結(jié)論就是Binder方法 一定要是同步方法<叶帷A渚洹!!H瞎臁杆烁!
/**
*@param code 唯一標識,客戶端傳遞標識執(zhí)行服務(wù)端代碼
*@param data 客戶端傳遞過來的參數(shù)
*@param reply 服務(wù)器返回回去的值
*@param flags 是否有返回值 0:有 1:沒有
*@return
*@throws 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_addData: { //客服端請求區(qū)分1
data.enforceInterface(DESCRIPTOR);
com.server.service.data.ServiceData _arg0;
if ((0 != data.readInt())) {
//讀取客戶端傳遞過來的參數(shù)
_arg0 = com.server.service.data.ServiceData.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addData(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getDataList: { //客服端請求區(qū)分2
data.enforceInterface(DESCRIPTOR);
java.util.List<com.server.service.data.ServiceData> _result = this.getDataList();
reply.writeNoException();
//想客戶端寫入?yún)?shù)
reply.writeTypedList(_result);
return true;
}
}
//利用Binder驅(qū)動發(fā)送數(shù)據(jù)
return super.onTransact(code, data, reply, flags);
}
//注意這里的Proxy 這個類名也是不變的沪饺,從前文我們知道 只有在多進程通信的情況下 才會返回這個代理的對象
//這個代理并不是Binder的子類蛾扇,只是實現(xiàn)了MyAIDLService接口的一個影子更哄,客戶端通過操作這個影子來實現(xiàn)對服務(wù)端的通信
//這個代理端只是告訴客戶端 服務(wù)端擁有什么樣的功能可以提供麻敌。正真完整通信實現(xiàn)數(shù)據(jù)交換的還是服務(wù)端的Binder子類
private static class Proxy implements com.server.service.aidl.MyAIDLService {
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ù)類型玩讳,其他類型的參數(shù)都需要標上方向類型:in(輸入), out(輸出), inout(輸入輸出)
* 客戶端調(diào)用
*/
@Override
public void addData(com.server.service.data.ServiceData data) 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 ((data != null)) {
_data.writeInt(1);
//寫入請求參數(shù)
data.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//想服務(wù)端發(fā)送請求
mRemote.transact(Stub.TRANSACTION_addData, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
/**
* 客戶端調(diào)用
* @return
* @throws android.os.RemoteException
*/
@Override
public java.util.List<com.server.service.data.ServiceData> getDataList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.server.service.data.ServiceData> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getDataList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.server.service.data.ServiceData.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//客服端請求區(qū)分1
static final int TRANSACTION_addData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
//客服端請求區(qū)分2
static final int TRANSACTION_getDataList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* 除了基本數(shù)據(jù)類型蝌戒,其他類型的參數(shù)都需要標上方向類型:in(輸入), out(輸出), inout(輸入輸出)
*/
public void addData(com.server.service.data.ServiceData data) throws android.os.RemoteException;
public java.util.List<com.server.service.data.ServiceData> getDataList() throws android.os.RemoteException;
}
五、測試
我們在把Server端app和Client端app都安裝啟動并在Client操作數(shù)據(jù)后沼琉,打開ADM:
選中遠程的server進程北苟,點擊紅色的進程停止stop按鈕,看看我們的死亡遠程Binder死亡監(jiān)聽重連是否有效:
詳細代碼請參考:
https://github.com/Dawish/AndReservoir
看這一篇真的還不夠:
后面我還會寫手動Client在Server端注冊監(jiān)聽回調(diào)接口打瘪,這樣在服務(wù)端支持多線程的情況下每一個客戶端都可以知道數(shù)據(jù)在變化以及變化后的結(jié)果友鼻。
手動首先Binder機制通信,不依靠AIDL文件來實現(xiàn)闺骚,雖然這里我已經(jīng)把AIDL實現(xiàn)IPC說得很清楚了彩扔,手動實現(xiàn)Binder無非就是吧aidl文件生產(chǎn)的java代碼文件手動實現(xiàn)一遍,按照C/S和代理模式來操作就可以了僻爽。也是難事虫碉!
參考: