Android進程通信AIDL之數據監聽,監聽器移除

前言:抱着最起碼的要求盡力去做好每一件事 ! ——秋不白

前言

Studio生成AIDL的代碼編寫,就不贅述了,要注意的是.aidl和對應的.java 文件的包名要保持一致。直蹦主題吧

 本博客中有些細節問題忽略了,代碼中也註釋說明,在不考慮業務耗時的情況下,實現AIDL進程通信,數據更新監聽,註冊監聽器和移除監聽器,Binder死亡監聽,服務斷開連接重連解決方案。實現業務:監聽圖書新增變化。

支持數據類型

  AIDL間通信比較簡單,支持的數據類型除了基礎數據類型,String CharSequence,其他定義的對象都要實現Parcelable,List只支持ArrayList,Map只支持HashMap,當然AIDL接口本身也是可以在AIDL文件中使用的。(實現AIDL中的數據更新監聽)

實現功能描述

    1. 監聽器的添加和移除,數據監聽,數據更新監聽都是進程間傳遞數據的過程,對象傳遞的過程有個共同的特點,當然我們知道,對象是無法直接跨進程傳遞的,所以實現了Parcelable,進過序列化,反序列化後就不是同一個對象了。那如何實現移除監聽器呢?這些對象傳遞的過程中有什麼共同的特點,來實現移除對應的監聽器

     答案是:這些對象傳遞,多次跨進程傳輸客戶端的對象在服務端生成不同的對象,它們底層的Binder對象死同一個,利用這個特性,就可以實現移除監聽器。這裏就要使用到RemoteCallbackList,這不是List,內部實現了線程同步,可以簡單查看源碼都加上了同步鎖。

    2.另外還使用到的CopyOnWriteArrayList,注意它不是繼承自ArrayList,同樣支持併發讀寫。使用這個來存放Book,被監聽的數據,前面提到只支持ArrayList,爲什麼呢,詳情看AidlService中代碼註釋。

    3. 介紹第二種,服務斷開重連的解決方案。(未實踐,主要是理解這種方法) 使用CountDownLatch這個類,countDownLatch是一個計數器,線程完成一個記錄一個,計數器遞減,只能只用一次。(常用的是在onServiceDisconnected()中去處理服務重連的問題)。

   文字描述結束

   Activity(部分代碼,客戶端)

 private ServiceConnection aidlConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iBookManager = IBookManager.Stub.asInterface(service);
        }

        /**
         * 服務斷開重連解決方案一
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(aidlConn);
        /** 移除新書到達監聽*/
        try {
            if (iBookManager != null) {
                iBookManager.unregisterListener(mOnNewBookArrivedListener);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            Log.d("RedRose", " 新書已到達");
        }
    };

    public void openAidl(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                connectBinderService();
            }
        }).start();
    }

    /**
     * 服務斷開重連解決方案二
     * 這部分代碼未實踐
     */
    private synchronized void connectBinderService() {
        /** 線程計數器*/
        mConnectBinderCountDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(this, AidlService.class);
        bindService(intent, aidlConn, BIND_AUTO_CREATE);
        try {
            mConnectBinderCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private IBinder.DeathRecipient mBinderDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            /** 對Binder 斷開 重練回調*/
            Log.d("RedRose", "binder died");
            iBookManager.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
            iBookManager = null;
            connectBinderService();
        }
    };

    /**
     * 其實這樣寫是有問題的,因爲在服務端註冊和取消註冊是運行在Binder線程池,如果遠程調用方法
     * 存在耗時,會導致主線程ANR
     * 應該放到線程池去執行,這裏只是側重點是數據更新監聽和取消註冊
     */
    public void unregisterListener(View view) {
        if (iBookManager != null) {
            try {
                iBookManager.unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    public void registerListener(View view) {
        if (iBookManager != null) {
            try {
                iBookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    public void addBook(View view) {
        try {
            if (iBookManager != null) {
                iBookManager.addBook(new Book());
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void getBookList(View view) {
        try {
            if (iBookManager != null) {
                iBookManager.getBookList();
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

       Book.aidl和IBookManager.aidl

// Book.aidl
package com.redrose.aidldemo;

parcelable Book;
---------------------------------------------------------------
// IBookManager.aidl
package com.redrose.aidldemo;

// Declare any non-default types here with import statements
import com.redrose.aidldemo.Book;
import com.redrose.aidldemo.IOnNewBookArrivedListener;
interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}
-------------------------------------------------------------------
// IOnNewBookArrivedListener.aidl
package com.redrose.aidldemo;

// Declare any non-default types here with import statements
import com.redrose.aidldemo.Book;
interface IOnNewBookArrivedListener {
   void onNewBookArrived(in Book book);
}

AidlService(服務端  爲了更好體現進程通信,請在清單文件指定service的進程),可以查下RemoteCallbackList的使用方法

package com.redrose.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class AidlService extends Service {
    /**
     * Aidl 中使用的List只有ArrayList
     * 但是CopyOnWriteArrayList 並不是繼承ArrayList
     * 因爲AIDL中支持的是抽象的List,而List只是一個接口
     * 因此服務端返回的是CopyOnWriteArrayList,但是在Binder中會按照
     * List的規範去訪問數據並最終形成一個新的ArrayList傳遞給客戶端
     */
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    //使用CopyOnWriteArrayList 來存放監聽器是無法實現的,進程間通信傳遞的不是同一個對象,最後導致無法找到,而無法正確移除
    //需要使用RemoteCallbackList,因爲多次跨進程傳輸客戶端的同一個對象在服務端生成不同的對象,但是他們底層的Binder對象
    //是同一個,所以就很明瞭了。 RemoteCallbackList內部同樣也實現了線程同步的功能,可以查看源碼
//    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();


    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBookBinder;
    }

    private IBinder mBookBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

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

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            Log.d("RedRose", " --registerListener--");
            mListenerList.register(listener);
            final int num = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d("RedRose", " mListenerList size = " + num);

        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            Log.d("RedRose", " --unregisterListener--");
            mListenerList.unregister(listener);
            final int num = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d("RedRose", " mListenerList size = " + num);
        }


        //在Binder被調用 綁定時調用,用作判斷是否具有權限
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

            //做權限判斷,沒有權限就返回null

            return super.onTransact(code, data, reply, flags);
        }
    };

    private void onNewBookArray(Book book) throws RemoteException {
        final int num = mListenerList.beginBroadcast();
        for (int i = 0; i < num; i++) {
            IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
            if (listener != null) {
                listener.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }
}

AIDL進程通信之數據監聽,監聽和移除總結實例就到這裏了,寫的比較簡潔,菜鳥肯定還需要多看下AIDL方面的知識點。老司機,勿噴。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章