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

前言

五一假期终于结束了,回来一直也是在面试,今天先把之前的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下载地址

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值