AIDL(Android Interface Definition Language) 即Android 接口定義語言,是用來實現不同進程間通信的。AIDL同時也是另外兩種進程通信方式Messager和ContentProvider的底層實現方法,所以瞭解aidl的使用顯得尤爲重要。
本案例可在 Github 獲取到Demo源碼。
操作步驟
AIDL通信的實現是需要客戶端和服務端的配合,具體的實現過程如下:
創建aidl文件
aidl文件是客戶端請求服務端操作獲取交互信息的基礎,只需要在Android Studio的src/main目錄下創建一個aidl目錄(或者也可以在gradle中指定aidl位置),然後在該目錄下創建一個aidl文件,android studio會自動生成一個包名並將aidl放在該包下。
// ITestInterface.aidl
package com.kubo.aidlproject;
import com.kubo.aidlproject.TestData;
// Declare any non-default types here with import statements
//設置客戶端調用的接口
interface ITestInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void sendTest(in TestData testData);
void sendTest2(out TestData testData,int a);
void sendTest3(inout TestData testData,String a);
}
如上所示,aidl支持如下如下數據傳遞:
- Java所有的基本數據類型(int、long、short等)
- String和CharSequence
- List 子項中也必須是aidl支持的類型
- Map 子項中也必須是aidl支持的類型
- AIDL aidl本省也可作爲傳遞的類型
- Parcelable 所有實現Parcelable接口的對象
AIDL中除了基本類型外其他類型需要設置數據流的方向(in、out,inout):
- in
in 代表爲輸入流方向,即client可修改,service端修改無效 - out
out 代表輸出流方向,即service端可修改,client修改無效 - inout
inout代表雙向流,即service和client端均可對其進行修改
如上,創建的aidl需要傳遞的參數TestData:
package com.kubo.aidlproject;
import android.os.Parcel;
import android.os.Parcelable;
/**
*
* 測試數據序列化
* @author hfcai
*/
public class TestData implements Parcelable {
private int id;
private String name;
@Override
public String toString() {
return "TestData{" +
"id=" + id +
", name='" + name + '\'' +
", tips='" + tips + '\'' +
", can=" + can +
'}';
}
private String tips;
private boolean can;
public TestData(){}
public TestData(int id, String name, String tips, boolean can) {
this.id = id;
this.name = name;
this.tips = tips;
this.can = can;
}
protected TestData(Parcel in) {
id = in.readInt();
name = in.readString();
tips = in.readString();
can = in.readByte()!=0;
}
public void readFromParcel(Parcel in) {
id = in.readInt();
name = in.readString();
tips = in.readString();
can = in.readByte()!=0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeString(tips);
dest.writeByte((byte) (can?1:0));
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<TestData> CREATOR = new Creator<TestData>() {
@Override
public TestData createFromParcel(Parcel in) {
return new TestData(in);
}
@Override
public TestData[] newArray(int size) {
return new TestData[size];
}
};
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isCan() {
return can;
}
public void setCan(boolean can) {
this.can = can;
}
public String getTips() {
return tips;
}
public void setTips(String tips) {
this.tips = tips;
}
}
同時還需要在aidl中聲明一個parcelable對象:
//TestData .aidl
package com.kubo.aidlproject;
// Declare any non-default types here with import statements
parcelable TestData;
創建完aidl後,android sdk工具可將其自動轉化爲對應的繼承IInterface的接口,其實也就是生成一個binder的過程,如上aidl對應的接口如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.kubo.aidlproject;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
public interface ITestInterface extends IInterface {
void sendTest(TestData var1) throws RemoteException;
void sendTest2(TestData var1, int var2) throws RemoteException;
void sendTest3(TestData var1, String var2) throws RemoteException;
public abstract static class Stub extends Binder implements ITestInterface {
private static final String DESCRIPTOR = "com.kubo.aidlproject.ITestInterface";
static final int TRANSACTION_sendTest = 1;
static final int TRANSACTION_sendTest2 = 2;
static final int TRANSACTION_sendTest3 = 3;
public Stub() {
this.attachInterface(this, "com.kubo.aidlproject.ITestInterface");
}
public static ITestInterface asInterface(IBinder obj) {
if (obj == null) {
return null;
} else {
IInterface iin = obj.queryLocalInterface("com.kubo.aidlproject.ITestInterface");
return (ITestInterface)(iin != null && iin instanceof ITestInterface ? (ITestInterface)iin : new ITestInterface.Stub.Proxy(obj));
}
}
public IBinder asBinder() {
return this;
}
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String descriptor = "com.kubo.aidlproject.ITestInterface";
TestData _arg0;
switch(code) {
case 1:
data.enforceInterface(descriptor);
if (0 != data.readInt()) {
_arg0 = (TestData)TestData.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.sendTest(_arg0);
reply.writeNoException();
return true;
case 2:
data.enforceInterface(descriptor);
_arg0 = new TestData();
int _arg1 = data.readInt();
this.sendTest2(_arg0, _arg1);
reply.writeNoException();
if (_arg0 != null) {
reply.writeInt(1);
_arg0.writeToParcel(reply, 1);
} else {
reply.writeInt(0);
}
return true;
case 3:
data.enforceInterface(descriptor);
if (0 != data.readInt()) {
_arg0 = (TestData)TestData.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
String _arg1 = data.readString();
this.sendTest3(_arg0, _arg1);
reply.writeNoException();
if (_arg0 != null) {
reply.writeInt(1);
_arg0.writeToParcel(reply, 1);
} else {
reply.writeInt(0);
}
return true;
case 1598968902:
reply.writeString(descriptor);
return true;
default:
return super.onTransact(code, data, reply, flags);
}
}
private static class Proxy implements ITestInterface {
private IBinder mRemote;
Proxy(IBinder remote) {
this.mRemote = remote;
}
public IBinder asBinder() {
return this.mRemote;
}
public String getInterfaceDescriptor() {
return "com.kubo.aidlproject.ITestInterface";
}
public void sendTest(TestData testData) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken("com.kubo.aidlproject.ITestInterface");
if (testData != null) {
_data.writeInt(1);
testData.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
this.mRemote.transact(1, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public void sendTest2(TestData testData, int a) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken("com.kubo.aidlproject.ITestInterface");
_data.writeInt(a);
this.mRemote.transact(2, _data, _reply, 0);
_reply.readException();
if (0 != _reply.readInt()) {
testData.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
public void sendTest3(TestData testData, String a) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken("com.kubo.aidlproject.ITestInterface");
if (testData != null) {
_data.writeInt(1);
testData.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
_data.writeString(a);
this.mRemote.transact(3, _data, _reply, 0);
_reply.readException();
if (0 != _reply.readInt()) {
testData.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
}
}
}
創建服務
如上完成了aidl的創建後還需要創建提供服務的服務類,從而實現提供服務的目的,如下:
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
ITestAidlStub stub = new ITestAidlStub();
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG,"-----------創建AIDL服務-------------");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"----------------onStartCommand-----------------");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e(TAG,"----------------------onDestroy---------------");
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG,"----------------------onBind---------------");
return stub;
}
}
public class ITestAidlStub extends ITestInterface.Stub {
private static final String TAG = "ITestAidlStub";
@Override
public void sendTest(TestData testData) throws RemoteException {
Log.e(TAG,"sendTest,testData is in:"+testData.toString());
testData.setName("test_service");
}
@Override
public void sendTest2(TestData testData, int a) throws RemoteException {
Log.e(TAG,"sendTest,testData is out:"+testData.toString());
testData.setName("test_service");
}
@Override
public void sendTest3(TestData testData, String a) throws RemoteException {
Log.e(TAG,"sendTest,testData is inout:"+testData.toString());
testData.setName("test_service");
}
}
完成服務的編寫還得需要在manifest中註冊該服務:
<service android:name=".AIDLService"
android:exported="true"
android:process=":test"/>
由於本次是爲了測試多進程通信,且是在同一個app中,所以就需要我們爲這個服務單獨開一個進程
請求服務
服務創建好後,我們就可以在我們app的主進程中創建一個服務連接並通過bindService綁定服務了。
public class MainActivity extends AppCompatActivity {
ITestInterface iTestInterface;
/**
* 連接服務
*/
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iTestInterface = ITestAidlStub.asInterface(service);
Log.e("MainActivity","onServiceConnected");
}
/**
* service異常終止
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
iTestInterface = null;
Log.e("MainActivity","onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view){
TestData testData = new TestData(0,"hfcai","注意提示!",true);
switch (view.getId()){
case R.id.bind:
//綁定服務
Intent intent = new Intent(this,AIDLService.class);
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
break;
case R.id.send_in:
//發送測試數據
if (iTestInterface!=null){
try {
iTestInterface.sendTest(testData);
Log.e("in","testData:"+testData.toString());
} catch (RemoteException e) {
e.printStackTrace();
Log.e("in error",e.getMessage());
}
}else {
Toast.makeText(this,"連接服務失敗!",Toast.LENGTH_SHORT).show();
}
break;
case R.id.send_out:
//發送測試數據
if (iTestInterface!=null){
try {
iTestInterface.sendTest2(testData,0);
Log.e("out","testData:"+testData.toString());
} catch (RemoteException e) {
e.printStackTrace();
Log.e("out error",e.getMessage());
}
}else {
Toast.makeText(this,"連接服務失敗!",Toast.LENGTH_SHORT).show();
}
break;
case R.id.send_inout:
//發送測試數據
if (iTestInterface!=null){
try {
iTestInterface.sendTest3(testData,"inout");
Log.e("inout","testData:"+testData.toString());
} catch (RemoteException e) {
e.printStackTrace();
Log.e("inout error",e.getMessage());
}
}else {
Toast.makeText(this,"連接服務失敗!",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
這樣就完成了一個簡單的Demo實現。