Binder(1)--App使用Binder通信.md

参考资料

  1. Android Binder详解 https://mr-cao.gitbooks.io/android/content/android-binder.html

简介

在初学Android的时候,一般是从四大组件开始学起的。最开始学,是通过追代码流程的方式快速熟悉Android系统框架。

在这个速学的过程中,很多细节部分就被忽略掉了(这也是必须的,否则学起来极其痛苦且缓慢)。

比如四大组件之一的Service,在跨进程通信的时候,我们只知道是通过Binder通信的。至于内部实现是如何就不甚了了。

接下来我们通过一个简单的Demo深入探究这个跨进程通信的过程。

一. 在App之间使用Binder通信

目标是在AppClient中给AppServer发送一个简单的字符串

1.1 创建Interface

首先在服务端提供接口文件:IDemoInterface.

Android Studio中通过"File->New->AIDL"创建这个 aidl 文件。

interface IDemoInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void sayHello(long aLong, String aString);
}

创建之后需要在 “Build->Rebuild Project”, 生成对应可用的文件

1.2 实现服务端接口

class DemoService : Service() {
    val mName = "DemoService"

    class DemoBinder:IDemoInterface.Stub() {
        override fun sayHello(aLong: Long, aString: String?) {
            Log.d("DemoService", "$aString:$aLong")
        }
    }

    private val binder = DemoBinder()

    override fun onBind(intent: Intent?): IBinder? {
        return binder
    }
}

很简单,就是将传入的字符串输出出来。但是这个地方就有一个疑问,为啥是继承IDemoInterface.Stub呢?直接看这个文件的内容:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.oneplus.opbench.server;
// Declare any non-default types here with import statements

public interface IDemoInterface extends android.os.IInterface
{
  /** Default implementation for IDemoInterface. */
  public static class Default implements com.oneplus.opbench.server.IDemoInterface
  {
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    @Override public void sayHello(long aLong, java.lang.String aString) 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.oneplus.opbench.server.IDemoInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.oneplus.opbench.server.IDemoInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.oneplus.opbench.server.IDemoInterface interface,
     * generating a proxy if needed.
     */
    public static com.oneplus.opbench.server.IDemoInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.oneplus.opbench.server.IDemoInterface))) {
        return ((com.oneplus.opbench.server.IDemoInterface)iin);
      }
      return new com.oneplus.opbench.server.IDemoInterface.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_sayHello:
        {
          data.enforceInterface(descriptor);
          long _arg0;
          _arg0 = data.readLong();
          java.lang.String _arg1;
          _arg1 = data.readString();
          this.sayHello(_arg0, _arg1);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.oneplus.opbench.server.IDemoInterface
    {
      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;
      }
      /**
           * Demonstrates some basic types that you can use as parameters
           * and return values in AIDL.
           */
      @Override public void sayHello(long aLong, java.lang.String aString) 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.writeLong(aLong);
          _data.writeString(aString);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().sayHello(aLong, aString);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.oneplus.opbench.server.IDemoInterface sDefaultImpl;
    }
    static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    public static boolean setDefaultImpl(com.oneplus.opbench.server.IDemoInterface impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.oneplus.opbench.server.IDemoInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  /**
       * Demonstrates some basic types that you can use as parameters
       * and return values in AIDL.
       */
  public void sayHello(long aLong, java.lang.String aString) throws android.os.RemoteException;
}

注释的就很清楚,是自动生成的文件。Stub是一个静态内部抽象类,继承了Binder和接口IDemoInterface。

1.3 客户端调用

注意把服务端aidl文件夹内容拷贝到客户端项目的aidl文件内,然后客户端也rebuild一下project.

/*
        <!-- for binder(aidl) -->
        <service android:name=".server.DemoService"
            android:process=".DemoService"
            android:enabled="true"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.DemoService"/>
            </intent-filter>
        </service>
*/
  private fun attemptToBindService() {
      val intent = Intent()
      Log.e("client", " connected now")
      intent.action = "android.intent.action.DemoService" //服务类的Action
      intent.`package` = "com.oneplus.opbench" //服务端包名
      // 建立通信
      bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
  }

  private val mDemoServiceConnection = object : ServiceConnection {
      override fun onBindingDied(name: ComponentName?) {
          Log.d("Client", "DemoService died!")
      }

      override fun onServiceDisconnected(p0: ComponentName?) {
          Log.d("Client", "DemoService disconnected!")
      }

      override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
          Log.d("Client", "DemoService connected")
          // 远程服务连接成功,打个招呼
          val mProxyBinder = IDemoInterface.Stub.asInterface(p1)
          try {
              mProxyBinder.sayHello(5000, "Hello?")
          } catch (e:RemoteException) {

          }
      }
  }

相关类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9MJC3VO-1605791864261)(res/1_1_DemoInterface类图.png)]

接下来我们从客户端的bindService建立远程连接开始看看是怎么通信的,当然这里我们重点是binder通信,而不是service的bind流程。

二. 通信过程

分析跨进程通信的过程,一定要时刻谨记当前代码所处的进程哦, 为了方便和聚焦, 忽略非紧要代码。

