Service的遠端通信AIDL
先解釋下什麼是AIDL,AIDL (Android Interface Definition Language)是一種IDL 語言,用于生成可以在Android裝置上兩個程序之間進行程序間通信(IPC)的代碼。如果在一個程序中(例如Activity)要調用另一個程序中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。
AIDL IPC機制是面向接口的、輕量級的。通過AIDL定義的接口可以實作伺服器端與用戶端的IPC通信。
使用AIDL實作IPC(程序間通訊)
說明:
以下代碼我用的是最新版的SDK,android6.0 api
建立.aidl檔案
// IAppServiceRemoteBinder.aidl
package me.dengfengdecao.android.servicedemo;
// Declare any non-default types here with import statements
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);
void syncData(String data);
}
其中 syncData方法用來和用戶端同步資料,測試用的。
實作AIDL接口
在RemoteService中的onBind方法中傳回aidl接口中Stub内部抽象類的實作,這裡使用了匿名對象:
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return new IAppServiceRemoteBinder.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
Log.d(TAG, "basicTypes: ");
}
// data:從用戶端同步過來的資料
@Override
public void syncData(String data) throws RemoteException {
Log.d(TAG, "syncData: 要同步的資料:" + data);
defaultdata += data;
}
};
}
檢視自定義的aidl接口可知Stub為aidl的抽象内部類,并且它繼承自Binder類和實作我們自定義的aidl接口。
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements me.dengfengdecao.android.servicedemo.IAppServiceRemoteBinder
這個内部抽象類是必須要實作的。
注意:
服務端AIDL的任何修改都必須的同步到所有的用戶端,否則用戶端調用服務端得接口可能會導緻程式異常(因為此時用戶端此時可能會調用到服務端已不再支援的接口)。
這樣,onBind方法就傳回了一個Stub類的對象,該對象為service提供了RPC接口。該對象會被傳遞到實作了ServiceConnection接口的類的public void onServiceConnected(ComponentName name, IBinder service)方法中。
實作ADIL接口時需要注意一下幾點:
- 不能保證所有對aidl接口的調用都在主線程中執行,是以必須考慮多線程調用的情況,也就是必須考慮線程安全。
- 預設IPC調用是同步的。如果已知IPC服務端會花費很多毫秒才能完成,那就不要在Activity或View線程中調用,否則會引起應用程式挂起(Android可能會顯示“應用程式未響應”對話框),可以試着在獨立的線程中調用。
- 不會将異常傳回給調用方
下一步中該對象将會向用戶端公開,這樣用戶端就可以通過該對象與該service進行互動了。
向用戶端暴露接口
在完成了接口的實作後需要向用戶端暴露接口了,也就是釋出服務,實作的方法是繼承 Service,然後實作以Service.onBind(Intent)傳回一個實作了接口的類對象。
下面的代碼片斷表示了暴露IRemoteService接口給用戶端的方式。
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return new IAppServiceRemoteBinder.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
Log.d(TAG, "basicTypes: ");
}
// data:從用戶端同步過來的資料
@Override
public void syncData(String data) throws RemoteException {
Log.d(TAG, "syncData: 要同步的資料:" + data);
defaultdata += data;
}
};
}
即在繼承了Service的類中的onBind方法中傳回aidl接口的Binder對象。
然後在manifest中注冊遠端服務:
<service
android:name=".RemoteService"
android:process=":remote"
>
<intent-filter>
<action android:name="me.dengfengdecao.action.REMOTE_SERVICE"/>
</intent-filter>
</service>
android:process=":remote",指定建立一個遠端服務在新的程序中。
注意,最後要将遠端服務端的aidl copy到用戶端,包括完整的包名。
現在,如果用戶端(ClientActivity)調用bindService()來連接配接該服務端(RemoteService) ,用戶端的onServiceConnected()回調函數将會獲得從服務端(RemoteService )的onBind()傳回的Binder對象。
ClientActivity清單(為了易讀,删除了view代碼):
public class ClientActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
private static final String TAG = "ClientActivity";
// 遠端服務的AIDL
private IAppServiceRemoteBinder mRemoteBinder;
private Intent mIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
// 隐式intent啟動遠端服務
mIntent = new Intent();
mIntent.setAction("me.dengfengdecao.action.REMOTE_SERVICE");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
// 接收從服務端傳遞過來的IBinder對象
mRemoteBinder = IAppServiceRemoteBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
}
ok,現在啟動ClientActivity如下:
綁定到遠端服務,日志如下:
可以看到,從遠端服務那裡接收到了IBinder對象。
切換到遠端端,日志如下:
輸入要同步到遠端端的資料,日志如下:
切換回ClientActivity 解綁服務,日志如下:
可以看到,和遠端服務斷開了關聯,切換到系統正在運作的app:
可以确定,确實和遠端服務斷開了連接配接。
如果對遠端傳輸對象感興趣請看官方文檔:
http://developer.android.com/intl/zh-cn/guide/components/aidl.html#PassingObjects
還可以和郭霖前輩的這篇博文一起學習:
http://blog.csdn.net/guolin_blog/article/details/9797169
OK,如果有任何問題請指出,謝謝!
部分内容參考:
http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html