Binder框架 -- android AIDL 的使用
Android Aidl 的使用
Binder框架 – android AIDL 的使用
Binder框架 – 用户空间和驱动的交互
Binder框架 – Binder 驱动
Binder 框架 – binder 用户空间框架
Aidl 是android 跨进程通信的中一种,是一种RPC。底层基于binder 框架。通常用在C/S架构中。
Aidl 跨进程通信支持有限的数据类型
Aidl 可以进行跨进程通信,但是不是所有的数据类型都支持,支持的类型主要是:
- Java 的基本类型
- String 和CharSequence
- List 和 Map, 并且List和Map 对象的元素必须是AIDL支持的数据类型;以上三种类型都不需要导入(import)
- AIDL 自动生成的接口 需要导入(import)
- 实现android.os.Parcelable 接口的类. 需要导入(import)。
创建Aidl 文件
在android studio 中直接new 一个Aidl 文件 IHelloWorldInterface.aidl。注意文件命名规则,IXXX.adil。 只定义了一个printHelloWorld 接口。
interface IHelloWorldInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); String printHelloWorld();}
编译后生成 IHelloWorldInterface.java 文件。由于文件是aidl 工具生成的,格式比较乱,代码用工具格式化后IHelloWorldInterface 是一个接口,里面主要是两个类静态虚类public Stub类和private Stub.Proxy 类,Stub为存根的意思,那就是服务端使用,Stub.Proxy 为代理类,客户端使用。
public interface IHelloWorldInterface extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements com.example.louiewh.aidlapplication.IHelloWorldInterface { private static final java.lang.String DESCRIPTOR = "com.example.louiewh.aidlapplication.IHelloWorldInterface"; private static class Proxy implements com.example.louiewh.aidlapplication.IHelloWorldInterface { private android.os.IBinder mRemote; @Override public java.lang.String printHelloWorld() throws android.os.RemoteException { } } }}
服务端的实现
服务端 HelloWorldService 继承 IHelloWorldInterface.Stub 接口
public class HelloWorldService extends IHelloWorldInterface.Stub { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public String printHelloWorld() throws android.os.RemoteException { if(mListerner != null) { final int begin = mListerner.beginBroadcast(); for (int i = 0; i < begin; i++) { mListerner.getBroadcastItem(i).onAidlListerner( new StringBuilder(). append("Pid:").append(android.os.Process.myPid()). append(" Threadtime:").append(SystemClock.uptimeMillis()). toString() ); } mListerner.finishBroadcast(); } return "Hello AIDL!"; }}
创建Service
创建一个AidlService extends Service。AndroidManifext 中注册Service。 在Service 的
onBind 中返回HelloWorldService。
public class AidlService extends Service { public final static String TAG = "AidlService"; @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { Log.d(TAG, "onDestroy"); super.onDestroy(); } @Nullable @Override public IBinder onBind(Intent intent) { return new HelloWorldService(); }}
代理端调用
service 启动
android Service 启动有两种方式,一种startService,一种binderService. 由于我们要获取Service 的代理端,使用binderService。在 MainActivity onCreate 中 binderService。
Intent intent = new Intent(context, AidlService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
获取Service Proxy
然后 New 一个ServiceConnection, 在ServiceConnection中的onServiceConnected回调函数中会通过IHelloWorldInterface.Stub.asInterface返回Stub.proxy 对象。这样我们就拿到了服务的代理端。在MainActivity中设置text.setOnClickListener,当点击时显示printHelloWorld 的结果,就是进程PID和uptimeMillis时间。
ServiceConnection helloWorldConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { remoteService = IHelloWorldInterface.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } };text.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { text.setText(remoteService.printHelloWorld()); } catch (RemoteException e) { e.printStackTrace(); } } });
service 退出
在Activity退出时,需要unbinder
protected void onDestroy() { helloWorldProxy.unbindService(); super.onDestroy();}
一个Aidl 通信的架构就基本完完成了.
需要注意的是ServiceConnection是异步通信
设置listener
现在客户端可以调用服务端了,如果服务端需要通知客户端呢,就需要listener 出场了,listener 同样基于Aidl。定义一个onAidlListerner 回调。
1. 定义IAidlListernerInterface
interface IAidlListernerInterface { void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); void onAidlListerner(String str);}
2. 定义一个抽象类 AidlListerner 继承IAidlListernerInterface.Stub。
public abstract class AidlListerner extends IAidlListernerInterface.Stub {
}
3. IHelloWorldInterface.aidl 中增加两个函数,注册listener 和 反注册listener。
void registerListerner(IAidlListernerInterface listener); void unregisterListerner(IAidlListernerInterface listener);
4. HelloWorldService 中同样实现这两个函数。
同时有一个变量 ArrayList\
public String printHelloWorld() throws RemoteException { if(mListerner != null) { for (int i = 0; i < mListerner.size(); i++) { mListerner.get(i).onAidlListerner(new StringBuilder().append("current time:").append(SystemClock.currentThreadTimeMillis()).toString());; } } return "Hello AIDL!"; }
5. MainActivity 中 注册
这样在点击text 的时候 listenertext 会显示printHelloWorld 的结果。
remoteService.registerListener(new AidlListener() { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public void onAidlListerner(String str) throws RemoteException { listenerText.setText(str); } });
onDestory 中 unregisterListener。
6. 多进程模式下RemoteCallbackList
现在Service 和Activity 运行在一个进程中,Service 和 Client 通常运行在不同的进程中,现在我们配置Service 运行在另外一个进程。 这时候需要注意的是在HelloWorldService 中保存listener 的变量mListerner 类型为ArrayList 修改为: RemoteCallbackList\ mListerner = new RemoteCallbackList\();
原因是因为垮进程的listener 的指针地址改变了。listener 的遍历方式也发生了变化。
<service android:name=".AidlService" android:process=":AidlService"> service>
if(mListerner != null) { final int begin = mListerner.beginBroadcast(); for (int i = 0; i < begin; i++) { mListerner.getBroadcastItem(i).onAidlListerner(new StringBuilder(). append("Pid:").append(android.os.Process.myPid()). append("Current time:").append(SystemClock.currentThreadTimeMillis()). toString() ); } mListerner.finishBroadcast();}
代理服务
在一个应用里可能有很多这样的Aidl 文件,通常的做法是每个Aidl 文件都都建一个Service 用来bind,这样一个APK 进程中有很多Service 存在。是不是我们用一个Service 就可以了,因为都是在后台运行,这样就大大减小了对内存的消耗。在讲到Aidl 传输数据类型的时候Aidl 本身也支持Aidl自动生成的Interface 类型的传输,而我们定义的业务Service,比如HelloWorldService 本身就是继承Aidl 自动生成的Interface 类型。所以可以有一个专业的Service 来传递业务Service。 通常在获取系统服务的时候是 getApplicationContext().getSystemService(“XXX”), 同样也定义一个这样的API,来为应用内的调用提供APK 级别的Service 服务。
定义 IAidlBinderService.aidl
这个Aidl 文件之定义一个接口getService.
interface IAidlBinderService { void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); IBinder getService(String service);}
所有的业务Service 都通过getService 获取。
AidlBinderService 的服务端
onBind 的时候返回AidlBinderService 的Binder。
通过getService 传递进来的参数返回不同的Service 服务。这里一共两种业务Service,HelloWorldService HelloAidlService。这两种Service 单独一个java 文件,和Service 代码分离,当加入一个新的Service的时候,通常定义Aidl 文件,Service 代码继承 Stub 接口。然后定义getService 的字符串就可以加入一个新的Service。
public class AidlService extends Service { public final static String HELLOWORLDSERVICE = "HelloWorldService"; public final static String HELLOAIDLSERVICE = "HelloAidlService"; public final static String TAG = "AidlService"; @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { Log.d(TAG, "onDestroy"); super.onDestroy(); } @Nullable @Override public IBinder onBind(Intent intent) { return new AidlBinderService(); } class AidlBinderService extends IAidlBinderService.Stub{ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public IBinder getService(String service) throws RemoteException { switch(service) { case HELLOWORLDSERVICE: return new HelloWorldService(); case HELLOAIDLSERVICE: return new HelloAidlService(); default: return null; } } }}
AidlBinderService 的代理。
在处理AidlBinderService 的代理的时候,通常是在 ServiceConnection 回调中得到 iBinder。 这是一个异步的通信。我们希望代码能够耦合度更低,和业务能够分开,把AidlBinderService 单独的实现子啊一个java 文件中。一个做法是使用异步转同步的方法,直到ServiceConnection 的 onConnected 返回。但是这样毕竟有系统时间的消耗,而Service 的binder通常在APK 启动时,影响启动速度。这里继续沿用异步回到,两种方法,设置回调函数,使用Handler。AidlBinderServiceProxy 为单例模式,构造函数中去bindService,在onServiceConnected 使用传递进来的Handler 发送binderService 成功的消息。
获取AidlBinderService 的代理
public class AidlBinderServiceProxy { static final String TAG = "AidlBinderServiceProxy"; static final int AidlBinderService = 1; private Context mContext; private ServiceConnection mConnection; private IAidlBinderService mRemoteService; private Handler mHandler; private static AidlBinderServiceProxy instance; private AidlBinderServiceProxy(Context context, Handler handler) { mContext = context; mHandler = handler; initServiceConnection(); Intent intent = new Intent(context, AidlService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } public static AidlBinderServiceProxy instance(Context context, Handler handler){ synchronized (AidlBinderServiceProxy.class) { if(instance == null) { instance = new AidlBinderServiceProxy(context, handler); } } return instance; } public void unbindService() { Log.d(TAG, "unbindService"); mContext.unbindService(mConnection); instance = null; } private void initServiceConnection() { mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mRemoteService = IAidlBinderService.Stub.asInterface(iBinder); Message message = Message.obtain(mHandler, AidlBinderService); mHandler.sendMessage(message); Log.d(TAG, "onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; } public IBinder getService(String service) { if(mRemoteService == null) { } try { return mRemoteService.getService(service); } catch (RemoteException e) { Log.e(TAG, "getService " + "service"); e.printStackTrace(); } return null; }}
实现业务Service 的Proxy
实现两个业务Service 的Proxy。HelloAidlProxy 如下,在构造函数中通过getService 获取到IBinder 对象,通过I HelloAidlInterface.Stub.asInterface 函数转为代理对象。
public class HelloAidlProxy { static final String TAG = "HelloAidlProxy"; public AidlBinderServiceProxy mAidlBinderService; private IHelloAidlInterface mRemoteService; public HelloAidlProxy(AidlBinderServiceProxy proxy) { mAidlBinderService = proxy; IBinder binder = mAidlBinderService.getService(AidlService.HELLOAIDLSERVICE); mRemoteService = IHelloAidlInterface.Stub.asInterface(binder); } public PidInfo getPidInfo() { if(mRemoteService != null){ try { return mRemoteService.getPidInfo(); } catch (RemoteException e) { e.printStackTrace(); } } return null; }}
调用
修改MainActivity 的实现, 实现一个内部Handler 类:
class AidlHandler extends Handler{ public void handleMessage(Message msg) { switch(msg.what) { case AidlBinderServiceProxy.AidlBinderService: Log.d("louie", "AidlBinderService"); helloWorldProxy = new HelloWorldProxy(mAidlBinderService); default: break; } });
使用代理服务前后App 的结构变化:
https://www.processon.com/view/link/57a5991be4b02c28bf471316
Parcelable
上面已经实现了Aidl 生成的类型的数据跨进程传递,Aidl 生成的数据类型主要用于C/S 这样的架构。对于普通的对象如何处理呢,android 给我们准备了Parcelable 这种数据类型。和java 的Serializable 比较类似,Serializable 序列化基于文本,Parcelable 基于binder,效率更高。Parcelable 基于android提供的Parcel类型,将数据写入Parcel打包,需要的时候再从Parcel 读出完成序列化。
定义类继承 Parcelable
类 PidInfo implements Parcelable
- 重写writeToParcel方法,将对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中
- 重写describeContents方法,内容接口描述,默认返回0就可以
- 实例化静态内部对象CREATOR实现接口Parcelable.Creator,在 createFromParcel new 了一个参数为 Parcel 构造函数,需要注意的是:
这个构造函数实现的时候的读写顺序要和writeToParcel 方法一致
public class PidInfo implements Parcelable { private int mPid; PidInfo(int pid ){ mPid = pid; } PidInfo(Parcel in ) { mPid = in.readInt(); } public int getPid(){ return mPid; } public void setPid(int pid ){ mPid = pid; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mPid); } public static final Creator CREATOR = new Creator(){ @Override public PidInfo createFromParcel(Parcel source) { return new PidInfo(source); } @Override public PidInfo[] newArray(int size) { return new PidInfo[0]; } };}
AIDL 声明
PidInfo 定义完后还不能直接使用,需要在Aidl 中声明:
- 新建一个和类同名的Aidl 文件 PidInfo.aidl
- 在PidInfo.aidl 中 声明PidInfo 为parcelable 类型
parcelable PidInfo;
code
Aild 的使用大概就是这些,code 地址:github
更多相关文章
- C语言函数的递归(上)
- Android(安卓)NDK学习笔记4-Android.mk篇
- Android使用addView动态添加组件
- Android输入系统(三):加载按键映射
- [置顶] Android(安卓)Gallery用法(自定义边框+底部小圆点)
- Android那些事儿之自定义进度条
- Android向服务器的数据库MySQL传输数据:经过修正的 Android(安卓)
- Android的NDK开发(5)————Android(安卓)JNI层实现文件的read
- Android(安卓)编译,打包、签程名详细教