2.1 Client=>ContextImpl.bindService

    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
                getUser());
    }

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            String instanceName, Handler handler, Executor executor, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        // ......
        if (mPackageInfo != null) {
            if (executor != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
            } else {
                // 2.1.1 获取IServiceConnection对象
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            }
        }
        // .......
            // 2.2 看来是先与Android系统中的AMS服务通信
            int res = ActivityManager.getService().bindIsolatedService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
        // ......
    }

2.1.1 LoadedApk.getServiceDispatcher

    @UnsupportedAppUsage
    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        return getServiceDispatcherCommon(c, context, handler, null, flags);
    }
  
    private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
            Context context, Handler handler, Executor executor, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
                sd = map.get(c);
            }
            if (sd == null) {
                if (executor != null) {
                    sd = new ServiceDispatcher(c, context, executor, flags);
                } else {
                    // 第一次建立连接,当然需要新建
                    sd = new ServiceDispatcher(c, context, handler, flags);
                }
                if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
                if (map == null) {
                    map = new ArrayMap<>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler, executor);
            }
            // 返回的是ServiceDispatcher里的mIServiceConnection对象,是InnerConnection
            return sd.getIServiceConnection();
        }
    }

这里就是将ServiceConnection做一个打包,存起来,隐藏细节,注意到返回的是 InnerConnection 类型哦。
相关类图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7UzTr2X-1605791864263)(res/1_2_InnerConnection类图.png)]

2.2 SytemServer=>AMS.bindIsolatedService

这里其实就存在Binder通信,但是我们先不看,假设直接call过来了(四大组件之Service).

我们知道,当服务端app进程没有启动时,会先将进程启动,然后继续进行bindService操作,为了方便,这里假设服务端进程已启动。

这里忽略中间一系列调用,走到ActiveServices.bindServiceLocked中:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String instanceName, String callingPackage, final int userId)
            throws TransactionTooLargeException {
    // ......
    // 注意这里传入的connection是IServiceConnection
    // retrieveServiceLocked是从已安装的package中找到对应包以及指定Service组件
    ServiceLookupResult res =
            retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
                    callerFg, isBindExternal, allowInstant);
    // ......
    ServiceRecord s = res.record;
    // ......
        // 根据我们之前的研究过的Service知识,这个AppBindRecord就是记录App之间Service通信的
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent,
                callerApp.uid, callerApp.processName, callingPackage);
        // 注意这里的connection.asBinder调用的就是IServiceConnection.Stub.Proxy.asBinder
        // 返回的就是mRemote
        IBinder binder = connection.asBinder();
        // ......
        if (s.app != null && b.intent.received) {
          // Service is already running, so we can immediately
          // publish the connection.
          try {
              // c.conn就是Client端传入的IServiceConnection
              // 所以这里又调回去Client进程了,注意传入的b.intent.binder对象
              // 这个传入的b.intent.binder对象就是服务端App发布Service时传入的(publishServiceLocked)
              // 其实可以猜得到是这个就是对应服务端App的IDemoInterface对象
              // 2.3 转到Client进程
              c.conn.connected(s.name, b.intent.binder, false);
          } catch (Exception e) {
              Slog.w(TAG, "Failure sending service " + s.shortInstanceName
                      + " to connection " + c.conn.asBinder()
                      + " (in " + c.binding.client.processName + ")", e);
          }
}

2.3 Client=>LoadedApk.ServiceDispatcher.InnerConnection.connected

public void connected(ComponentName name, IBinder service, boolean dead)
        throws RemoteException {
    LoadedApk.ServiceDispatcher sd = mDispatcher.get();
    if (sd != null) {
        sd.connected(name, service, dead);
    }
}

2.3.1 LoadedApk.ServiceDispatcher.connected

public void connected(ComponentName name, IBinder service, boolean dead) {
    if (mActivityExecutor != null) {
        mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
    } else if (mActivityThread != null) {
        // 2.3.2 这里其实最后还是调用到了doConnected
        mActivityThread.post(new RunConnection(name, service, 0, dead));
    } else {
        doConnected(name, service, dead);
    }
}

2.3.2 LoadedApk.ServiceDispatcher.doConnected

public void doConnected(ComponentName name, IBinder service, boolean dead) {
    // ......

    // 这个service就是服务端App中的DemoBinder中的mRemote了
    if (service != null) {
        // mDemoServiceConnection通知已经建立连接
        mConnection.onServiceConnected(name, service);
    } else {
        // The binding machinery worked, but the remote returned null from onBind().
        mConnection.onNullBinding(name);
    }
}

2.3.3 客户端的ServiceConnection

override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
    Log.d("Client", "DemoService connected")
    // 远程服务连接成功,打个招呼
    val mProxyBinder = IDemoInterface.Stub.asInterface(p1)
    try {
        mProxyBinder.sayHello(5000, "Hello?")
    } catch (e:RemoteException) {

    }
}

到这里,Android系统中两个进程通过四大组件之一的Service进行跨进程通信的连接已经建立了。

简单来说这个过程借助了SystemServer的帮助:Client app <–> SystemServer <–> Server app

Server App将Service的IBinder保存在SystemServer中,在Client App通过bindService的时候,传入。这样Client App就有了和Server App通信的基础。

搞清楚是怎么建立连接的过程,接下来深入探究下mProxyBinder.sayHello调用到不同进程对应的方法的细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值