Android Binder机制

简介: Binder简介Binder简介Binder驱动binder使用内存映射(mmap)来实现进程间传递数据,比较传统的进程间通信.

Binder简介

img_3402a0349c01ed9e02017e6be4d4dae8.png
Binder简介
img_d94734b40edd80617a3b978ff0248725.png
Binder驱动
  • binder使用内存映射(mmap)来实现进程间传递数据,比较传统的进程间通信.

  • binder只需要进行一次的数据拷贝(copy_from_user()).

  • 传统进程通信需要经过两次数据拷贝(copy_from_user(),copy_to_user())

img_d8f6744e0f52ccc1c3f6ec3d655a651b.png
模型原理图

Binder跨进程通信的调用过程

img_d0e11c4c356941b326f31ab53f0ec875.png
Binder通信
  1. client进程将数据写入 Parcelable对象中
  2. client进程通过代理的Binder对象的transact()方法将数据传递给Binder驱动.
  3. client进程中的调用线程被挂起(直到server进程返回结果)
  4. Binder驱动根据Binder代理对象找到该真正的Binder对象对应的server进程.
  5. Binder驱动把数据发送到server进程中,并通知server进程执行解包
  6. server进程从Binder线程池中取出目标线程.
  7. server进程通过 onTransact()回调进行数据解包
  8. server进程执行目标方法
  9. server进程返回数据
  10. Binder驱动通知Binder代理对象返回结果
  11. client进程中的执行线程被唤醒
  12. client进程通过Binder代理对象接收到返回结果

说明 :

服务端的Binder指的是Binder的本地对象
客户端的Binder指的是Binder代理对象,它只是Binder本地对象的一个远程代理.

一个例子来解释上述的binder机制

通过aidl自动生成的java类来分析binder机制.

  1. 使用Parcelable来定义一个传输数据类.
package cn.jimmie.learn.art.ipc.aidl

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

class User : Parcelable {
    var id: Long = 0
    var name: String? = ""
    var age = 0

    constructor(parcel: Parcel) {
        id = parcel.readLong()
        name = parcel.readString()
        age = parcel.readInt()
    }

    constructor(id: Long, name: String, age: Int) {
        this.id = id
        this.name = name
        this.age = age
    }

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

    override fun describeContents(): Int = 0

    companion object CREATOR : Parcelable.Creator<User> {
        override fun createFromParcel(parcel: Parcel): User = User(parcel)
        override fun newArray(size: Int): Array<User?> = arrayOfNulls(size)
    }
}
  1. 使用 aidl文件声明 跨进程对象
// User.aidl
package cn.jimmie.learn.art.ipc.aidl;

parcelable User;
  1. 使用 aidl 编写数据传输接口.
// IUserManager.aidl
package cn.jimmie.learn.art.ipc.aidl;
import cn.jimmie.learn.art.ipc.aidl.User;

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

interface IUserManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    User getUserById(long id);
}

需要注意的是 非基本类型的数据,需要使用import导入,不管是否在同一个包中.

非基本类型的参数,需要使用 in,out,inout来标记. 该标记表示数据的流向.

in 表示从客户端流向服务端的数据.

out 表示从服务端流向客户端的数据.

您应该将方向限定为真正需要的方向,因为编组参数的开销极大。

到此,对 aidl进行编译,会得到一份自动生成的IUserManager.java文件.

接下来分析,这份自动生成的文件内容.

自动生成的类方法剖析

服务端使用的类-->Stub:

Stub是一个继承IBinder和实现 数据接口的抽象方法.

服务端需要实现该方法,来创建一个真正的binder对象.

onTransact()方法 :

该方法是在客户端和服务端不在同一个进程中,才会被调用.用于传递客户端定位方法和参数.
该方法是在binder驱动中binder线程池分配的一个线程中运行,服务端可以接收到客户端传来的方法参数和定位到调用的方法.
服务端在根据这些参数,来调用真实的服务端数据接口.

注意,客户端的调用线程会等待服务端调用结束,得到数据返回

客户端通过Stub的静态方法asInterface来获取数据接口:

Stub.asInterface() :

  • 该静态方法是提供给客户端调用,用来获取所需的数据接口对象.
  • 如果客户端和服务端在同一个进程,那么客户端服务端共享一个binder,则直接返回 Stub本身

注意此时客户端和服务端操作的是同一个对象.

  • 如果客户端和服务端在不同的进程中,那么返回一个服务端binder的代理对象.

注意此时客户端和服务端操作的不是同一个对象.

  • 客户端通过代理对象把需要调用的方法和参数数据传递给binder驱动,binder驱动通知服务端调用真正的的binder方法.
  • 然后将调用的返回值再传递给binder驱动,binder驱动又将数据的返回给代理对象.
  • 这样客户端就能从代理对象中获取跨进程数据的返回

代理对象的数据接口实现:

getUserById(long id)方法 :

  • 跨进程过程中,客户端调用的数据接口方法
  • 该方法将方法调用的参数 传递给binder驱动进而传递给远程服务的binder对象
  • 远程服务器进而调用真正的数据接口方法,此时会阻塞当前线程,直到远程服务把返回值通过binder驱动写入到reply中

请看 完整的实现:

package cn.jimmie.learn.art.ipc.aidl;
// Declare any non-default types here with import statements

