前言
最近在看AMS中的源碼,發現原理是AIDL進行IPC通訊,特此將基礎回顧下,編寫了一個簡單的DEMO,方便理解。
客戶端開啓和關閉遠程服務
創建兩個App
- ServiceApp:包含遠程服務
- ClientApp:調用遠程服務
ServiceApp
- 創建RemoteAppService服務
public class RemoteAppService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("RemoteAppService onCreate....");
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("RemoteAppService onDestroy....");
}
}
- 註冊服務
<service
android:name="com.martin.lib.ipc.remote.RemoteAppService"
android:enabled="true"
android:exported="true"
/>
ClientApp
- 調用ServiceApp的RemoteAppService服務
/**
* 包名爲應用程序的報名
*/
private static final String PKG = "com.optimize.lib.ipc";
/**
* 需要啓動的服務的全路徑名稱
*/
private static final String CLS = "com.martin.lib.ipc.remote.RemoteAppService";
Intent intent = new Intent();
intent.setComponent(new ComponentName(PKG,CLS));
findViewById(R.id.open_btn).setOnClickListener(v -> {
startService(intent);
});
findViewById(R.id.close_btn).setOnClickListener(v -> {
stopService(intent);
});
遠程服務調用在5.0以後,需要使用顯示調用。
- 查看日誌
綁定跨進程服務
- bindService:綁定服務
- unbindService:解綁服務
ClientApp
findViewById(R.id.bindRemoteServiceBtn).setOnClickListener(v -> {
bindService(intent,this, Service.BIND_AUTO_CREATE);
});
findViewById(R.id.unBindRemoteServiceBtn).setOnClickListener(v -> {
unbindService(this);
});
- 查看日誌
AIDL
AIDL是Android中編寫服務向外暴露接口的一種描述語言,服務在外提供接口時需要使用AIDL將接口編寫,Android會自動將AIDL文件轉換爲Java文件,若客戶端需要進行接口調用,只需拷貝遠程的AIDL文件生成Java代碼後使用即可。
ServiceApp
- 定義IRemoteServiceBinder的AIDL文件
// IRemoteServiceBinder.aidl
package com.optimize.lib.ipc;
// Declare any non-default types here with import statements
// IRemoteServiceBinder.aidl
package com.optimize.lib.ipc;
// Declare any non-default types here with import statements
interface IRemoteServiceBinder {
/**
* 遠程登錄
*/
boolean login(String user,String password);
}
- 在使用Service中使用,定義的AIDL生成的Java類,並在onBind中進行回傳。
public class RemoteAppService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IRemoteServiceBinder.Stub() {
@Override
public boolean login(String data) throws RemoteException {
System.out.println("user:"+user+",password"+password);
}
};
}
}
ClientApp進行遠程調用
- 創建AIDL文件夾,將將遠程的AIDL文件進行復制到aidl文件夾中,注意文件名稱和報名路徑必須和ServiceApp完全相同。
- 代碼中進行賦值和調用
IRemoteServiceBinder binder = null;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//這裏不能做強制類型的轉換,因爲他們的內存地址是不一樣的,所以修改一下. binder = (IRemoteServiceBinder) service;
//正確寫法
binder = IRemoteServiceBinder.Stub.asInterface(service);
}
注意,雖然拷貝了相同的文件,但是他們在內存中的地址是不一樣的,所以不能進行強轉,此時我們需要IRemoteServiceBinder.Stub.asInterface(service);
方法來進行類型轉換。
- 調用
findViewById(R.id.syncDataBtn).setOnClickListener(v -> {
if (binder != null) {
try {
EditText userNameEdt = findViewById(R.id.userNameEdt);
EditText passwordEdt = findViewById(R.id.passwordEdt);
boolean isSuccess = binder.login(userNameEdt.getText().toString(),
passwordEdt.getText().toString());
if (isSuccess){
System.out.println("login success");
}else {
System.out.println("login failure");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
- 查看結果