美文网首页
Binder机制(一)AIDL

Binder机制(一)AIDL

作者: 文泰ChrisTwain | 来源:发表于2023-03-19 23:29 被阅读0次

一、简介Android Interface Definition Language (AIDL)

基于Binder机制封装的Android跨进程通信DSL(领域特定语言),提供Android进程间通信能力。AIDL将复杂的Binder机制进行封装,让业务只需关注方法调用和参数传递。使用步骤如下:
(1)定义 AIDL 接口:在 Android Studio 中创建一个 AIDL 文件,定义需要跨-- 进程调用的方法和参数。方法和参数的声明需要按照 AIDL 的规范进行,包括数据类型、参数方向等;
(2)实现 AIDL 接口:通过继承 AIDL 自动生成的 Stub 类来实现;
(3)在服务中注册 AIDL 接口:Service的onBind()方法中返回步骤2的Stub类实现;
(4)绑定服务:在客户端中绑定服务,通过 IBinder 接口获取 AIDL 对象;
(5)调用远程方法:通过获取的 AIDL 对象调用远程方法实现跨进程通信。

二、AIDL支持的数据类型、文件类型:

1. AIDL支持的数据类型

(1) Java8种基本数据类型:byte、short、int、long、float、double、boolean、char(参数的定向tag默认为in)
(2) Java常用数据类型:String、CharSequence、List、Map(参数的定向tag默认为in,并且只能是 in)
(3) aidl文件的interface类型(接口类,用于进程间互相调用、回调)
(4) aidl文件的parcelable类型(数据类,需要配合同包路径的.java数据类,这个.java数据类需要实现Parcelable序列化)

2. AIDL文件有两大类:数据类,接口类(包含两类:交互接口,回调接口)

  • 自定义的Parcelable类和AIDL接口在其它AIDL中引用必须显示import
  • 自定义的Parcelable类必须新建一个同名的AIDL文件,添加package、parcelable xxxClass

三、AIDL关键字

1.in、out、inout修饰的参数

表示跨进程通信中数据的流向(基本数据类型默认是in,非基本数据类型可以使用其它数据流向out、inout),除基本类型,其它类型参数必须标明方向

in表示数据只能由客户端流向服务端(表现为服务端修改此参数,不会影响客户端的对象)

out表示数据只能由服务端流向客户端(表现为服务端收到的参数是空对象,并且服务端修改对象后客户端会同步变动)

inout则表示数据可在服务端与客户端之间双向流通(表现为服务端能接收到客户端传来的完整对象,并且服务端修改对象后客户端会同步变动)

原理:in会读取客户端传入的对象数据;out会新创建一个对象,而不读取原对象,同时会将新对象返回给客户端;inout则包含前面两步操作。

2.oneway关键字修饰的方法

用于修改远程调用的行为,oneway方法不可以有返回值,也不可以带out、inout参数

  • 本地调用(同步调用)
  • 远程调用(异步调用,客户端不会被阻塞)
    使用oneway时,远程调用不会阻塞,它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自Binder线程池的常规调用进行接收

四、使用举例

1.APK1和APK2两个进程需要进行通信,APK1作为客户端可调用服务端APK2方法且可以传入参数,服务端方法执行过程中可进行回调
  • AIDL文件,客户端APK1和服务端APK2保持包名和文件内容一致,aidl文件目录为src->main->aidl,与java目录同级,注意包名与java中保持一致
// IConfigService.aidl
package com.xxx.ipc;
interface IConfigService {
    oneway void queryData(String name);
}
  • 服务端APK2建立Service类作为IPC服务端
class IPCService : Service() {
    private var mBinder = object : IConfigService.Stub() {
        override fun queryData(name: String?) {
            // 服务端 doSomething
        }
    }
    override fun onBind(intent: Intent): IBinder {
        return mBinder
    }
}
  • 客户端调用服务端
private var iConfigService: IConfigService? = null

// 添加死亡代理
private var mDeathRecipient = object : IBinder.DeathRecipient {
    override fun binderDied() {
        if (iConfigService == null) {
            return
        }
        iConfigService?.asBinder()?.unlinkToDeath(this, 0)
        iConfigService = null
    }

}

var serviceConnectionConfig = object : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {
            // 与服务端意外断开时回调
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        iConfigService = IConfigService.Stub.asInterface(service)
        service?.isBinderAlive
        service?.linkToDeath(mDeathRecipient, 0)
    }
}

// 绑定远程服务
var intent = Intent()
intent.component = ComponentName("服务端进程包名", "服务端Service类完整包名.IPCService")
bindService(intent, serviceConnectionConfig, Context.BIND_AUTO_CREATE)

// ServiceConnection onServiceConnected()回调后 客户端可调用服务端方法
iConfigService?.queryData("hello")

// 解除绑定
unbindService(serviceConnectionConfig)
2.自定义enum Parcelable类 & 服务端回调
// SaveResult.java
package com.xxx.ipc.save.result

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

