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 多進程的優(yōu)點

1. 分散內(nèi)存的占用

Android系統(tǒng)對每個應用進程的內(nèi)存占用是有限制的,占用內(nèi)存越大的進程,被系統(tǒng)殺死的可能性就越大。使用多進程可以減少主進程占用的內(nèi)存,避免OOM問題,降低被系統(tǒng)殺死的概率。

2. 實現(xiàn)多模塊

一個成熟的應用一定是多模塊化的。項目解耦,模塊化,意味著開辟新的進程,有獨立的JVM,帶來數(shù)據(jù)解耦。模塊之間互不干預,團隊并行開發(fā),同時責任分工也很明確。

3. 降低程序奔潰率

子進程崩潰不會影響主進程的運行,能降低程序的崩潰率。

4. 實現(xiàn)一些特殊功能

比如可以實現(xiàn)推送進程,使得主進程退出后,能離線完成消息推送服務。還可以實現(xiàn)守護進程,來喚醒主進程達到?;钅康?。還可以實現(xiàn)監(jiān)控進程專門負責上報bug,進而提升用戶體驗。

1.2 android:process 屬性

  • 四大組件均可在清單文件中通過設(shè)置 android:process 屬性,使其運行在指定的進程中,以此實現(xiàn)多進程。
  • 設(shè)置Application的 android:process 屬性可以修改應用程序的默認進程名(默認值為包名)。

1.3 公有進程和私有進程

android:process 屬性的值以冒號開頭的就是私有進程,否則就是公有進程。當然命名還需要符合規(guī)范,不能以數(shù)字開頭等等。

  • 私有進程:為創(chuàng)建此進程的應用所獨享,其他應用的組件無法跑在這個進程中。
  • 公有進程:也叫全局進程,為所有應用共享,其他應用通過設(shè)置相同的ShareUserId可以和它跑在同一個進程。

ShareUserId,用于應用間數(shù)據(jù)共享。在Android里面每個app都有一個唯一的linux user ID,因此應用程序的文件只對自身可見,對其他的應用程序不可見,<manifest>標簽中android:sharedUserId屬性能設(shè)置應用的ShareUserId。具有相同的userID的兩個apk能共享看到對方的文件。對具有相同ID的apk,系統(tǒng)有可能(并不一定)會為了節(jié)省資源,在一個linux進程中進行,共享一個虛擬機。

1.4 進程的生命周期

1. 前臺進程

  • 托管用戶正在交互的 Activity(已調(diào)用 Activity 的 onResume() 方法)
  • 托管某個 Service,后者綁定到用戶正在交互的 Activity
  • 托管正在“前臺”運行的 Service(服務的onCreate方法中調(diào)用 startForeground()
  • 托管正執(zhí)行一個生命周期回調(diào)的 Service(onCreate()onStart()onDestroy()
  • 托管正執(zhí)行其 onReceive() 方法的 BroadcastReceiver

2. 可見進程

  • 托管不在前臺、但仍對用戶可見的 Activity(已調(diào)用其 onPause() 方法)。
  • 托管綁定到可見(或前臺)Activity 的 Service

3. 服務進程

  • 正在運行已使用 startService() 方法啟動的服務且不屬于上述兩個更高類別進程的進程。

4. 后臺進程

  • 包含目前對用戶不可見的 Activity 的進程(已調(diào)用 Activity 的 onStop() 方法)。通常會有很多后臺進程在運行,因此它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最后一個被終止。

5. 空進程

  • 不含任何活動應用組件的進程。保留這種進程的的唯一目的是用作緩存,以縮短下次在其中運行組件所需的啟動時間。 為使總體系統(tǒng)資源在進程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會終止這些進程。

Android 會將進程評定為它可能達到的最高級別。另外服務于另一進程的進程其級別永遠不會低于其所服務的進程。

1.5 多進程產(chǎn)生的問題

  • Application的重復創(chuàng)建

創(chuàng)建新的進程時會創(chuàng)建新的Application對象,而我們通常在Application的onCreate方法中只是完成一些全局的初始化操作,不需要多次執(zhí)行。

解決思路:獲取當前進程名,判斷是否為主進程,只有主進程的時候才執(zhí)行初始化操作

獲取當前進程名的兩種方法:

  1. 根據(jù)當前進程的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. 根據(jù)當前進程的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文件包含進程的命令行參數(shù),包括進程的啟動路徑(argv[0])。?

Application中判斷是否是主進程(方法1例子):

val processName = getProcessName(this, Process.myPid())//根據(jù)進程id獲取進程名
if (!TextUtils.isEmpty(processName) && processName.equals(this.packageName)) {
    //初始化邏輯
    ...
}
  • 靜態(tài)成員和單例模式完全失效

    進程間的內(nèi)存空間是相互隔離的,一個內(nèi)存空間的值修改不影響另一個的值。

  • 線程同步機制失效

  • SharedPreferences 的可靠性下降

    多進程并發(fā)問題不好解決,應該盡量避免多進程并發(fā)操作,比如可以只讓主進程進行對數(shù)據(jù)庫的操作。

2 Serializable 和 Parcelable

Serializable 和 Parcelable是數(shù)據(jù)序列化的兩種方式,Android中只有進行序列化過后的對象才能通過intent和Binder傳遞。

序列化是指將一個對象轉(zhuǎn)換為可存儲或可傳輸狀態(tài)。

通常序列化后的對象完成傳輸后,通過反序列化獲得的是一個新對象,而不是原來的對象。

2.1 Serializable接口

Serializable是java接口,位于java.io的路徑下。Serializable的原理就是把Java對象序列化為二進制文件后進行傳遞。Serializable使用起來非常簡單,只需直接實現(xiàn)該接口就可以了。

2.2 Parcelable接口

Parcelable是Google為了解決Serializable效率低下的問題,為Android特意設(shè)計的一個接口。Parcelable的原理是將一個對象完全分解,分解成可以傳輸?shù)臄?shù)據(jù)類型(如基本數(shù)據(jù)類型)再進行傳遞。

