1. 程式人生 > >Android IPC程序間通訊(四)AIDL

Android IPC程序間通訊(四)AIDL

AIDL-Android介面定義語言

一·
1.相比於Messenger AIDL可跨程序呼叫方法。
2.支援資料型別:
(1) Java 的原生基本型別(int, long, char, boolean, double等)
(2)String 和CharSequence
(3) ArrayList 和 HashMap ,裡面的元素必須是AIDL支援的資料型別; 以上三種類型都不需要匯入(import)
(4) AIDL 自動生成的介面 需要匯入(import)
(5) 實現android.os.Parcelable 介面的類. 需要匯入(import)
3.除了Java 的原生基本型別其他型別引數都需標明方向:in 輸入 , out 輸出, inout 出入輸出。
4.建議吧所有一AIDL相關檔案放在同一個資料夾下,方便複製到客戶端,而且服務端可客戶端AIDL包結構需保持一致,否則 無法反序列化成功
5.優點:功能強大,支援一對多併發通訊,支援即時通訊。
缺點:稍複雜,需做好執行緒同步。
使用場景:一對多且有RPC-遠端方法呼叫 需求。
二,RemoteCallbackList系統用於管理跨程序listener,支援管理任意的AIDL

public class RemoteCallbackList<E extends IInterfaxc>

內部提供一個Map來管理這些AIDL,key為IBinder,value為Callback

ArrayMap<IBinder,Callback> mCallbacks = new ArrayMap<>();

當客戶端註冊listener時,Callback將listener的資訊以IBinder為key存入mCallbacks,刪除時也是找到此key對應的listener進行刪除,因為跨程序生成的是相同的兩個物件
三,建立AIDL檔案,此程式碼複製貼上即可順利執行
1.建立Book類,IPC程序間通訊(三)中已講解,此處再建立一個aidl介面IOnNewBookArriveListener.aidl用於訂閱書籍,同樣會生成它自己的java類

package com.songfeng.aidlautocreate;
// Declare any non-default types here with import statements
//自定義類都需要顯示的引入進來
import com.songfeng.aidlautocreate.Book;
//提供一個監聽新書到達的介面
interface IOnNewBookArriveListener {
    void onNewBookArrived(in Book book);
}

2.在IBookManger.aidl介面中增加註冊和解註冊方法,用於被遠端呼叫,以實現某種功能

// IBookManager.aidl
package com.songfeng.aidlautocreate;

// Declare any non-default types here with import statements
import com.songfeng.aidlautocreate.Book;
import com.songfeng.aidlautocreate.IOnNewBookArriveListener;

interface IBookManager {

    List<Book> getBookList();
    void addBook(in Book book);
    void registerLister(IOnNewBookArriveListener listener);
    void unRegisterLister(IOnNewBookArriveListener listener);
}

3.建立BookManagerService用於管理AIDL檔案,在其中實現Binder,並返回給客戶端,以便在客戶端呼叫Binder中的方法。

public class BookManagerService extends Service {

    private static final String TAG = "BookManagerService";
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();
    //註冊和解註冊多程序中的監聽方法
    private RemoteCallbackList mListenerList = new RemoteCallbackList();

    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new arrivedBookRunnable()).start();
    }
    //自動生成的java類,建立Binder,重寫其方法,實現邏輯
    private IBookManager.Stub mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            if (!mBookList.contains(book)){
                mBookList.add(book);
            }
        }

        @Override
        public void registerLister(IOnNewBookArriveListener listener) throws RemoteException {
                mListenerList.register(listener);
        }

        @Override
        public void unRegisterLister(IOnNewBookArriveListener listener) throws RemoteException {
            mListenerList.unregister(listener);
        }
    };
	//在這進行一個許可權驗證,只有客戶端註冊了此自定義許可權才返回Binder給客戶端
    @Override
    public IBinder onBind(Intent intent) {
       int check = checkCallingOrSelfPermission("com.songfeng.aidlautocreate.permission.BookManagerService");
       if (check == PackageManager.PERMISSION_DENIED){
            return null;
       }
        return mBinder;
    }

    /**
     * 開啟執行緒每5秒新增一本新書並通知每一個註冊了註冊了監聽方法的客戶端
     */
    private class arrivedBookRunnable implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int bookId = mBookList.size() + 1;
            Book newBook = new Book(bookId,"Android : " + bookId);
            //新書到達,啟動通知
            oOnNewBookArrived(newBook);
        }
    }

    /**
     * 通知每一個註冊可監聽方法IOnNewBookArriveListener的客戶端,並回調到客戶端的onNewBookArrived()
     * @param newBook
     */
    private void oOnNewBookArrived(Book newBook){
        int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArriveListener listener =(IOnNewBookArriveListener) mListenerList.getBroadcastItem(i);
            try {
                listener.onNewBookArrived(newBook);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        mListenerList.finishBroadcast();
    }
}

4.在客戶端的連線方法中獲取Binder,即可呼叫Binder中方法

public class MainActivity extends AppCompatActivity {


    private static final String TAG = "MainActivity";

    private static final int MSG_BOOK_ARRIVED = 1;
    //自定義的AID檔案,裡面是一個介面
    private IBookManager mIBookManager;
    private List<Book> mBookList;
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler();
        Intent intent = new Intent(MainActivity.this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    private class MyHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_BOOK_ARRIVED:
                    Book book = (Book) msg.obj;
                    Toast.makeText(MainActivity.this, "新書 ID:"
                            + book.getBookId() + ", 書名 :" + book.getBookName()
                            + "已到達。", Toast.LENGTH_SHORT).show();
                default:
                    break;
            }
        }
    }

    /**
     * 監聽圖書到達,當服務呼叫新書到達的方法時就會回撥到此方法
     */
    private IOnNewBookArriveListener mBookArriveListener = new IOnNewBookArriveListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            mHandler.obtainMessage(MSG_BOOK_ARRIVED, book).sendToTarget();
        }
    };

    /**
     * 從服務中取出IBinder建立IBookManager
     * public static com.songfeng.aidlautocreate.IBookManager asInterface(android.os.IBinder obj)
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
            try {
                Toast.makeText(MainActivity.this, "連結伺服器成功", Toast.LENGTH_SHORT).show();
                mIBookManager = bookManager;
                mBookList = bookManager.getBookList();
                Log.d(TAG, "BookList.size :" + mBookList.size());
                //註冊監聽
                bookManager.registerLister(mBookArriveListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Toast.makeText(MainActivity.this, "伺服器已斷開,重連中...", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(MainActivity.this, BookManagerService.class);
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {
            try {
                mIBookManager.unRegisterLister(mBookArriveListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            unbindService(mConnection);
        }
    }
}

5.AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
. .  .  .   .
        <service
            android:name=".BookManagerService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote">
        </service>
</manifest>