Binder通信机制与AIDL的使用

Dorothy ·
更新时间:2024-11-13
· 628 次阅读

Android进程间通信(Inter-Process Communication, IPC)采用Binder通信机制,是一种client/server结构。

AIDL(Android Interface Define Language):Android接口定义语言,帮助开发者自动生成实现Binder通信机制所需的相关模板代码。(如果你够牛逼的话,也可以不用AIDL生成代码,自己直接写相关java代码,当然也可以复制一份AIDL生成的代码到java目录下,用于分析Binder通信机制)。

下面通过一个实例讲解Binder机制以及AIDL的使用。
实例说明:Client进程传两个整数a和b给Server进程,Server进程进行加法运算,然后把相加后的结果返回给Client进程。

创建工程

步骤1:创建Client工程和Server工程,两个工程都创建IAdd.aidl文件,IAdd.aidl文件内容:

interface IAdd { int add(int a, int b); }

Client工程的目录结构:
在这里插入图片描述
Server工程的目录结构:
在这里插入图片描述

Client工程和Server工程中的aidl文件的名字,内容,以及所在的包名都要一模一样,最好就是在Client工程中写好了aidl文件后直接把整个aidl文件夹拷贝一份到Server的相同目录下即可,具体见上图。

aidl文件写好后,进行编译,编译后会自动生成IAdd类的源码:

public interface IAdd extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.binder.aidl.IAdd { private static final java.lang.String DESCRIPTOR = "com.binder.aidl.IAdd"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.binder.aidl.IAdd interface, * generating a proxy if needed. */ public static com.binder.aidl.IAdd asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.binder.aidl.IAdd))) { return ((com.binder.aidl.IAdd) iin); } return new com.binder.aidl.IAdd.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_add: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.add(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.binder.aidl.IAdd { 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 add(int a, int b) 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); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public int add(int a, int b) throws android.os.RemoteException; }

步骤2. Client工程的MainActivity代码如下:

public class MainActivity extends AppCompatActivity { Button btnPay; private IBinder binder; private IAdd iAdd; ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = iBinder; iAdd = IAdd.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(); intent.setAction("com.binder.server.MyService"); /*android5.0之后,如果service不在同一个App的包中, 需要设置service所在程序的包名,(包名可以到App的清单文件AndroidManifest中查看)*/ intent.setPackage("com.binder.server"); bindService(intent, serviceConnection, BIND_AUTO_CREATE);//开启Service btnPay = (Button) findViewById(R.id.btnPay); btnPay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { int result = iAdd.add(1, 2); Toast.makeText(getApplicationContext(), "result=" + result, Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { //因为是跨程序调用服务,可能会出现远程异常 e.printStackTrace(); } } }); } }

Server工程的MyService的代码如下:

public class MyService extends Service { private String TAG = "MyService"; public MyService() { } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind()"); iBinder = new MyBinder(); Log.i(TAG, "onBind(), iBinder=" + iBinder); return iBinder;//return MyBinder, 从而通过ServiceConnection在activity中拿到MyBinder } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } public int addFunction(int a, int b) { Log.i(TAG, "(), a=" + a + " ,b=" + b ); int result = a + b; return result; } private IBinder iBinder; class MyBinder extends IAdd.Stub { @Override public int add(int a, int b) throws RemoteException { int result = addFunction(a, b); return result; } //通过Binder实例将service中的方法暴露出去 } } 源码分析 Client端调用iAdd.add(1, 2);最终是如何调用Server端的addFunction()方法的

客户端Client通过ServiceConnectio获得Server的IBinder对象

ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = iBinder; iAdd = IAdd.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } };

这里的onServiceConnected(ComponentName componentName, IBinder iBinder)方法返回的并不是Server的MyService中创建的MyBinder对象本身,而是一个代理对象BinderProxy。(如果没有跨进程,则该方法返回的就是MyService中创建的MyBinder对象)。

iAdd是什么对象呢,看下IAdd.Stub.asInterface()方法:

/** * Cast an IBinder object into an com.binder.aidl.IAdd interface, * generating a proxy if needed. */ public static com.binder.aidl.IAdd asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.binder.aidl.IAdd))) { return ((com.binder.aidl.IAdd) iin); } return new com.binder.aidl.IAdd.Stub.Proxy(obj); }

最终执行的是

return new com.binder.aidl.IAdd.Stub.Proxy(obj);

所以iAdd其实是一个Proxy对象:

private static class Proxy implements com.binder.aidl.IAdd { 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 add(int a, int b) 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); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } }

所以当点击按钮,执行 result = iAdd.add(1, 2); 这句代码时,调用的是上面Proxy对象的add()方法:

@Override public int add(int a, int b) 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); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; }

关键的代码是这句:

mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

而这个mRemote对象就是上面onServiceConnected()方法返回的BinderProxy对象,BinderProxy类在Binder.java文件中,看下该类的transact()方法:

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Binder.checkParcel(this, code, data, "Unreasonably large binder buffer"); if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) { // For now, avoid spamming the log by disabling after we've logged // about this interface at least once mWarnOnBlocking = false; Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY", new Throwable()); } final boolean tracingEnabled = Binder.isTracingEnabled(); if (tracingEnabled) { final Throwable tr = new Throwable(); Binder.getTransactionTracker().addTrace(tr); StackTraceElement stackTraceElement = tr.getStackTrace()[1]; Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName()); } try { return transactNative(code, data, reply, flags); } finally { if (tracingEnabled) { Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); } } }

这里关键的就是调用了transactNative()方法,看下该方法

public native boolean transactNative(int code, Parcel data, Parcel reply, int flags) throws RemoteException;

可以看到,这个方法是native方法,这个方法其实是进行底层Binder驱动(C++层代码)发送消息的相关过程。
底层Binder驱动(C++层代码)最终会调用Binder的execTransact()方法

// Entry point from android_util_Binder.cpp's onTransact private boolean execTransact(int code, long dataObj, long replyObj, int flags) { Parcel data = Parcel.obtain(dataObj); Parcel reply = Parcel.obtain(replyObj); // theoretically, we should call transact, which will call onTransact, // but all that does is rewind it, and we just got these from an IPC, // so we'll just call it directly. boolean res; // Log any exceptions as warnings, don't silently suppress them. // If the call was FLAG_ONEWAY then these exceptions disappear into the ether. final boolean tracingEnabled = Binder.isTracingEnabled(); try { if (tracingEnabled) { Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code); } res = onTransact(code, data, reply, flags); } catch (RemoteException|RuntimeException e) { if (LOG_RUNTIME_EXCEPTION) { Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e); } if ((flags & FLAG_ONEWAY) != 0) { if (e instanceof RemoteException) { Log.w(TAG, "Binder call failed.", e); } else { Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e); } } else { reply.setDataPosition(0); reply.writeException(e); } res = true; } catch (OutOfMemoryError e) { // Unconditionally log this, since this is generally unrecoverable. Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e); RuntimeException re = new RuntimeException("Out of memory", e); reply.setDataPosition(0); reply.writeException(re); res = true; } finally { if (tracingEnabled) { Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); } } checkParcel(this, code, reply, "Unreasonably large binder reply buffer"); reply.recycle(); data.recycle(); // Just in case -- we are done with the IPC, so there should be no more strict // mode violations that have gathered for this thread. Either they have been // parceled and are now in transport off to the caller, or we are returning back // to the main transaction loop to wait for another incoming transaction. Either // way, strict mode begone! StrictMode.clearGatheredViolations(); return res; }

上面最关键的是调用了Binder的onTransact()方法,而Stub类重写了onTransact()方法,看下Stub类的onTransact()方法:

@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_add: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.add(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } } return super.onTransact(code, data, reply, flags); }

可以看到调用了Stub类的add()方法,而MyBinder重写了Stub类的add()方法:

class MyBinder extends IAdd.Stub { @Override public int add(int a, int b) throws RemoteException { int result = addFunction(a, b); return result; } }

add()方法里调用了addFunction()方法,到这里就完成了Server端的addFunction()方法的完整调用过程。

上述过程时序图:

在这里插入图片描述
总结:
Proxy是client端创建的用于向server端发送消息的代理(Proxy对象实现了IAdd接口,并且维护着一个Server端返回的BinderProxy对象),而Stub对象(也实现了IAdd接口)是server端用于接收消息的。client端通过BinderProxy对象的transact()方法将消息发送给底层Binder驱动,底层Binder驱动最终会将消息传递给Server端的Stub对象的onTransact()方法。

调用了 bindService() 后,onServiceConnected()方法的iBinder对象是如何返回的

参考:
Android AIDL与proxy,stub

Android跨进程通信IPC之10——Binder之Framework层Java篇

Android Framework:Binder(6)-Java层Service的注册及跨进程调用

腾讯面试题——谈一谈Binder的原理和实现一次拷贝的流程

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/os/Binder.java
https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/jni/AndroidRuntime.cpp
https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/jni/android_util_Binder.cpp


作者:yzpyzp



aidl binder

需要 登录 后方可回复, 如果你还没有账号请 注册新账号