天天看點

IPC機制之AIDL實作用戶端回調(四)

IPC機制之AIDL傳遞基礎類型資料(一)

IPC機制之Messenger示例(二)

IPC機制之AIDL傳遞Parcelable(三)

序言

前面的3篇文章文章實作了多程序的通訊,但是都隻完成了用戶端調用服務端的方法或者用戶端發送消息和服務端通信,下面我們介紹下AIDL中的觀察者模式,用戶端綁定服務端成功狗,回調用戶端的方法。

以上代碼都在在 IPC機制之AIDL傳遞Parcelable 文章的基礎上實作的,是以隻展示不同代碼,後面會附上完整代碼。

Server#IOnNewBookArrivedListener.aidl

package com.aidd.sample;

import com.aidd.sample.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}
           

Server#IBookManager.aidl

package com.aidd.sample;

import com.aidd.sample.Book;
import com.aidd.sample.IOnNewBookArrivedListener;

interface IBookManager {

    List<Book> getBookList();

    void addBook(in Book book);

    void registerListener(IOnNewBookArrivedListener listener);

    void unregisterListener(IOnNewBookArrivedListener listener);
}
           

Server#BookManagerService.java

package com.aidd.sample;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

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

public class BookManagerService extends Service {

    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();

    private String TAG = "info";

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(, "Android"));
        mBookList.add(new Book(, "iOS"));
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public void onDestroy() {
        mIsServiceDestroyed.set(true);
        super.onDestroy();
    }

    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);
            int count = mListenerList.beginBroadcast();
            Log.i(TAG, "registerListener success, count-->>" + count);
            mListenerList.finishBroadcast();
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
            int count = mListenerList.beginBroadcast();
            Log.i(TAG, "unregisterListener success, count-->>" + count);
            mListenerList.finishBroadcast();
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        Log.i(TAG, "onNewBookArrived, notify booklist-->>" + mBookList.size());
        for (int i = ; i < N; i++) {
            IOnNewBookArrivedListener arrivedListener = mListenerList.getBroadcastItem(i);
            if (arrivedListener != null)
                arrivedListener.onNewBookArrived(book);
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()) {
                try {
                    Thread.sleep();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + ;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           
RemoteCallbackList 是一個鍵值對,Key是Binder,value就是回調的callback,目的是為了記錄注冊了回調的用戶端。

Client#MainActivity.java

package sample.aidl.com.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.aidd.sample.Book;
import com.aidd.sample.IBookManager;
import com.aidd.sample.IOnNewBookArrivedListener;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private IBookManager iBookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void startIPC(View view) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.aidd.sample", "com.aidd.sample.BookManagerService"));
        this.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iBookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> bookList = iBookManager.getBookList();
                Log.i("info", "清單類型" + bookList.getClass().getCanonicalName());
                Log.i("info", "資料查詢結果" + bookList.toString());
                iBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iBookManager = null;
        }
    };

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case :
                    Log.i("info", "recive new book");
                    break;
            }
        }
    };

    private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            handler.obtainMessage(, newBook).sendToTarget();
        }
    };

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

Q: Client#MainActivity.java中為什麼IOnNewBookArrivedListener回調方法使用Handle?

A: 因為用戶端的回調方法運作在用戶端的Binder線程池中,即子線程,是以無法直接更新UI,但是onServiceConnected和onServiceDisconnected是運作在UI線程的。

Q: Server#BookManagerService.java中的mBinder方法是運作在哪裡?

A: 用戶端調用服務端的方法也是運作在Binder線程池中的,隻是是運作在服務端的Binder線程池中,是以這裡面的方法是非UI線程中。

Q: 服務端binder提供的方法是不是可以執行耗時操作?

A: 由于Binder中的方法是在Binder線程池中,是以是可以執行耗時操作的,但是如果用戶端是在UI線程中調用的這些方法,會導緻用戶端長時間收不到回調而卡死,出現ANR。同理,如果服務端如果在主線程中調用用戶端的回調方法時,如果是用戶端回調是耗時操作,服務端也會出現ANR。

源碼下載下傳