Android跨进程通信

本文整理和引用他人的笔记,旨在个人复习使用。

参考链接:

https://blog.csdn.net/fanleiym/article/details/83894399

https://github.com/274942954/AndroidCollection/blob/master/Docs/Android%E7%9F%A5%E8%AF%86%E7%82%B9%E6%B1%87%E6%80%BB.md#%E8%BF%9B%E7%A8%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F

https://www.kaelli.com/4.html

https://carsonho.blog.csdn.net/article/details/73560642?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

1 Android多进程

默认情况下,一个app只会运行在一个进程中,进程名为app的包名。

1.1 多进程的优点

1. 分散内存的占用

Android系统对每个应用进程的内存占用是有限制的,占用内存越大的进程,被系统杀死的可能性就越大。使用多进程可以减少主进程占用的内存,避免OOM问题,降低被系统杀死的概率。

2. 实现多模块

一个成熟的应用一定是多模块化的。项目解耦,模块化,意味着开辟新的进程,有独立的JVM,带来数据解耦。模块之间互不干预,团队并行开发,同时责任分工也很明确。

3. 降低程序奔溃率

子进程崩溃不会影响主进程的运行,能降低程序的崩溃率。

4. 实现一些特殊功能

比如可以实现推送进程,使得主进程退出后,能离线完成消息推送服务。还可以实现守护进程,来唤醒主进程达到保活目的。还可以实现监控进程专门负责上报bug,进而提升用户体验。

1.2 android:process 属性

  • 四大组件均可在清单文件中通过设置 android:process 属性,使其运行在指定的进程中,以此实现多进程。
  • 设置Application的 android:process 属性可以修改应用程序的默认进程名(默认值为包名)。

1.3 公有进程和私有进程

android:process 属性的值以冒号开头的就是私有进程,否则就是公有进程。当然命名还需要符合规范,不能以数字开头等等。

  • 私有进程:为创建此进程的应用所独享,其他应用的组件无法跑在这个进程中。
  • 公有进程:也叫全局进程,为所有应用共享,其他应用通过设置相同的ShareUserId可以和它跑在同一个进程。

ShareUserId,用于应用间数据共享。在Android里面每个app都有一个唯一的linux user ID,因此应用程序的文件只对自身可见,对其他的应用程序不可见,标签中android:sharedUserId属性能设置应用的ShareUserId。具有相同的userID的两个apk能共享看到对方的文件。对具有相同ID的apk,系统有可能(并不一定)会为了节省资源,在一个linux进程中进行,共享一个虚拟机。

1.4 进程的生命周期