使用Parcelable的兩種方法
  1. 實現(xiàn)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 返回當前對象的內(nèi)容描述。如果含有文件描述符,返回 1,否則返回 0,幾乎所有情況都返回 0。
         */
        override fun describeContents(): Int {
            return 0
        }
    
        companion object CREATOR : Parcelable.Creator<Person> {
            //通過parcel對象反序列化一個對象。
            //這里直接調(diào)用次構(gòu)造函數(shù),也可以不要該構(gòu)造函數(shù),改為將反序列化邏輯寫到此方法。
            override fun createFromParcel(parcel: Parcel): Person {
                return Person(parcel)
            }
    
            //創(chuàng)建一個指定大小的Parcelable數(shù)組,每一項初始化為null
            //這里是默認寫法,直接調(diào)用arrayOfNulls方法。
            override fun newArray(size: Int): Array<Person?> {
                return arrayOfNulls(size)
            }
        }
    
    }
    

    Parcel 內(nèi)部包裝了可序列化的數(shù)據(jù),可以在 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
        }
    }
    

    需要把字段都放在主構(gòu)造函數(shù)中,然后直接使用@Parcelize注解就能完成序列化,非常方便。

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

2.3 Serializable 和 Parcelable的對比

  • Serializable 使用 I/O 讀寫存儲在硬盤上,而 Parcelable 是直接在內(nèi)存中讀寫

  • Serializable 會使用反射,在序列化的時候會創(chuàng)建許多的臨時對象,容易觸發(fā)垃圾回收,序列化和反序列化過程需要大量 I/O 操作,整體效率較低。而 Parcelable 自已實現(xiàn)封送和解封(marshalled &unmarshalled)操作不需要用反射,數(shù)據(jù)也存放在 Native 內(nèi)存中,效率要快很多。

通常需要存到本地磁盤的數(shù)據(jù)就使用Serializable,其他情況就使用效率更高的Parcelable。

3 IPC

IPC 即 Inter-Process Communication (進程間通信)。Android 基于 Linux,而 Linux 出于安全考慮,不同進程間不能之間操作對方的數(shù)據(jù),這叫做“進程隔離”。

在 Linux 系統(tǒng)中,通過虛擬內(nèi)存機制來實現(xiàn)進程隔離。虛擬內(nèi)存機制就是為每個進程在邏輯上分配了線性連續(xù)的內(nèi)存空間,每個進程只管理自己的虛擬內(nèi)存空間,因此從邏輯上實現(xiàn)了進程間的隔離。

PS:由操作系統(tǒng)負責將虛擬內(nèi)存空間映射到物理內(nèi)存空間,具體操作是cpu通過內(nèi)存管理單元(MMU)將虛擬地址裝換為物理地址。

