AIDL使用学习(二):跨进程回调以及RemoteCallbackList

来源:互联网 发布:射精后强制刺激 知乎 编辑:程序博客网 时间:2024/06/07 19:26

前言

五一假期终于结束了,回来一直也是在面试,今天先把之前的AIDL的内容讲完,再整理一下面试总结。

正文

上一篇我们已经了解了AIDL的基本使用方法,一般服务内都是要做耗时操作的,等处理结束之后在回调给调用方,首先我们需要定义一个callback

// IOnCallbackListener.aidlpackage com.lzp.aidlstudy.callback;interface IOnCallbackListener{    void callback(int result);}

有些朋友可能有疑问了:这不是跟之前定义的Service一样吗?

恭喜你答对了,他俩就是一样的,这个时候在好好的体会一下AIDL的定义:Android 接口定义语言。也就是说他本身就是定义接口的,只不过他自动生成了内部的Binder机制,我们就以这个IOnCallbackListener为例,看看到底生成了什么东西:

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: /Users/li504799868/Desktop/AIDLStudy/app/src/main/aidl/com/lzp/aidlstudy/callback/IOnCallbackListener.aidl */package com.lzp.aidlstudy.callback;public interface IOnCallbackListener extends android.os.IInterface {    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements com.lzp.aidlstudy.callback.IOnCallbackListener {        private static final java.lang.String DESCRIPTOR = "com.lzp.aidlstudy.callback.IOnCallbackListener";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.lzp.aidlstudy.callback.IOnCallbackListener interface,         * generating a proxy if needed.         */        public static com.lzp.aidlstudy.callback.IOnCallbackListener asInterface(android.os.IBinder obj) {            ...        }        @Override        public android.os.IBinder asBinder() {            return this;        }        @Override        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {            ...        }        private static class Proxy implements com.lzp.aidlstudy.callback.IOnCallbackListener {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote) {                mRemote = remote;            }            @Override            public android.os.IBinder asBinder() {                return mRemote;            }            public java.lang.String getInterfaceDescriptor() {                return DESCRIPTOR;            }            @Override            public void callback(int result) throws android.os.RemoteException {                ...            }        }        static final int TRANSACTION_callback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    }    public void callback(int result) throws android.os.RemoteException;}

由于这个文件有点太长了,所以一些方法的代码就省略了,更为具体的大家可以自己运行去看一下源文件,简单的分析一下:

1、IOnCallbackListener里面除了定义了Stub内部类以外,只有callback()方法。
2、在Stub里面有很多熟悉的方法,例如获取代理,返回binder之类的,内部还有代理类Proxy。
3、Proxy中跨进程通信的Binder机制实现,这个是最核心的代码。

ok,总结也就是说,系统为我们自定义的接口实现了Binder机制,这样就可以实现跨进行,只不过这个Binder没有我们之前绑定服务使用的那么明显。

然后在之前的ITestInterface.aidl文件中新定义一个方法:

// aidl  定义实现的Service方法package com.lzp.aidlstudy;import com.lzp.aidlstudy.bean.TestBean;import com.lzp.aidlstudy.callback.IOnCallbackListener;interface ITestInterface {    // 定义一个计算方法    int getCalculateResult(in TestBean bean);    // 通过线程回调的方式返回计算结果    void getCalculateResultByThread(in TestBean bean, IOnCallbackListener callback);}

接下来去实现getCalculateResultByThread方法,打开TestService文件:

@Overridepublic void getCalculateResultByThread(final TestBean bean, final IOnCallbackListener callback) throws RemoteException {            new Thread() {                @Override                public void run() {                    callback.callback(bean.getX() + bean.getY());                }            }.start();        }

最后在MainActivity中添加一个按钮:

findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                try {                    TestBean testBean = new TestBean();                    testBean.setX(100);                    testBean.setY(250);                    // 这里要使用IOnCallbackListener.Stub                    binder.getCalculateResultByThread(testBean, new IOnCallbackListener.Stub() {                        @Override                        public void callback(final int result) {                            runOnUiThread(new Runnable() {                                @Override                                public void run() {                                    Toast.makeText(MainActivity.this, result + "", Toast.LENGTH_SHORT).show();                                }                            });                        }                    });                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });

首先强调一下,跨进程callback回调是在子线程中,千万不要忘记。

带着无比激动的心情,赶紧运行一下,完美!

这里写图片描述

扩展

很多朋友在网上查看资料的时候发现,还有一个RemoteCallbackList,他的主要作用是可以把多个callback保存到列表里,在合适的时机同时回调,也可以防止重复的调用相同的任务,只保证你需要的一个结果回调,这个就厉害了,他的源码也非常的简单:

package android.os;import android.util.ArrayMap;/** * 擅长 简单的持续性的一系列的远程接口的使用,尤其是Service对他的客户端的回调。 * 需要注意的是: * 使用的时候,请确保每一个注册的callback唯一性,这样可以在进程停止的时候,清空这些callback。 * 多线程请注意锁的问题。 *  * 使用这个类只要在Service使用单例模式就可以了,使用register和unregister方法来添加客户端的回调,使用时,先beginBroadcast,在getBroadcastItem,最后finishBroadcast。 *  * 如果一个注册的会滴啊进程结束了,这个类将自动从列中中移除,如果你想做一些额外的工作,就通过继承来实现onCallbackDied方法。 * /public class RemoteCallbackList<E extends IInterface> {    /*package*/ ArrayMap<IBinder, Callback> mCallbacks            = new ArrayMap<IBinder, Callback>();    private Object[] mActiveBroadcast;    private int mBroadcastCount = -1;    private boolean mKilled = false;    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) {        return register(callback, null);    }    /**     *注册的具体实现方法,注册的callback,只有在调用unregister或者进程结束才会被解绑,返回添加到集合中的结果(true、false)     */    public boolean register(E callback, Object cookie) {        synchronized (mCallbacks) {            if (mKilled) {                return false;            }            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;            }        }    }    /**     * 解绑callback     */    public boolean unregister(E callback) {        synchronized (mCallbacks) {            Callback cb = mCallbacks.remove(callback.asBinder());            if (cb != null) {                cb.mCallback.asBinder().unlinkToDeath(cb, 0);                return true;            }            return false;        }    }    /**     * 清空之前所有注册的callback     */    public void kill() {        synchronized (mCallbacks) {            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {                Callback cb = mCallbacks.valueAt(cbi);                cb.mCallback.asBinder().unlinkToDeath(cb, 0);            }            mCallbacks.clear();            mKilled = true;        }    }    /**     * 老版本的onCallbackDied     */    public void onCallbackDied(E callback) {    }    /**     * 默认调用的是onCallbackDied(E callback)r     */    public void onCallbackDied(E callback, Object cookie) {        onCallbackDied(callback);    }    /**     * 开始对保存的集合进行回调,返回目前回调集合的大小     */    public int beginBroadcast() {        synchronized (mCallbacks) {            if (mBroadcastCount > 0) {                throw new IllegalStateException(                        "beginBroadcast() called while already in a broadcast");            }            final int N = mBroadcastCount = mCallbacks.size();            if (N <= 0) {                return 0;            }            Object[] active = mActiveBroadcast;            if (active == null || active.length < N) {                mActiveBroadcast = active = new Object[N];            }            for (int i=0; i<N; i++) {                active[i] = mCallbacks.valueAt(i);            }            return N;        }    }    /**     * 获取指定索引的callback     */    public E getBroadcastItem(int index) {        return ((Callback)mActiveBroadcast[index]).mCallback;    }    /**     * 回去指定索引的callback的Cookie     */    public Object getBroadcastCookie(int index) {        return ((Callback)mActiveBroadcast[index]).mCookie;    }    /**     * 清楚之前的beginBroadcast的初始状态,当处理结束请调用这个方法。     *     * @see #beginBroadcast     */    public void finishBroadcast() {        synchronized (mCallbacks) {            if (mBroadcastCount < 0) {                throw new IllegalStateException(                        "finishBroadcast() called outside of a broadcast");            }            Object[] active = mActiveBroadcast;            if (active != null) {                final int N = mBroadcastCount;                for (int i=0; i<N; i++) {                    active[i] = null;                }            }            mBroadcastCount = -1;        }    }    /**     * 返回当前的的要处理的callback数量     * beginBroadcast 返回的是当前注册的数量     * getRegisteredCallbackCount返回的是处理的数量     * 两者的返回结果可能不同     */    public int getRegisteredCallbackCount() {        synchronized (mCallbacks) {            if (mKilled) {                return 0;            }            return mCallbacks.size();        }    }}

他的代码不多,但是英文注释是真心的多,我就直接在注释里面给大家简单的翻译一下了,具体大家可以自己去看源码注释。

下面就简单的修改一下我们的代码:

// 添加RemoteCallbackListprivate final RemoteCallbackList<IOnCallbackListener> mCallbacks            = new RemoteCallbackList<>();// 修改后的getCalculateResultByThread @Overridepublic void getCalculateResultByThread(final TestBean bean, final IOnCallbackListener callback) throws RemoteException {            new Thread() {                @Override                public void run() {                //注册callback                    mCallbacks.register(callback);                    callback(bean.getX() + bean.getY(), callback);                }            }.start();        }/*** 处理callback*/void callback(int result, IOnCallbackListener callback) {        final int N = mCallbacks.beginBroadcast();        try {            for (int i = 0; i < N; i++) {                IOnCallbackListener ibc = mCallbacks.getBroadcastItem(i);                ibc.callback(result);                // 处理结束,解绑callback                mCallbacks.unregister(ibc);            }        } catch (RemoteException e) {            e.printStackTrace();        }        mCallbacks.finishBroadcast();    }

运行结果也是一样的,就不贴出来了。

总结

这样AIDL的跨进程回调我们也了解了,那么他具体的调用过程是怎么样的,系统为我们生成的代码都起到了什么作用呢?这些我们在下一篇在深入的讨论。

Demo下载地址

0 0
原创粉丝点击