1. 前台进程

  • 托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
  • 托管某个 Service,后者绑定到用户正在交互的 Activity
  • 托管正在“前台”运行的 Service(服务的onCreate方法中调用 startForeground()
  • 托管正执行一个生命周期回调的 Service(onCreate()onStart()onDestroy()
  • 托管正执行其 onReceive() 方法的 BroadcastReceiver

2. 可见进程

  • 托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。
  • 托管绑定到可见(或前台)Activity 的 Service

3. 服务进程

  • 正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。

4. 后台进程

  • 包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。

5. 空进程

  • 不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

Android 会将进程评定为它可能达到的最高级别。另外服务于另一进程的进程其级别永远不会低于其所服务的进程。

1.5 多进程产生的问题

  • Application的重复创建

创建新的进程时会创建新的Application对象,而我们通常在Application的onCreate方法中只是完成一些全局的初始化操作,不需要多次执行。

解决思路:获取当前进程名,判断是否为主进程,只有主进程的时候才执行初始化操作

获取当前进程名的两种方法:

  1. 根据当前进程的pid获取进程名
fun getProcessName(context: Context, pid: Int): String? {
    val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val runningApps = am.runningAppProcesses ?: return null
    for (procInfo in runningApps) {
        if (procInfo.pid == pid) {
            return procInfo.processName
        }
    }
    return null
}
  1. 根据当前进程的cmdline文件的信息获取进程名
fun getProcessName(): String? {
    return try {
        val file = File("/proc/" + Process.myPid() + "/" + "cmdline")
        val mBufferedReader = BufferedReader(FileReader(file))
        val processName: String = mBufferedReader.readLine().trim()
        mBufferedReader.close()
        processName
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}
//cmdline文件包含进程的命令行参数,包括进程的启动路径(argv[0])。?

Application中判断是否是主进程(方法1例子):

val processName = getProcessName(this, Process.myPid())//根据进程id获取进程名
if (!TextUtils.isEmpty(processName) && processName.equals(this.packageName)) {
    //初始化逻辑
    ...
}
  • 静态成员和单例模式完全失效

    进程间的内存空间是相互隔离的,一个内存空间的值修改不影响另一个的值。

  • 线程同步机制失效

  • SharedPreferences 的可靠性下降

    多进程并发问题不好解决,应该尽量避免多进程并发操作,比如可以只让主进程进行对数据库的操作。

2 Serializable 和 Parcelable

Serializable 和 Parcelable是数据序列化的两种方式,Android中只有进行序列化过后的对象才能通过intent和Binder传递。

序列化是指将一个对象转换为可存储或可传输状态。

通常序列化后的对象完成传输后,通过反序列化获得的是一个新对象,而不是原来的对象。

2.1 Serializable接口

Serializable是java接口,位于java.io的路径下。Serializable的原理就是把Java对象序列化为二进制文件后进行传递。Serializable使用起来非常简单,只需直接实现该接口就可以了。

2.2 Parcelable接口

Parcelable是Google为了解决Serializable效率低下的问题,为Android特意设计的一个接口。Parcelable的原理是将一个对象完全分解,分解成可以传输的数据类型(如基本数据类型)再进行传递。

使用Parcelable的两种方法
  1. 实现Parcelable接口

    class Person() : Parcelable {
        var name = ""
        var age = 0
    
        constructor(parcel: Parcel) : this() {
            name = parcel.readString() ?:"" //读取name
            age = parcel.readInt() //读取age
        }
    
        override fun writeToParcel(parcel: Parcel, flags: Int) {
            parcel.writeString(name) //写出name
            parcel.writeInt(age) //写出age
        }
    
        /**
         * @return 返回当前对象的内容描述。如果含有文件描述符,返回 1,否则返回 0,几乎所有情况都返回 0。
         */
        override fun describeContents(): Int {
            return 0
        }
    
        companion object CREATOR : Parcelable.Creator {
            //通过parcel对象反序列化一个对象。
            //这里直接调用次构造函数,也可以不要该构造函数,改为将反序列化逻辑写到此方法。
            override fun createFromParcel(parcel: Parcel): Person {
                return Person(parcel)
            }
    
            //创建一个指定大小的Parcelable数组,每一项初始化为null
            //这里是默认写法,直接调用arrayOfNulls方法。
            override fun newArray(size: Int): Array {
                return arrayOfNulls(size)
            }
        }
    
    }
    

    Parcel 内部包装了可序列化的数据,可以在 Binder 中自由传输。序列化功能由 writeToParcel 方法完成,最终是通过 Parcel 中的一系列 write 方法完成。反序列化功能由 CREATOR 来完成,通过 Parcel 的一系列 read 方法来完成反序列化过程。

    注意:read与write的顺序必须是一致的,否则会出错。

  2. 使用@Parcelize注解

    使用@Parcelize注解需要在module的build.gradle文件中中配置两个地方

    plugins {
        ...
        id 'kotlin-android-extensions' //顺序上,必须在id 'kotlin-android'之后才行
    }
    android {
        ...
        androidExtensions {
            experimental = true
        }
    }
    

    需要把字段都放在主构造函数中,然后直接使用@Parcelize注解就能完成序列化,非常方便。

    @Parcelize
    class Person(var name: String, var age: Int) : Parcelable
    

2.3 Serializable 和 Parcelable的对比

  • Serializable 使用 I/O 读写存储在硬盘上,而 Parcelable 是直接在内存中读写

  • Serializable 会使用反射,在序列化的时候会创建许多的临时对象,容易触发垃圾回收,序列化和反序列化过程需要大量 I/O 操作,整体效率较低。而 Parcelable 自已实现封送和解封(marshalled &unmarshalled)操作不需要用反射,数据也存放在 Native 内存中,效率要快很多。

通常需要存到本地磁盘的数据就使用Serializable,其他情况就使用效率更高的Parcelable。

3 IPC

IPC 即 Inter-Process Communication (进程间通信)。Android 基于 Linux,而 Linux 出于安全考虑,不同进程间不能之间操作对方的数据,这叫做“进程隔离”。

在 Linux 系统中,通过虚拟内存机制来实现进程隔离。虚拟内存机制就是为每个进程在逻辑上分配了线性连续的内存空间,每个进程只管理自己的虚拟内存空间,因此从逻辑上实现了进程间的隔离。

PS:由操作系统负责将虚拟内存空间映射到物理内存空间,具体操作是cpu通过内存管理单元(MMU)将虚拟地址装换为物理地址。

每个进程的虚拟内存空间(进程空间)又被分为了用户空间和内核空间进程只能访问自身用户空间,只有操作系统能访问内核空间。

操作系统知识补充:

  • 在cpu所有指令中,有些指令是危险的、一旦错用就可能导致系统崩溃。出于安全考虑,cpu会将这类指令交给自己信任的程序(即操作系统的程序,在Linux中叫内核程序)去执行。如何做到呢?cpu有不同的特权级别,特权级别越高能执行的指令越多。intel x86 CPU中提供了Ring0~Ring3四种特权级,Ring0特权级最高。Linux系统中只使用了,Ring0和Ring3两个特权级,Ring0特权级对应内核态,Ring3对应用户态。内核态和用户态是从cpu的运行状态的角度。
  • 当运行用户程序时cpu会处于用户态,因此用户程序能执行的操作十分有限,但内核(操作系统)提供了一些封装好一定功能的接口,用户程序可以使用这些接口(这个过程叫系统调用),指使操作系统帮忙完成一些操作。系统调用本质就是用户程序中断,跳转到内核程序(此时cpu为内核态),完成指定操作后回到用户程序(cpu回到用户态)。
  • 通常32位Linux内核(2^32,即4G)会将进程的虚拟地址空间划分03G为用户空间,34G为内核空间。当cpu处于用户态时,只能访问用户空间,只有进入内核态才能访问内核空间。内核空间存放了内核程序,以及用到的数据等等。每个进程的内核空间映射到的是同一块物理地址,因此内核空间是所有进程共享的。

由于进程只能访问自身用户空间,因此在传统的IPC中,发送进程需要通过copy_from_user(系统调用)将数据从自身用户空间拷贝到内核空间,再由接受进程通过copy_to_user从内核空间复拷贝到自身用户空间,共需要拷贝2次,效率十分低下。Android采用的是Binder作为IPC的机制,只需复制一次。

4 Binder

Binder翻译过来是粘合剂,是进程之间的粘合剂。

4.1 为什么选用Binder作为IPC的机制?

  • 性能较好,管道、消息队列、Socket方式都需要2次数据拷贝,而Binder只需要1次数据拷贝,性能仅次于共享内存。
  • 缺点少,Binder基于C/S架构,架构十分清晰明了,相比于内存共享,其实现方式更为简单,且无需解决同步问题。
  • 安全性好,Android为每个安装好的应用程序分配了自己的UID,传统的 IPC 形式,无法识别对方的身份标识(UID/GID),而使用 Binder IPC 时,这些身份标示是跟随调用过程而自动传递的。Server 端很容易就可以知道 Client 端的身份,非常便于做安全检查。

4.2 Binder 1次拷贝的原理

Binder IPC通信的底层原理是通过内存映射(mmap),将接收进程的用户空间映射到内核空间,有了这个映射关系,接收进程就能通过用户空间的地址获得内核空间的数据,这样只需发送进程将数据拷贝到内核空间就可完成通讯。

一次完整的Binder IPC通信:

  1. 在内核空间创建一个数据接收缓存区。
  2. 在内核空间中开辟一块内核缓存区。
  3. 建立接收进程和数据接受缓存区的映射关系。
  4. 建立内核缓存区和数据接受缓存区的映射关系。
  5. 发送进程调用copy_from_user() 将数据copy到内核缓存区。

4.3 Binder机制

从IPC的角度看,Binder是一种跨进程通信机制(一种模型),Binder 是基于 C/S 架构的,这个通信机制中主要涉及四个角色:Client、Server、ServiceManager和Binder驱动。

角色 类比 位置 实现方式 作用
Client 客户端(浏览器) 用户空间 开发者实现 从ServiceManager中获取Server提供的服务
Server 服务器(网站) 用户空间 开发者实现 将服务注册到ServiceManager中
ServiceManager DNS服务器 用户空间 系统实现 管理服务的注册和查询
Binder驱动 路由器 内核空间 系统实现 Client、Server和ServiceManager之间的桥梁。

Client、Server、ServiceManager都是运行在用户空间的进程,他们通过系统调用(open、mmap 和 ioctl)来访问设备文件/dev/binder,从而实现与Binder驱动的交互。Binder驱动提供进程间通信的能力(负责完成一些底层操作,比如开辟数据接受缓存区等),是Client、Server和ServiceManager之间的桥梁。

Client、Server就是需要进行通信两个的进程,通信流程:

  1. 注册服务:Server进程创建一个Binder实体(携带了Server提供的各种接口方法),并为这个实体取了个字符名字,通过系统调用,Binder驱动会在内核创建这个实体,并建立ServiceManager对这个实体的应用,ServiceManager将字符名字和实体引用填入查找表中。
  2. 获得服务:Client进程根据字符名字请求获取服务,ServiceManager根据字符查找到Binder实体的引用,Binder驱动为这个实体创建代理对象并返回给Client进程(如果查找的是自己注册的服务则直接返回该Binder实体)。
  3. 使用服务:Client进程通过Binder驱动将数据发送到Server进程后自身阻塞,Binder驱动唤醒Server进程并运行完对应接口方法,然后Binder驱动唤醒阻塞的Client进程,再将执行结果返回给Client进程。(发送数据和返回执行结果分别对应了一次底层完整的Binder IPC通信)

细心的你一定发现了,注册服务和获得服务本身就是和ServiceManager进行跨进程通信。其实和ServiceManager的通信的过程也是获取Binder对象(早已创建在Binder驱动中,携带了注册和查询服务等接口方法)来使用,所有需要和ServiceManager通信的进程,只需通过0号引用,就可以获得这个Binder对象了。

4.4 代码分析

AIDL内部原理就是基于Binder的,可以借此来分析Binder的使用。

AIDL是接口定义语言,简短的几句话就能定义好一个复杂的、内部有一定功能的java接口。

先看看ICallBack.aidl文件,这里定义了一个接口,表示了服务端提供的功能。

// ICallBack.aidl
package com.zhy.aidltest.Server;

// Declare any non-default types here with import statements

interface ICallBack {
    void onSuccess(String result);

    void onFailed(String errorMsg);
}

被定义出来的java接口继承了IInterface接口,并且内部提供了一个Stub抽象类给服务端(相当于封装了一下,服务端只需继承这个类,然后完成功能的里面具体的实现)。

//定义了Binder对象提供的功能
public interface IInterface
{
    public IBinder asBinder();
}
package com.zhy.aidltest.Server;
// Declare any non-default types here with import statements

public interface ICallBack extends android.os.IInterface
{
  ...
  //一个继承了Binder类,并实现了ICallBack接口的抽象类
  //Binder类是一个继承了IBinder接口的java类,IBinder定义了跨进程通信的能力,Binder是它的实现。
  public static abstract class Stub extends android.os.Binder implements com.zhy.aidltest.Server.ICallBack
  {
    private static final java.lang.String DESCRIPTOR = "com.zhy.aidltest.Server.ICallBack";//关联ICallBack接口的字符
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);//为IBinder对象绑定一个接口对象,并关联字符(用于到时将IBinder对象装换成接口对象)
    }
    public static com.zhy.aidltest.Server.ICallBack asInterface(android.os.IBinder obj)
    {//将IBinder对象【【【Binder或BinderProxy对象】】】装换成接口对象的方法()
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//如果是BinderProxy对象直接返回null,如果是Binder对象则返回接口对象
      if (((iin!=null)&&(iin instanceof com.zhy.aidltest.Server.ICallBack))) {
        return ((com.zhy.aidltest.Server.ICallBack)iin);//是Binder对象
      }
      return new com.zhy.aidltest.Server.ICallBack.Stub.Proxy(obj);//获取不到接口对象的话,返回一个代理接口对象。
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    //Binder类的onTransact是最终会被调用的方法,重写此方法来处理
    @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)//根据code码调用自身接口方法
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_onSuccess:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.onSuccess(_arg0);//调用onSuccess方法(服务端继承Stub后需要具体实现)
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_onFailed:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.onFailed(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.zhy.aidltest.Server.ICallBack
    {//客户端的封装,免去了劳烦的打包数据过程。使得客户端调用形式看起来和服务端一模一样。
      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 void onSuccess(java.lang.String result) 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.writeString(result);
          boolean _status = mRemote.transact(Stub.TRANSACTION_onSuccess, _data, _reply, 0);//BinderProxy的transact内部调用本地方法transactNative(code, data, reply, flags)
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().onSuccess(result);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void onFailed(java.lang.String errorMsg) 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.writeString(errorMsg);
          boolean _status = mRemote.transact(Stub.TRANSACTION_onFailed, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().onFailed(errorMsg);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.zhy.aidltest.Server.ICallBack sDefaultImpl;
    }
    static final int TRANSACTION_onSuccess = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_onFailed = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.zhy.aidltest.Server.ICallBack 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.zhy.aidltest.Server.ICallBack getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void onSuccess(java.lang.String result) throws android.os.RemoteException;
  public void onFailed(java.lang.String errorMsg) throws android.os.RemoteException;
}

5 AIDL使用

参考:https://www.cnblogs.com/sqchen/p/10660939.html

(以下是添加了回调的最终实现,可以看参考链接一步一步来)

为需要使用的类,创建aidl文件。

// Student.aidl
package com.zhy.aidltest.Server;

parcelable Student;

系统会自动在main文件下生成aidl文件夹,并在该文件夹下创建相应目录。

image-20210127145813899

在java相同路径下创建Student类,这里不能使用@Parcelize注解,否则会报错

package com.zhy.aidltest.Server

import android.os.Parcel
import android.os.Parcelable

class Student(var id:Int=0, var name:String=""):Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readInt(),
        parcel.readString()?:""
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeString(name)
    }

    override fun describeContents(): Int {
        return 0
    }

    fun readFromParcel(parcel: Parcel): Student{//当Student可作为out时需要实现
        return Student(parcel.readInt(),parcel.readString()?:"")
    }

    companion object CREATOR : Parcelable.Creator {
        override fun createFromParcel(parcel: Parcel): Student {
            return Student(parcel)
        }

        override fun newArray(size: Int): Array {
            return arrayOfNulls(size)
        }
    }
}

创建IStudentService.aidl,定义了一个接口,该接口定义了服务端提供的功能。创建完后rebuild一下项目(每次创建和修改定义接口文件都要rebuild一下)

// IStudentService.aidl
package com.zhy.aidltest.Server;

// Declare any non-default types here with import statements
import com.zhy.aidltest.Server.Student;
import com.zhy.aidltest.Server.ICallBack;

interface IStudentService {
    List getStudentList();

    void addStudent(inout Student student);

    void register(ICallBack callback);

    void unregister(ICallBack callback);
}
定向 tag 含义
in 数据只能由客户端流向服务端,服务端将会收到客户端对象的完整数据,客户端对象不会因为服务端对传参的修改而发生变动。
out 数据只能由服务端流向客户端,服务端将会收到客户端对象,该对象不为空,但是它里面的字段为空,但是在服务端对该对象作任何修改之后客户端的传参对象都会同步改动。
inout 服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

创建在服务端的StudentService

package com.zhy.aidltest.Server

class StudentService : Service() { //服务端
    private val mStuList = CopyOnWriteArrayList()
    private val sCallbackList = RemoteCallbackList()

    private var mBinder = object: IStudentService.Stub() { //服务端提供接口的具体实现
        override fun register(callback: ICallBack?) {
            callback.apply { sCallbackList.register(this) }
        }

        override fun addStudent(student: Student?){
            student?.apply {
                mStuList.add(this)
                dispatchResult(true,"add succeeded")
                return
            }
            dispatchResult(false,"add failed")
        }

        override fun unregister(callback: ICallBack?) {
            callback.apply { sCallbackList.unregister(callback) }
        }

        override fun getStudentList(): MutableList {
            return mStuList
        }
    }

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

    /**
     * 分发结果
     * @param result
     * @param msg
     */
    private fun dispatchResult(result: Boolean, msg: String) { //执行所有注册了的回调
        val length = sCallbackList.beginBroadcast()
        for (i in 0 until length) {
            val callback: ICallBack = sCallbackList.getBroadcastItem(i)
            try {
                if (result) {
                    callback.onSuccess(msg)
                } else {
                    callback.onFailed(msg)
                }
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
        sCallbackList.finishBroadcast() //beginBroadcast()后必须调用
    }
}

可以看见有回调,说明客户端也提供了接口给服务端来回调(双向通信,此时客户端的变成了服务端),即ICallBack.aidl

// ICallBack.aidl
package com.zhy.aidltest.Server;

// Declare any non-default types here with import statements

interface ICallBack {
    void onSuccess(String result);

    void onFailed(String errorMsg);
}

客户端是通过Binder驱动返回的Binder调用StudentService里的具体实现方法

class MainActivity : AppCompatActivity(), View.OnClickListener {//客户端
    companion object{
        private const val TAG = "MainActivity"
    }

    private lateinit var mViewBinding: ActivityMainBinding
    private lateinit var mStudentService:IStudentService
    private val mDeathRecipient = object :DeathRecipient {//Binder死亡的回调
        override fun binderDied() {
            //解除死亡代理
            mStudentService.asBinder().unlinkToDeath(this, 0);
            //重新绑定服务
            //bindStudentService()
        }
    }
    private val mServiceConnection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mStudentService = IStudentService.Stub.asInterface(service) //由Binder对象返回IStudentService对象
            try {
                mStudentService.apply {
                    asBinder().linkToDeath(mDeathRecipient, 0) //设置死亡代理
                    register(mCallback) //调用Server提供的register方法
                }
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
    }
    private val mCallback = object:ICallBack.Stub(){ //提供给Server回调的具体实现
        override fun onFailed(errorMsg: String?) {
            Log.d(TAG, "onFailed: $errorMsg")
        }

        override fun onSuccess(result: String?) {
            Log.d(TAG, "onSuccess: $result")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mViewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mViewBinding.root)
        mViewBinding.apply {
            btnBind.setOnClickListener(this@MainActivity)
            btnAddStudent.setOnClickListener(this@MainActivity)
            btnGetStudentList.setOnClickListener(this@MainActivity)
            btnUnBind.setOnClickListener(this@MainActivity)
        }
        startService(Intent(this,StudentService::class.java))
    }

    override fun onClick(v: View?) {
        mViewBinding.apply {
            when(v?.id){
                btnBind.id->{
                    bindStudentService()
                }
                btnAddStudent.id->{
                    mStudentService.addStudent(Student(1,"zhy"))
                }
                btnGetStudentList.id->{
                    mStudentService.studentList
                }
                btnUnBind.id->{
                    unbindService(mServiceConnection)
                }
            }
        }
    }

    override fun onDestroy() {
        unbindService(mServiceConnection)
        stopService(Intent(this,StudentService::class.java))
        super.onDestroy()
    }

    private fun bindStudentService(){
        bindService(Intent(this@MainActivity,StudentService::class.java),mServiceConnection,
            Context.BIND_AUTO_CREATE)
    }
}



    

AIDL使用注意:

  • 接口名和aidl文件名相同

  • 接口和方法前不加访问权限修饰符,也不能用final,static。

  • Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、 CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或返回值,必须实现Parcelable接口。

  • 自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。

  • 在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。

  • Java原始类型默认的标记为in,不能为其它标记。

6 Messenger

Messenger可以在不同进程中传递 Message 对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger 是一种轻量级的 IPC 方案,是对AIDL的封装,底层实现是 AIDL。

使用详见:https://blog.csdn.net/qq951127336/article/details/90678698

你可能感兴趣的:(Android跨进程通信)