每個進程的虛擬內(nèi)存空間(進程空間)又被分為了用戶空間和內(nèi)核空間,進程只能訪問自身用戶空間,只有操作系統(tǒng)能訪問內(nèi)核空間。

操作系統(tǒng)知識補充:

  • 在cpu所有指令中,有些指令是危險的、一旦錯用就可能導致系統(tǒng)崩潰。出于安全考慮,cpu會將這類指令交給自己信任的程序(即操作系統(tǒng)的程序,在Linux中叫內(nèi)核程序)去執(zhí)行。如何做到呢?cpu有不同的特權(quán)級別,特權(quán)級別越高能執(zhí)行的指令越多。intel x86 CPU中提供了Ring0~Ring3四種特權(quán)級,Ring0特權(quán)級最高。Linux系統(tǒng)中只使用了,Ring0和Ring3兩個特權(quán)級,Ring0特權(quán)級對應內(nèi)核態(tài),Ring3對應用戶態(tài)。內(nèi)核態(tài)和用戶態(tài)是從cpu的運行狀態(tài)的角度。
  • 當運行用戶程序時cpu會處于用戶態(tài),因此用戶程序能執(zhí)行的操作十分有限,但內(nèi)核(操作系統(tǒng))提供了一些封裝好一定功能的接口,用戶程序可以使用這些接口(這個過程叫系統(tǒng)調(diào)用),指使操作系統(tǒng)幫忙完成一些操作。系統(tǒng)調(diào)用本質(zhì)就是用戶程序中斷,跳轉(zhuǎn)到內(nèi)核程序(此時cpu為內(nèi)核態(tài)),完成指定操作后回到用戶程序(cpu回到用戶態(tài))。
  • 通常32位Linux內(nèi)核(2^32,即4G)會將進程的虛擬地址空間劃分03G為用戶空間,34G為內(nèi)核空間。當cpu處于用戶態(tài)時,只能訪問用戶空間,只有進入內(nèi)核態(tài)才能訪問內(nèi)核空間。內(nèi)核空間存放了內(nèi)核程序,以及用到的數(shù)據(jù)等等。每個進程的內(nèi)核空間映射到的是同一塊物理地址,因此內(nèi)核空間是所有進程共享的。

由于進程只能訪問自身用戶空間,因此在傳統(tǒng)的IPC中,發(fā)送進程需要通過copy_from_user(系統(tǒng)調(diào)用)將數(shù)據(jù)從自身用戶空間拷貝到內(nèi)核空間,再由接受進程通過copy_to_user從內(nèi)核空間復拷貝到自身用戶空間,共需要拷貝2次,效率十分低下。Android采用的是Binder作為IPC的機制,只需復制一次。

4 Binder

Binder翻譯過來是粘合劑,是進程之間的粘合劑。

4.1 為什么選用Binder作為IPC的機制?

  • 性能較好,管道、消息隊列、Socket方式都需要2次數(shù)據(jù)拷貝,而Binder只需要1次數(shù)據(jù)拷貝,性能僅次于共享內(nèi)存。
  • 缺點少,Binder基于C/S架構(gòu),架構(gòu)十分清晰明了,相比于內(nèi)存共享,其實現(xiàn)方式更為簡單,且無需解決同步問題。
  • 安全性好,Android為每個安裝好的應用程序分配了自己的UID,傳統(tǒng)的 IPC 形式,無法識別對方的身份標識(UID/GID),而使用 Binder IPC 時,這些身份標示是跟隨調(diào)用過程而自動傳遞的。Server 端很容易就可以知道 Client 端的身份,非常便于做安全檢查。

4.2 Binder 1次拷貝的原理

Binder IPC通信的底層原理是通過內(nèi)存映射(mmap),將接收進程的用戶空間映射到內(nèi)核空間,有了這個映射關(guān)系,接收進程就能通過用戶空間的地址獲得內(nèi)核空間的數(shù)據(jù),這樣只需發(fā)送進程將數(shù)據(jù)拷貝到內(nèi)核空間就可完成通訊。

