美文网首页Android开发技术分享aidlAndroid开发经验谈
AIDL使用学习(二):跨进程回调以及RemoteCallbac

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

作者: 珠穆朗玛小王子 | 来源:发表于2017-09-12 16:59 被阅读0次

    前言#

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

    正文#

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

    // IOnCallbackListener.aidl
    package 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文件:

    @Override
    public 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();
            }
        }
    }
    
    

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

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

    // 添加RemoteCallbackList
    private final RemoteCallbackList<IOnCallbackListener> mCallbacks
                = new RemoteCallbackList<>();
    
    // 修改后的getCalculateResultByThread
     @Override
    public 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下载地址

    相关文章

      网友评论

        本文标题:AIDL使用学习(二):跨进程回调以及RemoteCallbac

        本文链接:https://www.haomeiwen.com/subject/nsocsxtx.html