Android——AIDL基礎實現demo以及原理探究
阿新 • • 發佈:2019-01-30
最近有一段時間沒寫部落格了,打算最近趁著有時間補補,本文是總結的AIDL的基本使用和原理。
分為兩個部分:一是簡單的上手demo,二是對程式碼邏輯進行分析。
一:簡單的AIDL小Demo:
服務端:
1.新建服務端工程AIDLserver,新建AIDL檔案:
2.開啟該檔案,編寫一個測試方法:
3.點選build-rebuild進行編譯,可以看到生成的java類檔案,在最後一行可以看到自己剛才寫的測試方法input():
4.編寫服務端的服務類:
這裡要注意一定要返回Ibinder例項。 5.在Manifest裡配置該服務,在Activity中啟動服務並執行程式:
啟動服務
客戶端: 1.新建客戶端工程AIDLClient,新建一個module工程mylibrary:
2.把之前服務端的aidl資料夾複製到客戶端相同位置,並rebuild客戶端:
3.在MainActivity中繫結服務端的TESTServer服務,並通過ServiceConnection進行訊息回撥,在其的onServiceConnected方法中得到IMyAidlInterface的例項。 4.之後建立一個按鈕的點選方法,在其中直接呼叫IMyAidlInterface例項物件發出客戶端的資訊並直接獲得服務端的回撥結果。 (3、4完整程式碼如下): public class MainActivity extends AppCompatActivity {
Button btn_test_aidl;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_test_aidl = (Button) findViewById(R.id.btn_test_aidl);
//繫結服務
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.aidlserver", "com.example.aidlserver.MyService" ));
bindService(intent, conn, BIND_AUTO_CREATE);
btn_test_aidl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
String rep = iMyAidlInterface.input("客戶端發出的資訊");
Log.i("nangua", "從服務端呼叫成功的結果:" + rep);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
IMyAidlInterface iMyAidlInterface;
/**
* 服務回撥方法
*/
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
//解綁服務,回收資源
unbindService(conn);
}
}
最後,點選客戶端測試按鈕,檢視雙端顯示的Log:
二.底層實現原理: 圍繞著IMyAidlInterface的程式碼(方便查詢):
從客戶端開始,一步步說: 1.首先在onCreate方法中,綁定了服務端的TestServer服務:
然後在我們的點選方法中:
所以此時我們看到服務端IMyAidlInterface例項的代理類Proxy的onTranscat方法:
此時,客戶端的執行方法終於收到了回傳的引數:
總結:
可以看到整個資料互動過程,就是客戶端通過繫結服務,得到服務端的Aidl例項mRemote,並通過呼叫該例項的input方法,並在input方法中最終呼叫程序間通訊的底層方法onTransact,實現資料從客戶端到服務端,再到客戶端的整個回撥。過程雖然比較複雜,但是思路是連貫的。
2.開啟該檔案,編寫一個測試方法:
3.點選build-rebuild進行編譯,可以看到生成的java類檔案,在最後一行可以看到自己剛才寫的測試方法input():
4.編寫服務端的服務類:
這裡要注意一定要返回Ibinder例項。 5.在Manifest裡配置該服務,在Activity中啟動服務並執行程式:
啟動服務
客戶端: 1.新建客戶端工程AIDLClient,新建一個module工程mylibrary:
2.把之前服務端的aidl資料夾複製到客戶端相同位置,並rebuild客戶端:
3.在MainActivity中繫結服務端的TESTServer服務,並通過ServiceConnection進行訊息回撥,在其的onServiceConnected方法中得到IMyAidlInterface的例項。 4.之後建立一個按鈕的點選方法,在其中直接呼叫IMyAidlInterface例項物件發出客戶端的資訊並直接獲得服務端的回撥結果。 (3、4完整程式碼如下): public class MainActivity extends AppCompatActivity {
Button btn_test_aidl;
@Override
protected void onCreate
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_test_aidl = (Button) findViewById(R.id.btn_test_aidl);
//繫結服務
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.aidlserver", "com.example.aidlserver.MyService"
bindService(intent, conn, BIND_AUTO_CREATE);
btn_test_aidl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
String rep = iMyAidlInterface.input("客戶端發出的資訊");
Log.i("nangua", "從服務端呼叫成功的結果:" + rep);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
IMyAidlInterface iMyAidlInterface;
/**
* 服務回撥方法
*/
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
//解綁服務,回收資源
unbindService(conn);
}
}
最後,點選客戶端測試按鈕,檢視雙端顯示的Log:
二.底層實現原理: 圍繞著IMyAidlInterface的程式碼(方便查詢):
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: E:\\Android-workspace\\AIDLsample\\AIDLClient\\app\\src\\main\\aidl\\com\\example\\aidlserver\\IMyAidlInterface.aidl
*/
package com.example.aidlserver;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidlserver.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.aidlserver.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidlserver.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidlserver.IMyAidlInterface))) {
return ((com.example.aidlserver.IMyAidlInterface)iin);
}
return new com.example.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_input:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.input(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.aidlserver.IMyAidlInterface
{
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;
}
/**
* 自動生成的方法
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String input(java.lang.String str) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(str);
mRemote.transact(Stub.TRANSACTION_input, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_input = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* 自動生成的方法
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.lang.String input(java.lang.String str) throws android.os.RemoteException;
}
從客戶端開始,一步步說: 1.首先在onCreate方法中,綁定了服務端的TestServer服務:
//繫結服務
Log.d("nangua","客戶端onCreate中繫結服務");
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.aidlserver", "com.example.aidlserver.TestService"));
bindService(intent, conn, BIND_AUTO_CREATE);
其中我們傳入的ServiceConnection為: /**
* 服務回撥方法
*/
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("nangua","客戶端回撥獲得iMyAidlInterface例項");
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
在onServiceConnected中,我們呼叫了IMyAidlInterface例項的Stub內部類的asInterface方法,我們走進該方法:
public static com.example.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidlserver.IMyAidlInterface))) {
return ((com.example.aidlserver.IMyAidlInterface)iin);
}
return new com.example.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
}
可以看到最後一行返回結果是呼叫了Stub內部類的代理內部類Proxy的構造方法,該方法為:
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
可以看到講傳入的遠端服務類的IBinder例項儲存到了本地,變數名為mRemote(意思是拿到的遠端例項).
到此為止,我們可以知道客戶端的IMyAidlInterface例項iMyAidlInterface就是該代理類例項mRemote。然後在我們的點選方法中:
btn_test_aidl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
Log.d("nangua","點選了");
String rep = iMyAidlInterface.input("客戶端發出的資訊");
Log.d("nangua", "從服務端呼叫成功的結果:" + rep);
} catch (Exception e) {
e.printStackTrace();
}
}
});
是呼叫了該例項的input方法,所以現在看到Proxy代理類的input方法:
@Override public java.lang.String input(java.lang.String str) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(str);
mRemote.transact(Stub.TRANSACTION_input, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
可以看到其中關鍵的一句:
mRemote.transact(Stub.TRANSACTION_input, _data, _reply, 0);
呼叫了mRemote例項的transact方法,其中TRANSACTION_input是標誌值。
該方法會呼叫服務端的onTransact方法!所以此時我們看到服務端IMyAidlInterface例項的代理類Proxy的onTranscat方法:
@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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_input:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.input(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
可以看到在case TRANSACTION_input:的情況下,
會得到客戶端傳來的資料:
java.lang.String _arg0;
_arg0 = data.readString();
然後執行服務端的input方法:
java.lang.String _result = this.input(_arg0);
最後將資料儲存:
reply.writeNoException();
reply.writeString(_result);
並返回資料:
return super.onTransact(code, data, reply, flags);
onTransact方法走的是底層程式碼,通過這個方法的實現,可以將服務端的資料回傳給客戶端。此時,客戶端的執行方法終於收到了回傳的引數:
String rep = iMyAidlInterface.input("客戶端發出的資訊");
到此,全過程結束。總結:
可以看到整個資料互動過程,就是客戶端通過繫結服務,得到服務端的Aidl例項mRemote,並通過呼叫該例項的input方法,並在input方法中最終呼叫程序間通訊的底層方法onTransact,實現資料從客戶端到服務端,再到客戶端的整個回撥。過程雖然比較複雜,但是思路是連貫的。