一次完整的Binder IPC通信:

  1. 在內(nèi)核空間創(chuàng)建一個數(shù)據(jù)接收緩存區(qū)。
  2. 在內(nèi)核空間中開辟一塊內(nèi)核緩存區(qū)。
  3. 建立接收進程和數(shù)據(jù)接受緩存區(qū)的映射關(guān)系。
  4. 建立內(nèi)核緩存區(qū)和數(shù)據(jù)接受緩存區(qū)的映射關(guān)系。
  5. 發(fā)送進程調(diào)用copy_from_user() 將數(shù)據(jù)copy到內(nèi)核緩存區(qū)。

4.3 Binder機制

從IPC的角度看,Binder是一種跨進程通信機制(一種模型),Binder 是基于 C/S 架構(gòu)的,這個通信機制中主要涉及四個角色:Client、Server、ServiceManager和Binder驅(qū)動。

角色 類比 位置 實現(xiàn)方式 作用
Client 客戶端(瀏覽器) 用戶空間 開發(fā)者實現(xiàn) 從ServiceManager中獲取Server提供的服務
Server 服務器(網(wǎng)站) 用戶空間 開發(fā)者實現(xiàn) 將服務注冊到ServiceManager中
ServiceManager DNS服務器 用戶空間 系統(tǒng)實現(xiàn) 管理服務的注冊和查詢
Binder驅(qū)動 路由器 內(nèi)核空間 系統(tǒng)實現(xiàn) Client、Server和ServiceManager之間的橋梁。

Client、Server、ServiceManager都是運行在用戶空間的進程,他們通過系統(tǒng)調(diào)用(open、mmap 和 ioctl)來訪問設(shè)備文件/dev/binder,從而實現(xiàn)與Binder驅(qū)動的交互。Binder驅(qū)動提供進程間通信的能力(負責完成一些底層操作,比如開辟數(shù)據(jù)接受緩存區(qū)等),是Client、Server和ServiceManager之間的橋梁。

Client、Server就是需要進行通信兩個的進程,通信流程:

  1. 注冊服務:Server進程創(chuàng)建一個Binder實體(攜帶了Server提供的各種接口方法),并為這個實體取了個字符名字,通過系統(tǒng)調(diào)用,Binder驅(qū)動會在內(nèi)核創(chuàng)建這個實體,并建立ServiceManager對這個實體的應用,ServiceManager將字符名字和實體引用填入查找表中。
  2. 獲得服務:Client進程根據(jù)字符名字請求獲取服務,ServiceManager根據(jù)字符查找到Binder實體的引用,Binder驅(qū)動為這個實體創(chuàng)建代理對象并返回給Client進程(如果查找的是自己注冊的服務則直接返回該Binder實體)。
  3. 使用服務:Client進程通過Binder驅(qū)動將數(shù)據(jù)發(fā)送到Server進程后自身阻塞,Binder驅(qū)動喚醒Server進程并運行完對應接口方法,然后Binder驅(qū)動喚醒阻塞的Client進程,再將執(zhí)行結(jié)果返回給Client進程。(發(fā)送數(shù)據(jù)和返回執(zhí)行結(jié)果分別對應了一次底層完整的Binder IPC通信)

細心的你一定發(fā)現(xiàn)了,注冊服務和獲得服務本身就是和ServiceManager進行跨進程通信。其實和ServiceManager的通信的過程也是獲取Binder對象(早已創(chuàng)建在Binder驅(qū)動中,攜帶了注冊和查詢服務等接口方法)來使用,所有需要和ServiceManager通信的進程,只需通過0號引用,就可以獲得這個Binder對象了。

4.4 代碼分析

AIDL內(nèi)部原理就是基于Binder的,可以借此來分析Binder的使用。

AIDL是接口定義語言,簡短的幾句話就能定義好一個復雜的、內(nèi)部有一定功能的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接口,并且內(nèi)部提供了一個Stub抽象類給服務端(相當于封裝了一下,服務端只需繼承這個類,然后完成功能的里面具體的實現(xiàn))。

