Android跨进程通信-AIDL

383 阅读6分钟

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);。

这样就调用到了我们自定义实现的方法

流程图:

image.png

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方法。我们来看看

433146B5-E985-478a-AAAD-970F1F178C15.png

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,实现跨进程通信。