public interface IUserManager extends android.os.IInterface {
    /**
     * 服务端需要实现 Stub类,来创建一个真正的binder对象
     */
    public static abstract class Stub extends android.os.Binder implements cn.jimmie.learn.art.ipc.aidl.IUserManager {
        private static final java.lang.String DESCRIPTOR = "cn.jimmie.learn.art.ipc.aidl.IUserManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 该静态方法是提供给客户端调用,用来获取所需的数据接口对象.
         * <p>
         * 如果客户端和服务端在同一个进程,那么客户端服务端共享一个binder,直接返回 共享的数据接口,
         * 注意此时客户端和服务端操作的是同一个对象.
         * <p>
         * 如果客户端和服务端在不同的进程中,那么返回一个服务端binder的代理对象.
         * 注意此时客户端和服务端操作的不是同一个对象.
         * <p>
         * 客户端通过代理对象把需要调用的方法和参数数据传递给binder驱动,binder驱动通知服务端调用真正的的binder方法.
         * 然后将调用的返回值再传递给binder驱动,binder驱动又将数据的返回给代理对象.
         * 这样客户端就能从代理对象中获取跨进程数据的返回
         */
        public static cn.jimmie.learn.art.ipc.aidl.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            // 判断是同一进程,则直接返回服务端的调用接口
            if (((iin != null) && (iin instanceof cn.jimmie.learn.art.ipc.aidl.IUserManager))) {
                return ((cn.jimmie.learn.art.ipc.aidl.IUserManager) iin);
            }
            // 使用代理返回调用接口
            return new cn.jimmie.learn.art.ipc.aidl.IUserManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 如果客户端和服务端在同一个进程 , 将不会回调到该函数.
         * 因为 在同一个进程将不会调用 Proxy类,代理类中 调用 `transact()`函数, 服务端将回调此函数
         */
        @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;
            // 根据code来判断远程调用的方法
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getUserById: {
                    // 该方法和 writeInterfaceToken()方法,配合进行数据有效性验证
                    data.enforceInterface(descriptor);
                    // 获取传递的方法参数
                    long _arg0 = data.readLong();
                    // 调用服务端binder中真实有效的方法
                    cn.jimmie.learn.art.ipc.aidl.User _result = this.getUserById(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        // 返回数据有效,写入标志位
                        reply.writeInt(1);
                        // 将返回的数据写入到 reply中
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        // 返回数据无效,写入标志位
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        /**
         * 数据接口的代理对象,当需要进行跨进程通信时,客户端的调用对象
         */
        private static class Proxy implements cn.jimmie.learn.art.ipc.aidl.IUserManager {
            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;
            }

            /**
             * 跨进程过程中,客户端调用的数据接口方法
             * 该方法将方法调用的参数 传递给binder驱动进而传递给远程服务的binder对象
             * 远程服务器进而调用真正的数据接口方法,此时会阻塞当前线程,直到远程服务把返回值通过binder驱动写入到reply中
             */
            @Override
            public cn.jimmie.learn.art.ipc.aidl.User getUserById(long id) throws android.os.RemoteException {
                // 方法参数
                android.os.Parcel _data = android.os.Parcel.obtain();
                // 方法返回值
                android.os.Parcel _reply = android.os.Parcel.obtain();
                // 返回的结果数据
                cn.jimmie.learn.art.ipc.aidl.User _result;
                try {
                    // 写入描述符,用于数据验证
                    _data.writeInterfaceToken(DESCRIPTOR);
                    // 写入参数
                    _data.writeLong(id);
                    // 传递参数数据和返回数据到远程,此时当前线程将被阻塞,等待远程调用结束
                    mRemote.transact(Stub.TRANSACTION_getUserById, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        // 数据正确返回,将其写入到_result中
                        _result = cn.jimmie.learn.art.ipc.aidl.User.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    // 数据回收
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getUserById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public cn.jimmie.learn.art.ipc.aidl.User getUserById(long id) throws android.os.RemoteException;
}

至此, binder机制的运转流程已经很明了了.

可以看出, aidl 是非必须的, 只要我们自己编写 IUserManager接口,也能过实现binder跨进程通信的功能.

参考

  1. Android开发艺术探索(任玉刚)
  2. Android跨进程通信:图文详解 Binder机制 原理
目录
相关文章
|
3月前
|
Linux Android开发 iOS开发
深入探索Android与iOS的多任务处理机制
在移动操作系统领域,Android和iOS各有千秋,尤其在多任务处理上展现出不同的设计理念和技术实现。本文将深入剖析两大平台在后台管理、资源分配及用户体验方面的策略差异,揭示它们如何平衡性能与电池寿命,为用户带来流畅而高效的操作体验。通过对比分析,我们不仅能够更好地理解各自系统的工作机制,还能为开发者优化应用提供参考。
|
3月前
|
算法 Linux 调度
深入探索安卓系统的多任务处理机制
【10月更文挑战第21天】 本文旨在为读者提供一个关于Android系统多任务处理机制的全面解析。我们将从Android操作系统的核心架构出发,探讨其如何管理多个应用程序的同时运行,包括进程调度、内存管理和电量优化等方面。通过深入分析,本文揭示了Android在处理多任务时所面临的挑战以及它如何通过创新的解决方案来提高用户体验和设备性能。
82 1
|
3月前
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
4月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
4月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
76 1
|
4月前
|
存储 安全 数据安全/隐私保护
探索安卓与iOS的隐私保护机制####
【10月更文挑战第15天】 本文深入剖析了安卓和iOS两大操作系统在隐私保护方面的策略与技术实现,旨在揭示两者如何通过不同的技术手段来保障用户数据的安全与隐私。文章将逐一探讨各自的隐私控制功能、加密措施以及用户权限管理,为读者提供一个全面而深入的理解。 ####
146 1
|
4月前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
114 2
|
4月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
4月前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
64 2
|
5月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
99 8

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 1
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    14
  • 2
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    28
  • 3
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    34
  • 4
    Android历史版本与APK文件结构
    121
  • 5
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 6
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 7
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    60
  • 8
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 9
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 10
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    121