1. AIDL 文件支持的數據類型:html
2. 自定義的 Parcelable 對象 和 AIDL 對象必需要顯示 import 進來,無論他們是否和當前的 AIDL 文件位於同一個包內。java
3. 若是 AIDL 文件中用到了自定義的 Parcelable 對象,那麼必須新建一個和它同名的 AIDL 文件,並在其中聲明它爲 Parcelable 類型。android
4. AIDL 中除了基本數據類型,其餘類型的參數必須標上方向:in 、out 或者 inout,in 表示輸入型參數 , out 表示輸出型參數,inout 表示輸入輸出型參數。安全
AIDL 是用於多進程通訊場景的,咱們建立兩個應用,一個 Client 和一個 Server 來講明 AIDL 的使用。在示例項目中實現了基本的交互操做,還有 權限驗證 、註冊 listener 到服務端監聽服務端事件 和重連機制。併發
客戶端工程目錄:app
看圖咱們能夠知道,工程中建立了和 AIDL 相關的四個文件,Book類、Book.aidl 、IBookManager.aidl、IOnNewBookArrivedListener.aidl 四個文件。ide
(1) Book類是示例中用於傳遞的數據類,實現了 Parcelable接口,代碼以下:ui
package com.sl.analyzebinder; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } @Override public int describeContents() { return 0; } 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]; } }; }
(2)Book.aidl 的做用是要符合上面第一章節第 3 點的規定,代碼以下:this
package com.sl.analyzebinder; parcelable Book;
(3)IBookManager.aidl 定義交互操做的接口,代碼以下:atom
// IBookManager.aidl package com.sl.analyzebinder; import com.sl.analyzebinder.Book; import com.sl.analyzebinder.IOnNewBookArrivedListener; // Declare any non-default types here with import statements interface IBookManager { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnNewBookArrivedListener listener); void unRegisterListener(IOnNewBookArrivedListener listener); }
(4)IOnNewBookArrivedListener.aidl 定義接聽服務事件的接口,代碼以下:
// IOnNewBookArrivedListener.aidl package com.sl.analyzebinder; import com.sl.analyzebinder.Book; // Declare any non-default types here with import statements interface IOnNewBookArrivedListener { void onNewBookArrived(in Book book); }
全部關於AIDL的文件寫好以後,咱們先 Make Project 一下,而後來看一下客戶端如何鏈接服務端,MainActivity 中進行鏈接:
package com.sl.analyzebinder; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.List; public class MainActivity extends AppCompatActivity { private static final int MESSAGE_NEW_BOOK_ARRIVED = 110; private BookHandler mHandler = new BookHandler(this); private IBookManager bookManager; private static class BookHandler extends Handler { private WeakReference<MainActivity> activity; BookHandler(MainActivity activity) { this.activity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_NEW_BOOK_ARRIVED: if (activity != null) { activity.get().changeContent((Book) msg.obj); } break; default: super.handleMessage(msg); break; } } } private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if (bookManager == null) return; bookManager.asBinder().unlinkToDeath(mDeathRecipient, 0); bookManager = null; bindService(); } }; private void changeContent(Book book) { String text = content.getText().toString(); content.setText(text.concat("-----------").concat(book.bookName)); } private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book book) throws RemoteException { mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget(); } }; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // onServiceConnected 和 onServiceDisconnected 都在ui線程中執行,不要執行服務端耗時操做,不然 ANR bookManager = IBookManager.Stub.asInterface(service); try { List<Book> bookList = bookManager.getBookList(); StringBuilder result = new StringBuilder(); if (bookList != null && bookList.size() > 0) { for (int i = 0; i < bookList.size(); i++) { result.append("----").append(bookList.get(i).bookName); } } content.setText(bookList != null ? result : "失敗了"); bookManager.addBook(new Book(3, "擺渡人")); bookManager.registerListener(mListener); service.linkToDeath(mDeathRecipient,0); //重連機制 } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { //重連機制 bookManager = null; } }; private TextView content; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService(); content = findViewById(R.id.book_list); } private void bindService() { Intent service = new Intent(); service.setAction("com.sl.aidl"); service.setPackage("com.sl.binderservice"); bindService(service, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); unRegisterListener(); } private void unRegisterListener() { if (bookManager != null && bookManager.asBinder().isBinderAlive()) { // 經過Binder 的 isBinderAlive 方法也能夠判斷 Binder 是否死亡 try { bookManager.unRegisterListener(mListener); } catch (RemoteException e) { e.printStackTrace(); } } } public void stopInform(View view) { unRegisterListener(); } }
爲何要先 Make Project 一下呢,目的是爲了讓系統爲咱們自動生成 AIDL 中的 Binder 實現,你能夠到下面這個目錄中去查看一下具體生成的代碼:
對了,要想 aidl 目錄下的 java 文件被 編譯,還須要再 app 下的build.xml 文件中添加配置:
android {
sourceSets{
main{
java.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
}
}
服務端要求權限驗證,因此客戶端得添加權限,這個權限在服務端中定義:
<uses-permission android:name="com.sl.permission.aidl" />
將 客戶端 aidl 目錄所有拷貝到 服務端,拷貝後來看服務端目錄:
拷貝過來以後,仍然須要 Make Project ,道理你懂得。BookManagerService 就是咱們的服務了,來看具體實現:
package com.sl.binderservice; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import com.sl.analyzebinder.Book; import com.sl.analyzebinder.IBookManager; import com.sl.analyzebinder.IOnNewBookArrivedListener; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; public class BookManagerService extends Service { private static final String TAG = "BMS"; private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); //CopyOnWriteArrayList 支持併發讀寫 private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); //CopyOnWriteArrayList 支持併發讀寫 private AtomicBoolean mIsServiceDestory = new AtomicBoolean(); 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 { mListenerList.register(listener); } @Override public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException { mListenerList.unregister(listener); } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { //onTransact處進行權限驗證 int check = checkCallingOrSelfPermission("com.sl.permission.aidl"); if (check == PackageManager.PERMISSION_DENIED) //權限驗證 return false; String packageName = null; String[] packages = getPackageManager().getPackagesForUid(getCallingUid()); // uid 驗證 if (packages != null && packages.length > 0) { packageName = packages[0]; } if (packageName == null || !packageName.startsWith("com.sl")) return false; return super.onTransact(code, data, reply, flags); } }; @Override public void onCreate() { super.onCreate(); mIsServiceDestory.set(false); mBookList.add(new Book(1, "Android")); mBookList.add(new Book(2, "IOS")); new Thread(new ServiceWorker()).start(); } @Override public void onDestroy() { super.onDestroy(); mIsServiceDestory.set(true); } @Override public IBinder onBind(Intent intent) { //onBind 處進行權限驗證,驗證權限並無經過。不知道是爲何?
/**int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
* if (check == PackageManager.PERMISSION_DENIED) * return null;
*/ return mBinder; } private class ServiceWorker implements Runnable { @Override public void run() { while (!mIsServiceDestory.get()) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int bookId = mBookList.size() + 1; Book newBook = new Book(bookId, "NewBook#" + bookId); try { onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } } } } private void onNewBookArrived(Book book) throws RemoteException { mBookList.add(book); int count = mListenerList.beginBroadcast(); for (int i = 0; i < count; i++) { IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i); if (listener != null) listener.onNewBookArrived(book); } mListenerList.finishBroadcast(); } }
記得在 AndroidManifest.xml 中註冊服務:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sl.binderservice"> <permission android:name="com.sl.permission.aidl" android:description="@string/service_permission" android:permissionGroup="com.sl.permissions" android:protectionLevel="normal" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".BookManagerService" android:permission="com.sl.permission.aidl" android:exported="true"> <intent-filter> <action android:name="com.sl.aidl" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> </manifest>
BookManagerService 中進行了權限驗證,涉及到 自定義權限 查看這篇。除了自定義的Permission 驗證外,還有經過 UID 驗證,這是 Binder 機制提供的,這就是體現 Binder 通訊機制相對其餘機制安全的點。
注意服務端中用到了幾個類,這裏提一下:
AtomicBoolean : 支持併發讀寫
RemoteCallbackList : 支持併發讀寫 ,是系統專門提供的用於刪除跨進程 listener 的接口,它是一個泛型,支持管理任意的 AIDL 接口 。它還能當客戶端進程終止後,它可以自動移除客戶端所註冊的 listener 。
CopyOnWriteArrayList : 支持併發讀寫 。
這三個這裏就不詳細介紹了,到這裏AIDL 的具體使用也基本完事了,若是有漏的話,後續補上或者告訴我。
示例工程代碼,下載地址 密碼: smf7