1、前言
我们知道Android垮进程通信主要采用的是Binder,而AIDL是一种Android接口定义语言,用于更加方便的在应用层使用Binder跨进程通信。
2、AIDL的基本使用
2.1、定义AIDL接口
package com.yili.aidl;
import com.yili.aidl.ICallBack;
interface IMyAidlInterface {
String getStudentByName(String name);
void setCallBack(ICallBack callBack);
}
package com.yili.aidl;
interface ICallBack {
void onResult(String result);
}
2.2、客户端
绑定服务bindService
package com.yili.aidl
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.os.Process
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "MainActivity"
}
var iMyAidlInterface: IMyAidlInterface? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val service = Intent(this, MyService::class.java)
val conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "${Process.myPid()}-${Thread.currentThread().name} onServiceConnected: ")
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
iMyAidlInterface?.setCallBack(callBack)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(
TAG,
"${Process.myPid()}-${Thread.currentThread().name} onServiceDisconnected: "
)
}
}
bindService(service, conn, BIND_AUTO_CREATE)
findViewById<Button>(R.id.btn_start).setOnClickListener {
val student = iMyAidlInterface?.getStudentByName("daxiaa")
Log.d(
TAG,
"${Process.myPid()}-${Thread.currentThread().name} getStudentByName: ${student}"
)
}
}
val callBack = object : ICallBack.Stub() {
override fun onResult(result: String?) {
Log.d(TAG, "${Process.myPid()}-${Thread.currentThread().name} onResult:${result} ")
}
}
}
2.3、服户端
创建服务MyService
package com.yili.aidl
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.Process
import android.util.Log
/**
* @Description:
* @author:weishixiong
* @date:2023/4/23 9:10
*
*/
class MyService : Service() {
companion object {
const val TAG = "MyService"
}
var mCallBack: ICallBack? = null
val binder = object : IMyAidlInterface.Stub() {
override fun getStudentByName(name: String): String {
Log.d(
TAG,
"${Process.myPid()}-${Thread.currentThread().name} getStudentByName:${name} "
)
mCallBack?.onResult("get name start")
return name
}
override fun setCallBack(callBack: ICallBack) {
Log.d(TAG, "${Process.myPid()}-${Thread.currentThread().name} setCallBack:${callBack} ")
mCallBack = callBack
}
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
}
3、AIDL生成源码分析
我们知道AIDL文件定义的接口,最终是会通过aidl.exe这个工具生成对应的Java代码文件,之所以通过工具来生成,不需要我们手写,是因为这些Java代码比较麻烦,并且大多数是重复的公共代码。 我们再来看看对应的AIDL接口,定义了两个接口IMyAidlInterface和ICallBack。
package com.yili.aidl;
import com.yili.aidl.ICallBack;
interface IMyAidlInterface {
String getStudentByName(String name);
void setCallBack(ICallBack callBack);
}
interface ICallBack {
void onResult(String result);
}
我们来分析一下IMyAidlInterface生成的源代码
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.yili.aidl;
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.yili.aidl.IMyAidlInterface
{
@Override public java.lang.String getStudentByName(java.lang.String name) throws android.os.RemoteException
{
return null;
}
@Override public void setCallBack(com.yili.aidl.ICallBack callBack) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.yili.aidl.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.yili.aidl.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.yili.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.yili.aidl.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.yili.aidl.IMyAidlInterface))) {
return ((com.yili.aidl.IMyAidlInterface)iin);
}
return new com.yili.aidl.IMyAidlInterface.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getStudentByName:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.getStudentByName(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_setCallBack:
{
data.enforceInterface(descriptor);
com.yili.aidl.ICallBack _arg0;
_arg0 = com.yili.aidl.ICallBack.Stub.asInterface(data.readStrongBinder());
this.setCallBack(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.yili.aidl.IMyAidlInterface
{
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 java.lang.String getStudentByName(java.lang.String name) 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);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentByName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getStudentByName(name);
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setCallBack(com.yili.aidl.ICallBack callBack) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((callBack!=null))?(callBack.asBinder()):(null)));
boolean _status = mRemote.transact(Stub.TRANSACTION_setCallBack, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setCallBack(callBack);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.yili.aidl.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_getStudentByName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.yili.aidl.IMyAidlInterface impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.yili.aidl.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.lang.String getStudentByName(java.lang.String name) throws android.os.RemoteException;
public void setCallBack(com.yili.aidl.ICallBack callBack) throws android.os.RemoteException;
}
代码非常简单,IMyAidlInterface对应的AIDL接口,也会对应生成相应的Java的IMyAidlInterface接口,同时包含了两个函数getStudentByName和setCallBack。在IMyAidlInterface里有一个静态内部类Stub和Proxy,实现了IMyAidlInterface接口,在Stub中有一个静态函数asInterface,客户端首先将服务端返回的IBinder转换成了本地端的Proxy,当我们通过通过代理对象Proxy调用AIDL接口的时候,在代理类里真正的实现是通过传入的IBinder对象实现跨进程通信,
如下代码:
将远程返回的IBinder转换成对应的IMyAidlInterface接口
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
创建代理对象new com.yili.aidl.IMyAidlInterface.Stub.Proxy(obj);,Proxy实现了IMyAidlInterface接口
public static com.yili.aidl.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.yili.aidl.IMyAidlInterface))) {
return ((com.yili.aidl.IMyAidlInterface)iin);
}
return new com.yili.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
val student = iMyAidlInterface?.getStudentByName("daxiaa")
调用IMyAidlInterface接口中的getStudentByName方法,在getStudentByName方法内部通过传入的IBinder调用transact方法,实现跨进程通信。
@Override public java.lang.String getStudentByName(java.lang.String name) 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);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentByName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getStudentByName(name);
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
那客户端调用了AIDL接口的方法之后,怎么又能回调到服务端的对应的方法呢?我们来看看服务端的实现
val binder = object : IMyAidlInterface.Stub() {
override fun getStudentByName(name: String): String {
Log.d(
TAG,
"${Process.myPid()}-${Thread.currentThread().name} getStudentByName:${name} "
)
mCallBack?.onResult("get name start")
return name
}
override fun setCallBack(callBack: ICallBack) {
Log.d(TAG, "${Process.myPid()}-${Thread.currentThread().name} setCallBack:${callBack} ")
mCallBack = callBack
}
}
我们知道服务端返回的IBinder,是一个IMyAidlInterface.Stub的实现类,来看看IMyAidlInterface.Stub
public static abstract class Stub extends android.os.Binder implements com.yili.aidl.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.yili.aidl.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.yili.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.yili.aidl.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.yili.aidl.IMyAidlInterface))) {
return ((com.yili.aidl.IMyAidlInterface)iin);
}
return new com.yili.aidl.IMyAidlInterface.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getStudentByName:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.getStudentByName(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_setCallBack:
{
data.enforceInterface(descriptor);
com.yili.aidl.ICallBack _arg0;
_arg0 = com.yili.aidl.ICallBack.Stub.asInterface(data.readStrongBinder());
this.setCallBack(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
在IMyAidlInterface.Stub中有一个onTransact方法,当客户端通过Ibinder调用了tansact方法之后。服务端中的onTransact将由Binder系统调用,在onTransact方法中根据对应的case调用对应的aidl接口的实现,比如
java.lang.String _result = this.getStudentByName(_arg0);。
这样就调用到了我们自定义实现的方法
流程图:
4、几个常见的关键字和方法
4.1、 oneway
当我们的AIDL接口的实现没有任何修饰的时候,默认是同步调用,客户端发起跨进程通信的时候会将当前线程挂起并阻塞在当前线程,当服务器返回之后再恢复当前线程。如果客户端我们是在UI线程调用AIDL接口的话,服务器由于长时间没响应就可能会造成客户端ANR。 oneway关键字指的是异步调用,并且oneway修饰的AIDL接口不能有返回值,我们看下面的例子。
- 客户端调用AIDL接口
findViewById<Button>(R.id.btn_add).setOnClickListener {
iMyAidlInterface?.addStudent("daxiaa")
Log.d(TAG, "onCreate: add finish")
}
- 服务端实现
val binder = object : IMyAidlInterface.Stub() {
override fun addStudent(name: String?) {
Thread.sleep(3000)
Log.d(TAG, "addStudent: $name")
}
}
我们在服务端休眠了3S,我们看打印结果,客户端调用之后立即打印完成,3S之后再打印服务端的日志
2023-04-23 16:05:54.119 30234-30234/com.yili.aidl D/MainActivity: onCreate: add finish
2023-04-23 16:05:57.120 30263-30283/com.yili.aidl D/MyService: addStudent: daxiaa
4.2、 in,out,intout
in和out关键字都可以用于修饰AIDL接口中的参数
(1)in表示数据参数由客户端流向服务端,服务端可以接收到客户端的真实数据 但是服务端修改了对象参数,不会影响客户端。
(2)out表示数据参数由服务端流向客户端,客户端的对象参数传递到服务端,服务端只会接受到空参数的对象,服务端修改了对象参数之后,客户端能收到对应改变后的参数。
(3) 表示客户端和服务端的对象相互流动,客户端和服务端修改了传递的参数,都会相互影响双方。
我们来看下面的三个例子:
AIDL接口
package com.yili.aidl;
import com.yili.aidl.User;
interface IMyAidlInterface {
void addUserIn(in User user);
void addUserOut(out User user);
void addUserInOut(inout User user);
}
服务端实现
val binder = object : IMyAidlInterface.Stub() {
override fun addUserIn(user: User) {
Log.d(TAG, "addUserIn:客户端传来的数据 = $user")
Log.d(TAG, "addUserIn:服务端将姓名修改为 daxiaa2")
user.name = "daxiaa2"
}
override fun addUserOut(user: User) {
Log.d(TAG, "addUserOut:客户端传来的数据 = $user")
user.name = "daxiaa3"
Log.d(TAG, "addUserIn:服务端将姓名修改为 daxiaa3")
}
override fun addUserInOut(user: User) {
Log.d(TAG, "addUserInOut: 客户端传来的数据 = $user")
Log.d(TAG, "addUserIn:服务端将姓名修改为 daxiaa4")
user.name = "daxiaa4"
}
客户端调用
findViewById<Button>(R.id.btn_addIn).setOnClickListener {
val user = User("xiaoming")
iMyAidlInterface?.addUserIn(user)
Log.d(TAG, "addUserIn 调用结果 = $user")
}
findViewById<Button>(R.id.btn_addOut).setOnClickListener {
val user = User("xiaoming")
iMyAidlInterface?.addUserOut(user)
Log.d(TAG, "addUserOut 调用结果 = $user")
}
findViewById<Button>(R.id.btn_addInOut).setOnClickListener {
val user = User("xiaoming")
iMyAidlInterface?.addUserInOut(user)
Log.d(TAG, "addUserInOut 调用结果 = $user")
}
例子1
com.yili.aidl D/MyService: addUserIn:客户端传来的数据 = StudentInfo{name='xiaoming'}
com.yili.aidl D/MyService: addUserIn:服务端将姓名修改为 daxiaa2
com.yili.aidl D/MainActivity: addUserIn 调用结果 = StudentInfo{name='xiaoming'}
例子2
com.yili.aidl D/MyService: addUserOut:客户端传来的数据 = StudentInfo{name='null'}
com.yili.aidl D/MyService: addUserIn:服务端将姓名修改为 daxiaa3
com.yili.aidl D/MainActivity: addUserOut 调用结果 = StudentInfo{name='daxiaa3'}
例子3
com.yili.aidl D/MyService: addUserInOut: 客户端传来的数据 = StudentInfo{name='xiaoming'}
com.yili.aidl D/MyService: addUserIn:服务端将姓名修改为 daxiaa4
com.yili.aidl D/MainActivity: addUserInOut 调用结果 = StudentInfo{name='daxiaa4'}
注意
in out 关键字必须并且只能修饰自定义对象类型的参数,元数据如基本类型,String,还有AIDL接口类型,Binder类型都是不需要修饰。
4.3、 linktodeath
Ibinder对应的服务端是否死亡。
val conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "${Process.myPid()}-${Thread.currentThread().name} onServiceConnected: ")
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
iMyAidlInterface?.setCallBack(callBack)
//监听服务端的是否死亡
service?.linkToDeath(object: IBinder.DeathRecipient {
override fun binderDied() {
}
}, 0)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(
TAG,
"${Process.myPid()}-${Thread.currentThread().name} onServiceDisconnected: "
)
}
}
我们在客户端通过binderService绑定服务的时候,虽然可以通过onServiceDisconnected回调判断服务端是否死亡,但是当我们要实现AIDL双向通信的时候,我们把客户端的Binder通过AIDL接口传给了服务端,此时服务端是没有onServiceDisconnected函数的,所以便可以通过binder?.linkToDeath的方式监听客户端是否死亡。也就是我们可以通过IBinder的linkToDeath函数来判断IBinder所在的进程是否死亡。创建IBinder的一方我们都可以当做服务端。如下例子:
val binder = object : IMyAidlInterface.Stub() {
override fun getStudentByName(name: String): String {
Log.d(
TAG,
"${Process.myPid()}-${Thread.currentThread().name} getStudentByName:${name} "
)
mCallBack?.onResult("get name start")
return name
}
override fun setCallBack(callBack: ICallBack) {
Log.d(TAG, "${Process.myPid()}-${Thread.currentThread().name} setCallBack:${callBack} ")
mCallBack = callBack
//监听客户端传过来的binder,监听客户端是否死亡
//此时我们可以把对应的客户端当成服务端
val binder = callBack.asBinder()
binder?.linkToDeath(object: IBinder.DeathRecipient {
override fun binderDied() {
}
}, 0)
}
override fun addStudent(name: String?) {
Thread.sleep(3000)
Log.d(TAG, "addStudent: $name")
}
5、SystemServer使用Binder的案例分析
5.1、获取IBinder的两种方式
我们知道在我们上面的例子跨进程通信,我们通过Service组件,通过binderService的方式,在onServiceConnected函数中拿到IBinder对象
var iMyAidlInterface: IMyAidlInterface? = null
val conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
然后再把对应的IBinder对象转换成对应的Prox代理接口
我们来看下SystemServer中是怎么获取IBinder的,我们知道在startActivity的时候,APP进程需要和SystemServer中的AMS通信。看如下的例子。
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
//通过ServiceManager.getService的方式获取IBinder
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
//将IBinder转换成对应的Prox代理接口
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
我们看到APP进程是通过ServiceManager.getService拿到AMS的IBinder对象的,然后后续也一样将IBinder转换成Prox代理对象
//通过ServiceManager.getService的方式获取IBinder
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
//将IBinder转换成对应的Prox代理接口
final IActivityManager am = IActivityManager.Stub.asInterface(b);
那我们就知道IActivityManager就应该是一个AIDL接口,并且里面包含了一个startActivity方法。我们来看看
。
5.2、AMS的注册方式
以上我们知道APP要拿到AMS的IBinder,需要通过ServiceManager.getService(Context.ACTIVITY_SERVICE);那AMS是什么时候注册到ServiceManager中的呢?我们在前面的文章提到,SystemServer启动之后,会启动我们的重要服务AMS,之后就会将AMS以"activity"这个名字注册到ServiceManager中, frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void setSystemProcess() {
...........
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
...........
}
之后我们便可以通过ServiceManager.getService(Context.ACTIVITY_SERVICE);的方式拿到AMS对应的IBinder,实现跨进程通信。