[文章內容來自Developers]javascript
AIDL(Android 接口定義語言)與您可能使用過的其餘 IDL 相似。 您能夠利用它定義客戶端與服務使用進程間通訊 (IPC) 進行相互通訊時都承認的編程接口。 在 Android 上,一個進程一般沒法訪問另外一個進程的內存。 儘管如此,進程須要將其對象分解成操做系統可以識別的原語,並將對象編組成跨越邊界的對象。 編寫執行這一編組操做的代碼是一項繁瑣的工做,所以 Android 會使用 AIDL 來處理。html
注:只有容許不一樣應用的客戶端用 IPC 方式訪問服務,而且想要在服務中處理多線程時,纔有必要使用 AIDL。 若是您不須要執行跨越不一樣應用的併發 IPC,就應該經過實現一個 Binder建立接口;或者,若是您想執行 IPC,但根本不須要處理多線程,則使用 Messenger 類來實現接口。不管如何,在實現 AIDL 以前,請您務必理解綁定服務。java
在您開始設計 AIDL 接口以前,要注意 AIDL 接口的調用是直接函數調用。 您不該該假設發生調用的線程。 視調用來自本地進程仍是遠程進程中的線程,實際狀況會有所差別。 具體而言:android
您必須使用 Java 編程語言語法在 .aidl文件中定義 AIDL 接口,而後將它保存在託管服務的應用以及任何其餘綁定到服務的應用的源代碼(src/目錄)內。
您開發每一個包含 .aidl文件的應用時,Android SDK 工具都會生成一個基於該 .aidl文件的 IBinder接口,並將其保存在項目的 gen/目錄中。服務必須視狀況實現 IBinder接口。而後客戶端應用即可綁定到該服務,並調用 IBinder中的方法來執行 IPC。
如需使用 AIDL 建立綁定服務,請執行如下步驟:編程
注意:在 AIDL 接口首次發佈後對其進行的任何更改都必須保持向後兼容性,以免中斷其餘應用對您的服務的使用。 也就是說,由於必須將您的 .aidl文件複製到其餘應用,才能讓這些應用訪問您的服務的接口,所以您必須保留對原始接口的支持。api
1. 建立 .aidl 文件
AIDL 使用簡單語法,使您能經過可帶參數和返回值的一個或多個方法來聲明接口。 參數和返回值能夠是任意類型,甚至能夠是其餘 AIDL 生成的接口。
您必須使用 Java 編程語言構建 .aidl文件。每一個 .aidl文件都必須定義單個接口,而且只需包含接口聲明和方法簽名。
默認狀況下,AIDL 支持下列數據類型:安全
您必須爲以上未列出的每一個附加類型加入一個 import語句,即便這些類型是在與您的接口相同的軟件包中定義。
定義服務接口時,請注意:多線程
注意:您應該將方向限定爲真正須要的方向,由於編組參數的開銷極大。併發
如下是一個 .aidl
文件示例:app
// IRemoteService.aidlpackage com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/** 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);}複製代碼
只需將您的 .aidl文件保存在項目的 src/目錄內,當您開發應用時,SDK 工具會在項目的 gen/目錄中生成 IBinder接口文件。生成的文件名與 .aidl文件名一致,只是使用了 .java擴展名(例如,IRemoteService.aidl生成的文件名是 IRemoteService.java)。
若是您使用 Android Studio,增量編譯幾乎會當即生成 Binder 類。 若是您不使用 Android Studio,則 Gradle 工具會在您下一次開發應用時生成 Binder 類 — 您應該在編寫完 .aidl文件後當即用 gradle assembleDebug(或 gradle assembleRelease)編譯項目,以便您的代碼可以連接到生成的類。
2.實現接口
當您開發應用時,Android SDK 工具會生成一個以 .aidl文件命名的 .java接口文件。生成的接口包括一個名爲 Stub的子類,這個子類是其父接口(例如,YourInterface.Stub)的抽象實現,用於聲明 .aidl文件中的全部方法。
注:Stub還定義了幾個幫助程序方法,其中最引人關注的是 asInterface(),該方法帶 IBinder(一般即是傳遞給客戶端 onServiceConnected()回調方法的參數)並返回存根接口實例。 如需瞭解如何進行這種轉換的更多詳細信息,請參見調用 IPC 方法。
如需實現 .aidl生成的接口,請擴展生成的 Binder接口(例如,YourInterface.Stub)並實現從 .aidl文件繼承的方法。
如下是一個使用匿名實例實現名爲 IRemoteService的接口(由以上 IRemoteService.aidl示例定義)的示例:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};複製代碼
如今,mBinder是 Stub類的一個實例(一個 Binder),用於定義服務的 RPC 接口。 在下一步中,將向客戶端公開該實例,以便客戶端能與服務進行交互。
在實現 AIDL 接口時應注意遵照如下這幾個規則:
3.向客戶端公開該接口
您爲服務實現該接口後,就須要向客戶端公開該接口,以便客戶端進行綁定。 要爲您的服務公開該接口,請擴展 Service並實現 onBind(),以返回一個類實例,這個類實現了生成的 Stub(見前文所述)。如下是一個向客戶端公開 IRemoteService
示例接口的服務示例。
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}複製代碼
如今,當客戶端(如 Activity)調用 bindService() 以鏈接此服務時,客戶端的 onServiceConnected()回調會接收服務的 onBind()方法返回的 mBinder實例。
客戶端還必須具備對 interface 類的訪問權限,所以若是客戶端和服務在不一樣的應用內,則客戶端的應用 src/目錄內必須包含 .aidl文件(它生成 android.os.Binder接口 — 爲客戶端提供對 AIDL 方法的訪問權限)的副本。
當客戶端在 onServiceConnected()回調中收到 IBinder時,它必須調用 YourServiceInterface.Stub.asInterface(service)
以將返回的參數轉換成 YourServiceInterface
類型。例如:
IRemoteService mIRemoteService;private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};複製代碼
如需查看更多示例代碼,請參見 ApiDemos 中的 RemoteService.java 類。
經過 IPC 接口把某個類從一個進程發送到另外一個進程是能夠實現的。 不過,您必須確保該類的代碼對 IPC 通道的另外一端可用,而且該類必須支持Parcelable 接口。支持 Parcelable接口很重要,由於 Android 系統可經過它將對象分解成可編組到各進程的原語。
如需建立支持 Parcelable協議的類,您必須執行如下操做:
AIDL 在它生成的代碼中使用這些方法和字段將您的對象編組和取消編組。
例如,如下這個 Rect.aidl文件可建立一個可打包的 Rect類:
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;複製代碼
如下示例展現了 Rect類如何實現 Parcelable協議。
import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator<Rect> CREATOR = newParcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public Rect() {
}
private Rect(Parcel in) {
readFromParcel(in);
}
public void writeToParcel(Parcel out) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}複製代碼
Rect
類中的編組至關簡單。看一看 Parcel上的其餘方法,瞭解您能夠向 Parcel 寫入哪些其餘類型的值。
警告:別忘記從其餘進程接收數據的安全影響。 在本例中,Rect從 Parcel讀取四個數字,但要由您來確保不管調用方目的爲什麼這些數字都在相應的可接受值範圍內。
調用類必須執行如下步驟,才能調用使用 AIDL 定義的遠程接口:
有關調用 IPC 服務的幾點說明:
如需瞭解有關綁定到服務的詳細信息,請閱讀綁定服務文檔。
如下這些示例代碼摘自 ApiDemos 項目的遠程服務示例代碼,展現瞭如何調用 AIDL 建立的服務。
public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary mSecondaryService = null;
Button mKillButton;
TextView mCallbackText;
private boolean mIsBound;
/** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill); mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false);
mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}
/** * Class for interacting with the main interface of the service. */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
mKillButton.setEnabled(true);
mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mKillButton.setEnabled(false);
mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
/** * Class for interacting with the secondary interface of the service. */
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.setEnabled(false);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
intent.setAction(ISecondary.class.getName());
bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mKillButton.setEnabled(false);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};
private OnClickListener mKillListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (mSecondaryService != null) {
try {
int pid = mSecondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill. Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
mCallbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this, R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};
// ---------------------------------------------------------------------- // Code showing how to deal with callbacks.
// ----------------------------------------------------------------------
/** * This implementation is used to receive callbacks from the remote * service. */
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here will * NOT be running in our main thread like most other things -- so, * to update the UI, we need to use a Handler to hop over there. */
public void valueChanged(int value) { mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
}複製代碼