//定義了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類,并實現(xiàn)了ICallBack接口的抽象類
  //Binder類是一個繼承了IBinder接口的java類,IBinder定義了跨進程通信的能力,Binder是它的實現(xiàn)。
  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";//關(guān)聯(lián)ICallBack接口的字符
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);//為IBinder對象綁定一個接口對象,并關(guān)聯(lián)字符(用于到時將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是最終會被調(diào)用的方法,重寫此方法來處理
    @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)//根據(jù)code碼調(diào)用自身接口方法
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_onSuccess:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.onSuccess(_arg0);//調(diào)用onSuccess方法(服務端繼承Stub后需要具體實現(xiàn))
          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
    {//客戶端的封裝,免去了勞煩的打包數(shù)據(jù)過程。使得客戶端調(diào)用形式看起來和服務端一模一樣。
      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內(nèi)部調(diào)用本地方法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

(以下是添加了回調(diào)的最終實現(xiàn),可以看參考鏈接一步一步來)

為需要使用的類,創(chuàng)建aidl文件。

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

parcelable Student;

系統(tǒng)會自動在main文件下生成aidl文件夾,并在該文件夾下創(chuàng)建相應目錄。

image-20210127145813899

在java相同路徑下創(chuàng)建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時需要實現(xiàn)
        return Student(parcel.readInt(),parcel.readString()?:"")
    }

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

        override fun newArray(size: Int): Array<Student?> {
            return arrayOfNulls(size)
        }
    }
}

創(chuàng)建IStudentService.aidl,定義了一個接口,該接口定義了服務端提供的功能。創(chuàng)建完后rebuild一下項目(每次創(chuàng)建和修改定義接口文件都要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<Student> getStudentList();

    void addStudent(inout Student student);

    void register(ICallBack callback);

    void unregister(ICallBack callback);
}
定向 tag 含義
in 數(shù)據(jù)只能由客戶端流向服務端,服務端將會收到客戶端對象的完整數(shù)據(jù),客戶端對象不會因為服務端對傳參的修改而發(fā)生變動。
out 數(shù)據(jù)只能由服務端流向客戶端,服務端將會收到客戶端對象,該對象不為空,但是它里面的字段為空,但是在服務端對該對象作任何修改之后客戶端的傳參對象都會同步改動。
inout 服務端將會接收到客戶端傳來對象的完整信息,并且客戶端將會同步服務端對該對象的任何變動。

創(chuàng)建在服務端的StudentService

package com.zhy.aidltest.Server

class StudentService : Service() { //服務端
    private val mStuList = CopyOnWriteArrayList<Student>()
    private val sCallbackList = RemoteCallbackList<ICallBack>()

    private var mBinder = object: IStudentService.Stub() { //服務端提供接口的具體實現(xiàn)
        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<Student> {
            return mStuList
        }
    }

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

    /**
     * 分發(fā)結(jié)果
     * @param result
     * @param msg
     */
    private fun dispatchResult(result: Boolean, msg: String) { //執(zhí)行所有注冊了的回調(diào)
        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()后必須調(diào)用
    }
}

可以看見有回調(diào),說明客戶端也提供了接口給服務端來回調(diào)(雙向通信,此時客戶端的變成了服務端),即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驅(qū)動返回的Binder調(diào)用StudentService里的具體實現(xiàn)方法

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死亡的回調(diào)
        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) //設(shè)置死亡代理
                    register(mCallback) //調(diào)用Server提供的register方法
                }
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
    }
    private val mCallback = object:ICallBack.Stub(){ //提供給Server回調(diào)的具體實現(xiàn)
        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)
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_bind"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Bind"/>

    <Button
        android:id="@+id/btn_addStudent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add Student"/>

    <Button
        android:id="@+id/btn_getStudentList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get Student List"/>

    <Button
        android:id="@+id/btn_unBind"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Unbind"/>

</LinearLayout>

AIDL使用注意:

  • 接口名和aidl文件名相同

  • 接口和方法前不加訪問權(quán)限修飾符,也不能用final,static。

  • Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、 CharSequence),使用這些類型時不需要import聲明。對于List和Map中的元素類型必須是Aidl支持的類型。如果使用自定義類型作為參數(shù)或返回值,必須實現(xiàn)Parcelable接口。

  • 自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即便在該類和定義的包在同一個包中。

  • 在aidl文件中所有非Java基本類型參數(shù)必須加上in、out、inout標記,以指明參數(shù)是輸入?yún)?shù)、輸出參數(shù)還是輸入輸出參數(shù)。

  • Java原始類型默認的標記為in,不能為其它標記。

6 Messenger

Messenger可以在不同進程中傳遞 Message 對象,在Message中放入我們需要傳遞的數(shù)據(jù),就可以輕松地實現(xiàn)數(shù)據(jù)的進程間傳遞了。Messenger 是一種輕量級的 IPC 方案,是對AIDL的封裝,底層實現(xiàn)是 AIDL。

使用詳見:https://blog.csdn.net/qq951127336/article/details/90678698

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容