RemoteCallBackList

在学习app进程与ams之间的通信的时候,发现它们之间的通信不简单是:app进程调用ams的方法;ams也需要调用app进程的方法。

仔细查了一下,发现通过Binder通信,数据类型也可以是Binder,也就是:

app通过Binder(ActiviyManager)传给ams一个Binder(ApplicationThread),此Binder(ApplicationThread)在app进程定义了stub类(ApplicationThreadNative),ams就可以通过拿到的Binder调用app的stub类的方法。

Aidl回调

通过Aidl可以完成一个进程A对另外一个进程B的调用,这个调用的数据传输是双向的,即B可以返回数据给A。不仅如此,B也可以主动返回数据给A,这就是下面要说的回调。

一个简单的回调demo,仅仅需要两个aidl:

interface IBookManager {

    void register(in IOnNewArrivalListener listener);

    void unregister(in IOnNewArrivalListener listener);
}

interface IOnNewArrivalListener {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onNewArrival(in Book book);
}

下面建立一个service模拟ipc通信

public class AidlCallbackService extends Service {

    private String TAG = AidlCallbackService.class.getSimpleName();
    public AidlCallbackService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new Callback();
    }


    class Callback extends IBookManager.Stub{

        IOnNewArrivalListener mListener;

        @Override
        public void add(Book book) throws RemoteException {

        }

        @Override
        public void register(IOnNewArrivalListener listener) throws RemoteException {
            Log.d(TAG,Thread.currentThread().getName());
            mListener = listener;
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        Book book = new Book();
                        book.setPrice(String.valueOf(Math.random()));
                        book.setName(String.valueOf(Math.random()));
                        mListener.onNewArrival(new Book());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            },5000);
        }

        @Override
        public void unregister(IOnNewArrivalListener listener) throws RemoteException {
            Log.d(TAG,Thread.currentThread().getName());
            mListener = listener;
        }
    }
}

app进程如下:

public class AidlCallbackActivity extends AppCompatActivity implements View.OnClickListener {

    private String TAG = AidlCallbackActivity.class.getSimpleName();

    private IBookManager mIBookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl_callback);
        findViewById(R.id.button).setOnClickListener(this);
        Intent intent  = new Intent(this,AidlCallbackService.class);

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

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
    }


    @Override
    public void onClick(View v) {
        try {
            mIBookManager.register(mOnNewArrival);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private IOnNewArrivalListener mOnNewArrival = new IOnNewArrivalListener.Stub(){

        @Override
        public void onNewArrival(Book book) throws RemoteException {
            Log.d(TAG,Thread.currentThread().getName()+"new book "+ book.getName()+"arrival, price :"+book.getPrice());
        }
    };

}

Binder线程池

如果把这个说清楚,已经超过这篇文章的范围。但是至少得知道:

存根类里的方法是运行在一个不确定的线程里的,这个线程就来自Binder线程池。

从之前说的回调的代码可以看出:server端需要维护回调对象IOnNewArrivalListener。而server端又可以被多端调用,所以这个对象必须使用一个容器维护。

如果使用map, 这个Map就得有个对应关系,value肯定是回调对象,key呢?Binder!!对,Binder对应连接的client,所以可以确保唯一的对应关系。

既然这里提到了Binder线程池,这个Map就得必须考虑多线程的问题。

RemoteCallBackList

为了能帮助开发者在server端开发的时候,能避免在使用map的时候出现问题,RemoteCallBackList就出现了。

在继续之前,先说一个问题:为什么是List而不是什么什么Map?Map不是更能快速的找到某一个client对应的callback吗?

可能的答案:如果需求里必须用到client与callback对应关系,建议不要使用RemoteCallBackList。

(顿时感觉这个玩意设计有缺陷)

从ams源码中也可以看到RemoteCallBackList,比较适用于一些事件的推送。

本来想着ApplicationThread作为一个callback应该也用RemoteCallBackList维护,最后看到源码,竟然是ProcessRecord。


废话没了。。。。开始说是这个玩意

为了解决client与callback的一一对应以及client意外死亡的问题:RemoteCallBackList内部维护了一个binder和callback的数组,并对callback进了封装:

//回调的封装
private final class Callback implements IBinder.DeathRecipient {
        final E mCallback;
        final Object mCookie;

        Callback(E callback, Object cookie) {
            mCallback = callback;
            mCookie = cookie;
        }

        public void binderDied() {
            synchronized (mCallbacks) {
                mCallbacks.remove(mCallback.asBinder());
            }
            onCallbackDied(mCallback, mCookie);
        }
    }
//对应关系
public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return false;
            }
            // Flag unusual case that could be caused by a leak. b/36778087
            logExcessiveCallbacks();
            IBinder binder = callback.asBinder();
            try {
                Callback cb = new Callback(callback, cookie);
                binder.linkToDeath(cb, 0);
                mCallbacks.put(binder, cb);
                return true;
            } catch (RemoteException e) {
                return false;
            }
        }
    }

核心代码就这么多。


下面为了将map变成list,有提供了三个方法:

beginBroadcast,getBroadcastItem,finishBroadcast

对应的操作:提取map的value到数组中,从数组获取对应index的value,清空数组。代码就不贴了。

为什么搞这个复杂??直接:

public E getBroadcastItem(int index){
    return map.valueAt(index)
}

不更爽!!求大神解答。。。

猜你喜欢

转载自blog.csdn.net/weixin_38801333/article/details/80668770