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下载地址
- AIDL使用学习(二):跨进程回调以及RemoteCallbackList
- Android 使用AIDL跨进程通信(二)--传递自定义对象
- Android 使用AIDL跨进程通信(二)--传递自定义对象
- aidl ( 七) RemoteCallbackList
- 【Android学习之路】使用AIDL进行跨进程通信
- 跨进程访问(AIDL服务)—Service(二)
- 跨进程间如何进行AIDL IPC 通信(二)
- 使用AIDL跨进程通信
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android使用AIDL实现跨进程通讯(IPC)
- window下控制台编码设置
- C++实现文件夹复制
- Mybatis 当实体属性与数据库字段不一致时的解决方案
- 第三章:嵌入式QT--自重启
- mysql + mybatis分组查询注意事项
- AIDL使用学习(二):跨进程回调以及RemoteCallbackList
- 【springboot 入门篇】第0篇 spring-boot是什么
- 类模板碰到友元函数
- Java编程思想-11持有对象
- Zookeeper的配置
- python中的map函数
- vlayout的使用
- 如何更好的理解动态代理
- 过滤器Filter和监听器Listener