Binder的学习与理解笔记之三:aidl原理及应用实例
1.Binder机制用于进程间通信,在Android的应用层开发中,所涉及的进程间通信的应用场景较少,一旦有这一类需求呢?是否要应用自身来维护Binder通信的流程?是的,如果应用层的开发者也需要对Binder机制足够了解,那进程通信的开发成本就太大了,毕竟,Binder的原理及流程还是相当复杂的。google向我们提供了应用层开发进程间通信的工具,aidl便是最普遍,也是定制灵活度最高的。其他还有一些,比如Content Provider通过开放数据访问接口支持进程间通信,IMessage等,应用范围都比较局限。
2.先来看一个简单的aidl示例,单向行为,即Client向服务器发送指令,服务器处理指令,返回结果。以下例子只是为了说明aidl的原理,非常简单。
a.先来看client与server通信的数据类,需要实现Parcel接口,可以在进程间传输的数据格式一定是序列化的
//com.demo.aidldemo.aidl.Data.java
package com.demo.aidldemo.aidl; import android.os.Parcel; import android.os.Parcelable;
public class Data implements Parcelable{ private int index; private String str; protected Data(Parcel in) { index = in.readInt(); str = in.readString(); } public Data(int i, String ss) { this.index = i; this.str = ss; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(index); dest.writeString(str); } @Override public int describeContents() { return 0; } public static final Creator<Data> CREATOR = new Creator<Data>() { @Override public Data createFromParcel(Parcel in) { return new Data(in); } @Override public Data[] newArray(int size) { return new Data[size]; } }; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } }
b.对应的aidl文件为:
// com.demo.aidldemo.aidl.Data.aidl
package com.demo.aidldemo.aidl; // Declare any non-default types here with import statements parcelable Data;
之所以要写一个Data的aidl文件,是因为要在aidl接口文件中出现的数据类型,必须要写有java类对应的aidl文件
c.client与server通用的aidl接口定义
// com.demo.aidldemo.aidl.IDataProcessManager.java
package com.demo.aidldemo.aidl;
// 此处必须要显式的引入Data,不管是否是在同一包名下 import com.demo.aidldemo.aidl.Data; // Declare any non-default types here with import statements interface IDataProcessManager { int getAddIndex(in Data data); String getNewString(in Data data); }
d.Server端(运行在:process进程中)
//com.demo.aidldemo.aidl.DataProcessService.java
package com.demo.aidldemo.aidl; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.support.annotation.Nullable; /** * Created by wangguoqiang on 2018/5/22. */ public class DataProcessService extends Service { public void onCreate() { super.onCreate(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } private Binder mBinder = new IDataProcessManager.Stub() { @Override public int getAddIndex(Data data) throws RemoteException { return data.getIndex() + 10; } @Override public String getNewString(Data data) throws RemoteException { return data.getStr() + " hi, change"; } }; }
e.client端(运行在主进程中)
//com.demo.aidldemo.MainActivity.java
package com.demo.aidldemo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import com.demo.aidldemo.aidl.Data; import com.demo.aidldemo.aidl.DataProcessService; import com.demo.aidldemo.aidl.IDataProcessManager; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, DataProcessService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Data data = new Data(10086, "China Mobile"); IDataProcessManager manager = IDataProcessManager.Stub.asInterface(iBinder); try { int index = manager.getAddIndex(data); String str = manager.getNewString(data); Log.d("KCSTEST", "index : " + index + " str : " + str); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; }
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.demo.aidldemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".aidl.DataProcessService" android:process=":process"/> </application> </manifest>
运行项目,日志输出结果为:
05-22 16:35:08.209 20529-20529/com.demo.aidldemo D/KCSTEST: index : 10096 str : China Mobile hi, change
达到了项目目的。
3.从上面简单的demo来看一下进程间通信方式Binder的各组成因素及其在aidl的配合方式
Binder驱动 -- demo中看不到, 从前几章的笔记中可以看出,Binder驱动为底层,面向的为C++层的BpBinder,在aidl向上封装的接口位于Java层,中间间隔着Binder Java层及c++层的一系列调用,具体可参考前两章的笔记
ServiceManager -- demo中看不到,隐藏在对外提供的接口调用之下
Binder Server -- 本例中为DataProcessService,位于:process进程中
Binder Client -- 本例中为MainActivity,位于应用主进程中
在项目中我们定义的IDataProcessManager.aidl文件,Android在编译期会生成IDataProcessManager.java文件,位置为:
4.重点分析这个类,分析完之后大家就会明白Binder和aidl接口的关系,来看一下该类的定义(为方便,下面的代码经过了格式化,原文件是没有的)
package com.demo.aidldemo.aidl;
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/wangguoqiang/othercode/demo/aidl/app/src/main/aidl/com/demo/aidldemo/aidl/IDataProcessManager.aidl
*/
// Declare any non-default types here with import statements
public interface IDataProcessManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.demo.aidldemo.aidl.IDataProcessManager
{
private static final java.lang.String DESCRIPTOR = "com.demo.aidldemo.aidl.IDataProcessManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.aidldemo.aidl.IDataProcessManager interface,
* generating a proxy if needed.
*/
public static com.demo.aidldemo.aidl.IDataProcessManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.demo.aidldemo.aidl.IDataProcessManager))) {
return ((com.demo.aidldemo.aidl.IDataProcessManager)iin);
}
return new com.demo.aidldemo.aidl.IDataProcessManager.Stub.Proxy(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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getAddIndex:
{
data.enforceInterface(DESCRIPTOR);
com.demo.aidldemo.aidl.Data _arg0;
if ((0!=data.readInt())) {
_arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
int _result = this.getAddIndex(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getNewString:
{
data.enforceInterface(DESCRIPTOR);
com.demo.aidldemo.aidl.Data _arg0;
if ((0!=data.readInt())) {
_arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
java.lang.String _result = this.getNewString(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.demo.aidldemo.aidl.IDataProcessManager
{
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 int getAddIndex(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((data!=null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getAddIndex, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public java.lang.String getNewString(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((data!=null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getNewString, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getAddIndex = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getNewString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int getAddIndex(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException;
public java.lang.String getNewString(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException;
}
回顾一下第一章提到的Java层的类结构,来对比着看一下上面的代码:
没错,上面两个类的角色是一样的,向客户端提供标准的调用方式,向服务端提供标准的实现方式,最终达到的目的是,客户端调用该接口,获取的结果为服务端同名方法的执行结果。
aidl所成生在IDataProcessManager里面呈放有内部类,因此看起来略为怪异,其自身的代码如下:
就是定义了我们需要的业务函数。
再来看一下IDataProcessManager.Stub这个类
这两个类在Binder的角色也是一致的,回忆一下我们之前提到过的ServiceManagerNative
看一下这个类的代码(摘掉了内部类Proxy,稍后会讲这个内部类):
public static abstract class Stub extends android.os.Binder implements com.demo.aidldemo.aidl.IDataProcessManager
{
private static final java.lang.String DESCRIPTOR = "com.demo.aidldemo.aidl.IDataProcessManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.aidldemo.aidl.IDataProcessManager interface,
* generating a proxy if needed.
*/
public static com.demo.aidldemo.aidl.IDataProcessManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.demo.aidldemo.aidl.IDataProcessManager))) {
return ((com.demo.aidldemo.aidl.IDataProcessManager)iin);
}
return new com.demo.aidldemo.aidl.IDataProcessManager.Stub.Proxy(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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getAddIndex:
{
data.enforceInterface(DESCRIPTOR);
com.demo.aidldemo.aidl.Data _arg0;
if ((0!=data.readInt())) {
_arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
int _result = this.getAddIndex(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getNewString:
{
data.enforceInterface(DESCRIPTOR);
com.demo.aidldemo.aidl.Data _arg0;
if ((0!=data.readInt())) {
_arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
java.lang.String _result = this.getNewString(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
static final int TRANSACTION_getAddIndex = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getNewString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
以下两函数是给Binder Client用的:
向客户端提供Binder对象
以下方法为给服务端用的:
自动实现了经过Binder驱动层之后,调至服务端的事务的具体实现(详情可参考前两章的调度方式)
好,看完这个类之后再来看IDataProcessManager.Stub.Proxy这个内部类,看一下下面两幅图:
这两个类的角色也是一致的。
private static class Proxy implements com.demo.aidldemo.aidl.IDataProcessManager
{
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 int getAddIndex(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((data!=null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getAddIndex, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public java.lang.String getNewString(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((data!=null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getNewString, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
实现Java层到c++层的调用,最终调至Binder驱动层。
5.为实现服务端具体的执行逻辑,我们只需要继承Stub这个类,并实现相关方法即可,如下所示:
private Binder mBinder = new IDataProcessManager.Stub() { @Override public int getAddIndex(Data data) throws RemoteException { Thread th = Thread.currentThread(); Log.d("KCSTEST", "getAddIndex thread : " + th.getName()); return data.getIndex() + 10; } @Override public String getNewString(Data data) throws RemoteException { Thread th = Thread.currentThread(); Log.d("KCSTEST", "getNewString thread : " + th.getName()); return data.getStr() + " hi, change"; } };
然后在Service的onBind方法中返回该mBinder
public IBinder onBind(Intent intent) { return mBinder; }
MainActivity中通过Intent bindService绑定至该Service,则可拿到该Binder对象,通过asInterface转为IDataProcessManager类型的对象,即可使用该接口提供的与服务端同一标准的业务功能。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, DataProcessService.class);
// 绑定至所定义的bind服务端,远程Service bindService(intent, connection, Context.BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// 此处可以拿到与Service通信的Binder对象 iBinder Data data = new Data(10086, "China Mobile"); IDataProcessManager manager = IDataProcessManager.Stub.asInterface(iBinder); try { int index = manager.getAddIndex(data); String str = manager.getNewString(data); Log.d("KCSTEST", "index : " + index + " str : " + str); Thread th = Thread.currentThread(); Log.d("KCSTEST", "onServiceConnected thread : " + th.getName()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; }
好,原理方面就讲完了,说两个注意点:
1.Binder服务端是开启线程来执行相关逻辑,因此,无特殊需要,不需要再新开线程,如上面代码中,我们加了两个注释,可以看一下日志:
05-25 15:58:23.425 16640-16649/com.demo.aidldemo:process D/KCSTEST: getAddIndex thread : Binder:16640_1
05-25 15:58:23.425 16640-16654/com.demo.aidldemo:process D/KCSTEST: getNewString thread : Binder:16640_2
两函数均运行在线程中,且为不同的线程
2.如果在aidl实现了服务器变化向客户端发回的回调,也是运行在线程中的,如果有ui操作,请回到主线程中执行,回调的代码也不复杂,例子也较多,此处就不再写了。