enum class SaveResult : Parcelable {
    SAVE_SUCCESS,                    // 保存成功
    SAVE_PARAMETER_NULL,             // 参数含空
    SAVE_FAILED_PATH_ERROR,          // 路径错误

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(ordinal)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<SaveResult> {
        override fun createFromParcel(parcel: Parcel): SaveResult {
            return values()[parcel.readInt()]
        }

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

每个Parcelable类客户端和服务端都需要相同的文件在工程目录中,这样代码中才能找到对应的类,所示enum类可作为参数传给服务端

// SaveResult.aidl
package com.xxx.ipc.save.result;

parcelable SaveResult;

定义服务端回调接口

// IContentCallback.aidl
package com.xxx.ipc;

import com.xxx.ipc.save.result.SaveResult;  // SaveResult为一个自定义的enum parcelable

interface IContentCallback {
    void onStart();
    void onProgress();
    void onSaveResult(in SaveResult result, String saveFullPath);
}
  • 服务端Service代码
class IPCService : Service() {
    private var mRemoteSaveCallback = RemoteCallbackList<IContentCallback>()

    fun sendSaveCallback(result: SaveResult, fullPath: String) {
        var callbackNum = mRemoteSaveCallback.beginBroadcast()
        for (i in 0 until callbackNum) {
            mRemoteSaveCallback.getBroadcastItem(i).onSaveResult(result, fullPath)
        }
        mRemoteSaveCallback.finishBroadcast()
    }

    private var mBinder = object : IConfigService.Stub() {

        override fun addSaveCallback(saveCallback: IContentCallback?) {
            mRemoteSaveCallback.register(saveCallback)
        }

        override fun removeSaveCallback(saveCallback: IContentCallback?) {
            mRemoteSaveCallback.unregister(saveCallback)
        }

    }

    override fun onBind(intent: Intent): IBinder {
        return mBinder
    }
}
  • 客户端实现回调代码
private var iContentCallback = object : IContentCallback.Stub() {
        override fun onSaveResult(SaveResult result, String saveFullPath) {
                // do something
        }
}

// ServiceConnection onServiceConnected()回调后执行
iConfigService?.addSaveCallback(iContentCallback)     // 添加回调
iConfigService?.removeSaveCallback(iContentCallback)  // 移除回调

五、原理

通过将接口的定义文件(.aidl 文件)编译成 Java 代码,生成一个实现 android.os.IInterface 接口的 Binder 对象,使得客户端可以通过获取这个 Binder 对象来调用服务端中的方法实现进程间通信。IInterface 接口是一个标准的 Android 接口,所有使用 Binder 进行进程间通信的服务端都需要实现这个接口。

六、注意事项

1.AIDL文件使用英文注释(测试使用Gradle5.4.1能使用中文注释,更高版本待测试);
2.AIDL接口文件不可以定义重载方法,即不能定义两个方法名相同的方法,即使参数个数、类型不一致也不行;
3.避免混淆AIDL文件;
4.在使用AIDL进行跨进程通信的过程中,需要使用Parcelable 或 Serializable 的数据类型进行参数传递,AIDL文件及自定义的Parcelable类客户端和服务端保持文件内容和包名一致;
5.Parcelable类在main-java目录中,对应的AIDL文件在main-aidl中,两个文件包名需一致;
6.AIDL文件、Parcelable类可打包为aar包提供给客户端作为SDK,放入module下libs目录,通过implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])导入
7.AIDL被调用的过程中可能会发生异常,需做好异常处理逻辑

参考:

Google官方文档-Android 接口定义语言 (AIDL)
AIDL中的in、out、inout

相关文章

  • Android源码分析——从AIDL了解Binder机制

    以AIDL为入口,探究Binder机制的原理 从AIDL了解Binder 前面简单学习了一下AIDL的用法,接下来...

  • Android多进程机制(二)Binder工作机制

    Binder工作机制 我们可以先从SDK自动为我们生成的AIDL对应的Binder类来分析Binder工作机制。 ...

  • AIDL学习

    AIDL AIDL的核心有两点 AIDL是一种跨进程通讯方式这种方式是基于Binder机制来进行的,Binder本...

  • Binder AIDL proxy stub

    Binder与AIDL?Android中有多种IPC机制,如AIDL,Messenger,Socket,Conte...

  • 2.6基础知识-Binder

    Binder详解 1 .Linux内核的基础知识(跟Binder有关的) Binder通信机制介绍 AIDL实现 ...

  • 借助 AIDL 理解 Android Binder 机制——AI

    在上一篇文章——借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉中我们已经分析了...

  • Binder 机制-AIDL

    AIDL 概念 前面我们梳理了 Binder 机制,应该有个概念并且可以简单实现了 Binder 机制入门[htt...

  • Binder源码阅读(java层)

    宏观上Binder机制 Binder机制小案例(进程通信) 进程A 进程B 创建和进程A一模一样的aidl文件,包...

  • 收集_Binder机制

    为什么 Android 要采用 Binder 作为 IPC 机制?AIDL原理解析Android中AIDL的工作原...

  • IPC之AIDL分析

    AIDL用法及代码分析 AIDL为安卓接口定义语言的简称,作用是利用binder机制,实现IPC。 1、AIDL用...

网友评论

      本文标题:Binder机制(一)AIDL

      本文链接:https://www.haomeiwen.com/subject/choqrdtx.html