AIDL 安卓程序間通訊/跨應用通訊
前言
最近出去面試找工作,被人問到AIDL,我就回答這個東西我用過,也大概理解,Android的程序間通訊語言嘛,人家不置可否,那我能咋著呢,畢竟沒深入研究過,也沒辦法,咱只能回來奮發圖強了
寫在前面
我以前就看過的一個部落格,裡面原理程式碼什麼都有,寫的水平肯定比我高
Android開發者指南(6) —— AIDL
首先字面解釋
A=Android
IDL=Interface definition language
意譯就是android介面定義語言,馬丹,完全看不明白
算了,就是Android官方給我們定義出來跨程序,甚至跨應用通訊用的
開發平臺
Android Studio 2.2+Android手機一部
新建工程
這個就不說了,跳過
就是新建工程後再建一個module 也是android app,功能後面再說
aidl語法
這裡請去看我寫在前面,裡面比較詳細,或者自行baidu/google,我也瞭解的不多
程式碼示例
最關鍵的地方到了
其實就是新建一個aidl檔案
// IMyAidlInterface.aidl
package com.kikt.aidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void test(int sum,int sum2);
}
接著make project,生成下java程式碼
找到生成的程式碼看下
我靠 好複雜,還是渣格式,這裡格式化一下:
不想看完全程式碼的可以看下後面的結構圖
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: H:\\Git\\ASWorkSpace\\public\\AidlDemo\\app\\src\\main\\aidl\\com\\kikt\\aidl\\IMyAidlInterface.aidl
*/
package com.kikt.aidl;
// 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.kikt.aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.kikt.aidl.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.kikt.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.kikt.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.kikt.aidl.IMyAidlInterface))) {
return ((com.kikt.aidl.IMyAidlInterface) iin);
}
return new com.kikt.aidl.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_test: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.test(_arg0, _arg1);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.kikt.aidl.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;
}
@Override
public void test(int sum, int sum2) 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(sum);
_data.writeInt(sum2);
mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void test(int sum, int sum2) throws android.os.RemoteException;
}
這裡生成了一個靜態內部類Stub
方法簽名如下
public static abstract class Stub extends android.os.Binder implements com.kikt.aidl.IMyAidlInterface
這個類是抽象類 這裡類本身實現了對應的介面IMyAidlInterface,但沒有實現我們在aidl中定義的方法test(int,int)方法
這也代表了test方法需要我們自己來實現
另外方法繼承了android.os.Binder,看到這個的時候,我們就知道,這個應該用在什麼地方了
對了,和Service必須實現的那個介面onBind的返回引數一樣!
這裡寫個類繼承下
package com.kikt.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.kikt.aidl.IMyAidlInterface;
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return new AidlEntity();
}
public class AidlEntity extends IMyAidlInterface.Stub{
@Override
public void test(int sum, int sum2) throws RemoteException {
Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
}
}
}
為了方便,直接寫在service內部
這裡只是簡單的做一個日誌輸出,其他什麼也沒有幹
同應用呼叫
這裡是同應用呼叫
直接看核心程式碼
bindService(new Intent(this, MyService.class), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
try {
test.test(1, 2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
這裡就是一個按鈕點選後bindService,然後connection會接受到一個IBinder的例項,這個例項就是上面service的那個
當然還有一個寫法,直接強轉也不會報錯
bindService(new Intent(this, MyService.class), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
// try {
// test.test(1, 2);
// } catch (RemoteException e) {
// e.printStackTrace();
// }
MyService.AidlEntity service1 = (MyService.AidlEntity) service;
try {
service1.test(4,5);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
效果都是一樣的,那麼這個什麼aidl還有個毛用啊,強轉就好了
接著就是高潮了
跨應用呼叫
沒錯,我個人認為最重要的也就是這點了,aidl可以在不同應用中呼叫
比如我要做一個提供服務的應用,或者說是同公司的核心元件,而不希望別人知道我的具體實現
這時我直接丟給你我的aidl檔案,告訴你包名和服務名稱,你就去調就好了!
ComponentName componentName = new ComponentName(
"com.kikt.aidldemo", "com.kikt.aidldemo.MyService");
Intent intent = new Intent();
intent.setComponent(componentName);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
try {
iinterface.test(10, 20);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
這裡和上面的程式碼看上去差不多,但是我和原來的不是一個應用,而是第二個應用
這裡使用test方法時,可以看到提供服務的應用可以輸出日誌
而引申下
如果這裡有一個演算法,實現了兩個引數間的計算,那麼我們是不是可以得到計算結果呢,比如說一個加密方法,我不需要知道具體實現,只要呼叫就好了
aidl修改如下:
// IMyAidlInterface.aidl
package com.kikt.aidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void test(int sum,int sum2);
int add(int num,int num2);
}
內部類實現修改
public class AidlEntity extends IMyAidlInterface.Stub{
@Override
public void test(int sum, int sum2) throws RemoteException {
Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
}
@Override
public int add(int num, int num2) throws RemoteException {
return num + num2;
}
}
呼叫者應用的bindService中修改如下:
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
try {
iinterface.test(10, 20);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
int add = iinterface.add(1, 2);
Log.d("MainActivity", "add:" + add);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
在第二個中點選可以看到日誌輸出
思維發散
其實aidl就像一個核心,模組化開發的時候,核心模組不用給你程式碼,只要你去aidl檔案,和跨應用的呼叫方法即可,而你實現後續的業務邏輯,也算是一種解耦的方式吧
高階/原理
再回到Stub的實現中,看下onTransact方法就可以看出,其實aidl是實現自android提供的序列化
通過約定的方式,將方法名的引數和返回值序列化後再通過約定的方式取出來,這樣來實現程序間通訊
當然具體的內部原理因為我對於framework層沒有深入研究,傳輸的過程我不太瞭解
@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_test: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.test(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
後記
其實aidl很方便也是一種跨應用的解決方案,非常實用,面試和工作中都應該用得到,後續如果有機會可以多使用下,就先寫這麼多吧,謝謝網上的android先驅者和大神們!!!
遨遊在android知識的海洋中不可自拔的博主