編輯:關於android開發
之前面試的時候被問到這個問題,然而當時只有一個大致的印象,隨GG,於是我就重新整理的一下。這裡大力推薦《Android開發藝術探索》這本書,寫的太好了!
AIDL(Android Interface Define Language) 是IPC進程間通信方式的一種.用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼.
下面一個簡單的例子來說明AIDL的使用:假設一個情景我們需要計算a+b,我們需要在客戶端傳遞兩個參數a和b,然後將參數傳遞給服務端(另一個進程)來進行計算,計算結果傳遞給客戶端。
1. 新建一個項目作為服務端,在項目中新建AIDL文件。這裡我命名為:IImoocAIDL.aidl
// IImoocAIDL.aidl
package com.mecury.aidltest;
// Declare any non-default types here with import statements
interface IImoocAIDL {
//計算num1 + num2
int add(int num1,int num2);
}
點擊同步按鈕(一定要先同步),查看是否生成IImoocAIDL文件。
生成的文件如下,我寫了詳細的注釋,相信你能夠看懂:
這裡來說一下AIDL通信的原理:首 先看這個文件有一個叫做proxy的類,這是一個代理類,這個類運行在客戶端中,其實AIDL實現的進程間的通信並不是直接的通信,客戶端和服務端都是通 過proxy來進行通信的:客戶端調用的方法實際是調用是proxy中的方法,然後proxy通過和服務端通信將返回的結果返回給客戶端。
package com.mecury.aidltest;
public interface IImoocAIDL extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.mecury.aidltest.IImoocAIDL {
private static final java.lang.String DESCRIPTOR = "com.mecury.aidltest.IImoocAIDL";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.mecury.aidltest.IImoocAIDL asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.mecury.aidltest.IImoocAIDL))) {
return ((com.mecury.aidltest.IImoocAIDL) iin);
}
return new com.mecury.aidltest.IImoocAIDL.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_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);
}
private static class Proxy implements com.mecury.aidltest.IImoocAIDL {
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 int add(int num1, int num2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int add(int num1, int num2) throws android.os.RemoteException;
}
代碼中的幾個方法:
DESCRIPTION
Binderd的唯一標識,一般用當前的類名表示。
asInterface(android.os.IBinder obj)
用於將服務端的Binder對象轉換為客戶端需要的AIDL接口類型的對象,轉換區分進程,客戶端服務端位於同一進程,返回服務端的 //Stub對象本身;否則返回的是系統的封裝後的Stub.proxy對象。
asBInder
返回Binder對象
onTransact
此方法運行在服務端中的Binder線程池中,當客戶端發起跨進程請求時,遠程請求會通過系統底層封裝後交由此方法處理。
Proxy#add
此
方法運行在客戶端,當客戶端遠程調用此方法時,它的內部實現是這樣的:首先創建該方法所需要的輸入型Parcel對象_data、輸出型Parcel對象
_reple和返回值對象_result,然後將該方法的參數信息寫入_data中;接著調用transact方法來發RPC請求,同時當前線程掛起;然
後服務端的onTransact方法會被調用,直到RPC過程返回後,當前線程繼續執行,並從_reply中取出RPC過程返回的結果,寫入
_result中。
2.新建一個客戶端File-》new--》new module--》phone & table module。這裡我的命名為aidlclient.java
同樣要在客戶端創建AIDL文件,裡面的包名和所在位置要求完全一樣。
3.在服務端創建一個Service用來監聽客戶端的連接請求。
public class IRemoteService extends Service {
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
private IBinder iBinder = new IImoocAIDL.Stub(){
@Override
public int add(int num1, int num2) throws RemoteException {
Log.e("TAG","收到了來自客戶端的請求" + num1 + "+" + num2 );
return num1 + num2;
}
};
}
最後,別忘記在AndroidManifest.xml中注冊該Service。
<service android:name=".IRemoteService"
android:process=":remote"
android:exported="true">
<intent-filter>
<action android:name="com.mecury.aidltest.IRomoteService"/>
</intent-filter>
</service>
4.客戶端的編寫
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/num1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="+"/>
<EditText
android:id="@+id/num2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="="/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="30dp" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="計算"/>
</LinearLayout>
MainActivity.java
package com.mecury.aidlclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.mecury.aidltest.IImoocAIDL;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText num1;
private EditText num2;
private Button button;
private TextView text;
private IImoocAIDL iImoocAIDL;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iImoocAIDL = IImoocAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iImoocAIDL = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
initView();
}
private void initView() {
num1 = (EditText) findViewById(R.id.num1);
num2 = (EditText) findViewById(R.id.num2);
button = (Button) findViewById(R.id.button);
text = (TextView) findViewById(R.id.text);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int num11 = Integer.parseInt(num1.getText().toString());
int num22 = Integer.parseInt(num2.getText().toString());
try {
int res = iImoocAIDL.add(num11,num22);
text.setText(num11 +"+"+ num22 +"="+ res);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void bindService() {
Intent intent = new Intent();
intent.setAction("com.mecury.aidltest.IRomoteService");
intent.setComponent(new ComponentName("com.mecury.aidltest","com.mecury.aidltest.IRemoteService"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
5.運行代碼
先啟動服務端,在啟動客戶端。
小結:看完上面,是不是已經對於AIDL
的用法有個大概的了解。下面來看一個更為復雜的例子,這是《android開發藝術探索》中的例子: 建立一個圖書管理,能夠添加圖書、得到圖書列表、使用觀察者模式、當新書到達時通知所有觀察者。
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public String toString() {
return bookId + ":" + bookName;
}
}
package com.mecury.aidltest2;
parcelable Book;
package com.mecury.aidltest2;
import com.mecury.aidltest2.book;
interface IOnNewBookArrivedListener {
void OnNewBookArrivedListener(in Book book);
}
package com.mecury.aidltest2;
import com.mecury.aidltest2.book;
import com.mecury.aidltest2.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>();
private Binder mBinder = new IBookManager.Stub(){
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListeners.register(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListeners.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1,"Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new serviceWork()).start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int N = mListeners.beginBroadcast();
Log.e("onNewBookArrived","registener listener size:" + N);
for (int i = 0; i < N; i++){
IOnNewBookArrivedListener l = mListeners.getBroadcastItem(i);
if (l!=null){
l.OnNewBookArrivedListener(book);
}
}
mListeners.finishBroadcast();
}
private class serviceWork implements Runnable{
@Override
public void run() {
while (!mIsServiceDestoryed.get()){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId,"new Book #" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
}
ArrayMap<IBinder, Callback> mCallback = new ArrayMap<IBinder, Callback>();
,當客戶端注冊listener時,會把listener的信息注冊到mCallBack
中,其中key和value通過下面方式獲得:IBinder key = listener.asBinder();Callback value = new Callback(listener, cookie)
。另外一點我們需要知道:對 象是不能跨進程傳輸的,對象的跨進程傳輸過程實際是反序列化的過程,這是我們Book類為什麼要實現Parcelable接口的原因。在跨進程傳輸 中,Binder會把客戶端傳遞的對象重新轉化並生成另一對象,當我們注冊和解注冊的過程中使用的是同一個客戶端對象,但是通過Binder傳遞到服務端 卻生成了兩個不同的對象。而RemoteCallBackList就是用來解決這個問題的,雖然所多次跨進程傳輸客戶端的同一個對象會在服務端 生成不同的對象,但在這些新生成的對象都有一個共同點,那就是他們底層的Binder對象是同一個,利用這個,就可以實現上面無法實現的功能。當客戶端解 注冊時,我們只要遍歷所有的listener,找出那個和解注冊listener具有相同Binder對象服務器listener並把他刪除掉即可,這就 是RemoteCallbackList為我們做的事情。(對於這個看不明白的,可以看看《android 開發藝術探索》)public class MainActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
private IBookManager bookManager;
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_NEW_BOOK_ARRIVED:
Log.e(TAG, "received new book:" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mService = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
Log.e(TAG, "query book list,list type:" + list.getClass().getCanonicalName());
Log.e(TAG, "query book list:" + list.toString());
Book newBook = new Book(3, "android進階");
bookManager.addBook(newBook);
Log.e(TAG, "add book:" + newBook);
List<Book> newList = bookManager.getBookList();
Log.e(TAG, "query book list:" + newList.toString());
bookManager.registerListener(mNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookManager = null;
Log.e(TAG, "binder died.");
}
};
private IOnNewBookArrivedListener mNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void OnNewBookArrivedListener(Book book) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
}
private void bindService() {
Intent intent = new Intent();
intent.setAction("com.mecury.aidltest2.BookManagerService");
intent.setComponent(new ComponentName("com.mecury.aidltest2", "com.mecury.aidltest2.BookManagerService"));
bindService(intent, mService, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (bookManager != null && bookManager.asBinder().isBinderAlive()){
Log.e(TAG, "unregister listener:" + mNewBookArrivedListener);
try {
bookManager.unregisterListener(mNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mService);
super.onDestroy();
}
}
目錄結構:
客戶端log:
06-29 22:52:29.438 17007-17007/com.mecury.client E/BookManagerActivity: query book list,list type:java.util.ArrayList
06-29 22:52:29.438 17007-17007/com.mecury.client E/BookManagerActivity: query book list:[1:Android, 2:Ios]
06-29 22:52:29.439 17007-17007/com.mecury.client E/BookManagerActivity: add book:3:android進階
06-29 22:52:29.439 17007-17007/com.mecury.client E/BookManagerActivity: query book list:[1:Android, 2:Ios, 3:android進階]
06-29 22:52:33.487 17007-17007/com.mecury.client E/BookManagerActivity: received new book:4:new Book #4
06-29 22:52:38.489 17007-17007/com.mecury.client E/BookManagerActivity: received new book:5:new Book #5
06-29 22:52:43.491 17007-17007/com.mecury.client E/BookManagerActivity: received new book:6:new Book #6
06-29 22:52:48.503 17007-17007/com.mecury.client E/BookManagerActivity: received new book:7:new Book #7
服務端log:
06-29 22:52:33.487 17027-17044/com.mecury.aidltest2:remote E/onNewBookArrived: registener listener size:1
06-29 22:52:38.488 17027-17044/com.mecury.aidltest2:remote E/onNewBookArrived: registener listener size:1
06-29 22:52:43.490 17027-17044/com.mecury.aidltest2:remote E/onNewBookArrived: registener listener size:1
06-29 22:52:48.492 17027-17044/com.mecury.aidltest2:remote E/onNewBookArrived: registener listener size:1
參考:
1.《android開發藝術探索》
詳細Android Studio + NDK范例 【本范例所采用的配置】 ·系統:Windows7旗艦版,Service Pack 1,32位(最新的NDK已不支
【開源】二維碼簽到手機andriod版下載!,開源簽到andriod 最近,應很多人的要求,我的易簽到二維碼簽到andriod版現
微信小程序監控,信小程序監控HotApp小程序統計,第一個專業的微信第三方小程序監控統計工具 1.什麼是HotApp小程序統計 HotApp小程序統計是第一個微信第三方小
Android 的系統架構,Android系統架構Android 的系統架構 Android其本質就是在標准的Linux系統上增加了Java虛擬機Dalvik,並在Dal