Framework底层原理——Binder调用流程分析

news2025/1/20 15:45:06

binder是一个非常好的跨进程通信工具,Android对其进行了各种封装,虽然我们用起来简单,但是理解起来却比较困难。

1.自己设计一个跨进程通信机制

在理解binder之前呢,首先我们想一下,如果我们自己设计一个跨进程通信的机制,那我们会怎么设计呢? 如果是我的话,我可能会按照下图设计。

图中左边是客户端,右边是服务端,客户端想要调用服务端的call函数,首先我们需要先将函数名称以及参数值进行序列化,然后再使用linux系统所提供的跨进程通信方式,例如socket或者是管道,将这些序列化过后的数据传递给服务端,然后服务端拿到这些数据之后,首先进行反序列化,然后再调用相应的函数,将返回值返回给客户端。 其实和我们使用网络访问服务器的结构很像。 接着我们来看下binder的通信流程。

2.跨进程通信流程

1.生成AIDL文件

首先我们新建一个AIDL文件,

interface IFile {
     Bitmap getBitmap(String path);
}

然后build过后,系统会自动帮我们生成一个IFile.java文件

public interface IFile extends android.os.IInterface {
    /**
     *继承binder,实现IFile接口,运行于服务端
     */
    public static abstract class Stub extends android.os.Binder implements com.android.hdemo.IFile {
        private static final java.lang.String DESCRIPTOR = "com.android.hdemo.IFile";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.android.hdemo.IFile interface,
         * generating a proxy if needed.
         */
        public static com.android.hdemo.IFile asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.android.hdemo.IFile))) {
                return ((com.android.hdemo.IFile) iin);
            }
            return new com.android.hdemo.IFile.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBitmap: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    android.graphics.Bitmap _result = this.getBitmap(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        //实现IFile接口,运行于客户端
        private static class Proxy implements com.android.hdemo.IFile {
            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 android.graphics.Bitmap getBitmap(java.lang.String path) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                android.graphics.Bitmap _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(path);
                    mRemote.transact(Stub.TRANSACTION_getBitmap, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = android.graphics.Bitmap.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public android.graphics.Bitmap getBitmap(java.lang.String path) throws android.os.RemoteException;
}

生成的代码结构并不复杂,IFile是一个接口,继承了android.os.IInterface,并加入了我们自定义的接口方法getBitmap,IFile中包含一个静态抽象类Stub,Stub又包含一个静态内部类Proxy。Stub继承binder,实现IFile接口,运行于服务端,Proxy实现IFile接口,运行于客户端。 接着我们来看一下从客户端到服务端的整个流程

2.从客户端到服务端

客户端

首先来看下我们是如何来获取服务的,从注释1处可以看出,客户端拿到binder对象之后,调用asInterface方法将其转换成本地的IFile对象。

Intent intent = new Intent(this,TestService.class);
this.bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        try {
            //1
            IFile iFile = IFile.Stub.asInterface(service);
            iFile.getBitmap("");
        }catch (Exception e){
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
}, Service.BIND_AUTO_CREATE);

而asInterface是系统生成的代码,从下面注释1处可以看出,如果客户端和服务端在同一个进程,则直接将binder强转成本地接口对象,否则返回Proxy对象。如注释2所示,Proxy的getFile方法会调用mRemote.transact方法,mRemote是一个binder对象,其真正的实现是BinderProxy。

public static com.android.hdemo.IFile asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    //1.如果客户端和服务端在同一个进程,则直接调用,否则使用Proxy
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.android.hdemo.IFile))) {
        return ((com.android.hdemo.IFile) iin);
    }
    return new com.android.hdemo.IFile.Stub.Proxy(obj);
}

//Proxy
private static class Proxy implements com.android.hdemo.IFile {
    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 android.graphics.Bitmap getBitmap(java.lang.String path) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        android.graphics.Bitmap _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeString(path);
            //2.调用binder的transact方法
            mRemote.transact(Stub.TRANSACTION_getBitmap, _data, _reply, 0);
            _reply.readException();
            if ((0 != _reply.readInt())) {
                _result = android.graphics.Bitmap.CREATOR.createFromParcel(_reply);
            } else {
                _result = null;
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

BinderProxy的transact方法会调用transactNative方法,最终会调用native层的BpBinder的transact方法,然后由BpBinder和binder驱动进行交互。

//BinderProxy
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    ......
    try {
        return transactNative(code, data, reply, flags);
    } finally {
        ......
    }
}

//Native层BpBinder
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        bool privateVendor = flags & FLAG_PRIVATE_VENDOR;
        // don't send userspace flags to the kernel
        flags = flags & ~FLAG_PRIVATE_VENDOR;

        // user transactions require a given stability level
        if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
            using android::internal::Stability;

            auto stability = Stability::get(this);
            auto required = privateVendor ? Stability::VENDOR : Stability::kLocalStability;

            if (CC_UNLIKELY(!Stability::check(stability, required))) {
                ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
                    Stability::stabilityString(stability).c_str(),
                    Stability::stabilityString(required).c_str());
                return BAD_TYPE;
            }
        }

        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;

        return status;
    }

    return DEAD_OBJECT;
}

服务端

binder驱动收到请求之后会调用native层BBinder的onTransact方法,

status_t BBinder::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/)
{
    switch (code) {
        case INTERFACE_TRANSACTION:
            reply->writeString16(getInterfaceDescriptor());
            return NO_ERROR;

        case DUMP_TRANSACTION: {
            int fd = data.readFileDescriptor();
            int argc = data.readInt32();
            Vector<String16> args;
            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
               args.add(data.readString16());
            }
            return dump(fd, args);
        }

        case SHELL_COMMAND_TRANSACTION: {
            int in = data.readFileDescriptor();
            int out = data.readFileDescriptor();
            int err = data.readFileDescriptor();
            int argc = data.readInt32();
            Vector<String16> args;
            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
               args.add(data.readString16());
            }
            sp<IShellCallback> shellCallback = IShellCallback::asInterface(
                    data.readStrongBinder());
            sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
                    data.readStrongBinder());

            // XXX can't add virtuals until binaries are updated.
            //return shellCommand(in, out, err, args, resultReceiver);
            (void)in;
            (void)out;
            (void)err;

            if (resultReceiver != nullptr) {
                resultReceiver->send(INVALID_OPERATION);
            }

            return NO_ERROR;
        }

        case SYSPROPS_TRANSACTION: {
            report_sysprop_change();
            return NO_ERROR;
        }

        default:
            return UNKNOWN_TRANSACTION;
    }
}

接着会回掉到java层的onTransact方法,如下注释1所示,this.getBitmap(_arg0)为Stub类的方法,从服务端获取到图片,然后在注释2处,将bitmap写入到返回值。

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(DESCRIPTOR);
            return true;
        }
        case TRANSACTION_getBitmap: {
            data.enforceInterface(DESCRIPTOR);
            java.lang.String _arg0;
            _arg0 = data.readString();
            //1.服务端获取的bitmap
            android.graphics.Bitmap _result = this.getBitmap(_arg0);
            reply.writeNoException();
            if ((_result != null)) {
                reply.writeInt(1);
                //2.将bitmap写入返回值
                _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

我们再来看下service的定义,当绑定到一个service之后,返回的Stub对象,实现了getBitmap方法,返回了本地的bitmap。

public class TestService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IFile.Stub() {
            @Override
            public Bitmap getBitmap(String path) throws RemoteException {
                //1.返回本地bitmap
                return mBitmap;
            }
        };
    }
}

这样从客户端到服务端的流程就走完了,我们看下它的流程图

3.binder的优势

linux本身提供了很多跨进程通信的方式,例如socket,共享内存,管道之类的,那为啥还要再弄出个binder呢,有以下几点

方便

binder使用起来对于开发者来说非常的友好,隐藏了底层的实现细节,我们只需要关注于业务逻辑即可。

高效

binder通信过程中,将内存同时映射到内核和应用进程当中,只需要拷贝一次即可。而socket和管道均需要从应用进程拷贝到内核,再从内核拷贝到应用进程,需要两次拷贝,共享内存不需要数据拷贝,但使用起来比较复杂。

安全

调用方的身份标记由binder机制本身在内核态中添加,调用方不能自己更改。我们可以通过Binder获取到调用方的uid和pid,从而进行权限控制。


之前小编为了彻底搞的这块知识点,对Framework 进行了一段时间的深入研究,并根据自己所学所理解整理了一些学习笔记,并更具不同小知识点进行了归类:

《Android Framework学习手册》:https://0a.fit/acnLL

  1. 开机Init 进程
  2. 开机启动 Zygote 进程
  3. 开机启动 SystemServer 进程
  4. Binder 驱动
  5. AMS 的启动过程
  6. PMS 的启动过程
  7. Launcher 的启动过程
  8. Android 四大组件
  9. Android 系统服务 - Input 事件的分发过程
  10. Android 底层渲染 - 屏幕刷新机制源码分析
  11. Android 源码分析实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/105958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简单Thinkphp5.1如何使用Topsdk\Topapi

一淘模板&#xff08;56admin.cn&#xff09;给大家介绍tp5.1相关知识&#xff0c;其中主要记录tp5.1是怎么使用Topsdk\Topapi&#xff08;对接淘宝客开放平台&#xff09;&#xff0c;希望对需要的朋友有所帮助&#xff01; 1、公司有一项目需要对接淘宝开放平台 先去申请帐号…

tensorrt debug问题汇总

目录 1. Dynamic dimensions required for input: input, but no shapes were provided. Automatically overriding 2. sampleMNIST.obj : error LNK2019: 无法解析的外部符号 cudaStreamCreate 3. Assertion failed: (smVersion &#xff1c; SM_VERSION_A100) &&…

条码管理系统,助力企业打造轻量级数字化车间

在原辅材料供应、生产管理、仓储物流、市场营销等相关业务环节中&#xff0c;采取适当的软硬件技术手段&#xff0c;实时记录产品信息。通过查询可以随时跟踪产品的生产状态、仓储状态和流向&#xff0c;达到可追溯管理的目的。随着制造企业对精细化管理要求的提高&#xff0c;…

【QT开发笔记-基础篇】| 第五章 绘图QPainter | 5.7 画笔设置

本节对应的视频讲解&#xff1a;B_站_视_频 https://www.bilibili.com/video/BV16W4y1g7dM 经过前面几节课的讲解&#xff0c;学会了绘制点、线、多段线、多边形、矩形、圆角矩形 到这里就可以学习画笔和画刷的设置了&#xff0c;本节先讲解画笔的设置 Qt 中画笔的类是 QPen…

正则表达式验证合集

1.定义封装的公共js 在src下定义一个util文件夹&#xff0c;并且定义个validate.js(当然你想取什么名字就什么名字哈哈哈哈) 2.上代码 //邮箱 /*** 邮箱* param {*} s*/ export function isEmail(s) {return /^([a-zA-Z0-9_-])([a-zA-Z0-9_-])((.[a-zA-Z0-9_-]{2,3}){1,2}…

基于FPGA的 矩阵键盘按键识别 【原理+源码】

目录 引言 原理阐述 实现方法 源码分享 板级调试演示 引言 最近了解了矩阵键盘扫描的原理&#xff0c;动手实现了一下&#xff0c;在这里做一个简单的总结。 原理阐述 矩阵键盘典型电路&#xff1a; FPGA的应用电路&#xff1a; 其中&#xff0c;行信号为FPGA输入信号&a…

企业从哪里开始构建弹性 IT 基础架构

混合工作模式扩大了工作范围&#xff0c;增加了 IT 团队的负担&#xff0c;因为他们需要在面对增加的攻击面时保持弹性。入侵企业的 IT 基础架构只需要一个受损的身份。 什么是企业标识&#xff1f; 这些是用户名、密码、网络、端点、应用程序等&#xff0c;充当业务敏感信息…

CheatEngine教程-官方9关

文章目录第一步&#xff1a;环境准备&#xff0c;下载并安装CE第二关&#xff1a;精确扫描数值第三关&#xff1a;未知数值扫描第四关&#xff1a;浮点数的扫描第五关&#xff1a;代码替换功能第六关&#xff1a;关于指针第七关&#xff1a;简单代码注入第八关&#xff1a;查找…

力扣(LeetCode)173. 二叉搜索树迭代器(C++)

设计 根据二叉树的中序遍历的迭代解法&#xff0c;稍改代码&#xff0c;就是本题的解法。 初始化 : 传入了根结点&#xff0c;根据迭代思路&#xff0c;将结点的左链依次入栈。 nextnextnext : 栈顶结点就是所求。根据迭代思路&#xff0c;当前结点要变成栈顶结点的右儿子。由…

Openlayers:自定义坐标系

Openlayers天然支持EPSG:4326(WGS1984地理坐标系)、EPSG:3857(Web墨卡托投影坐标系,即:将WGS84坐标系投影到正方形,南北投影范围为[-85.051129,+85.051129])。同时,Openlayers也支持开发者自定义坐标系。那么具体如何操作呢? 相关的API ol.proj.projection ol.proj.…

Android设计模式详解之工厂方法模式

前言 工厂方式模式是创建型设计模式&#xff1b; 定义&#xff1a;定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪个类。 使用场景&#xff1a;在任何需要生成复杂对象的地方&#xff0c;都可以使用工厂方法模式。复杂对象适合使用工厂模式&#xff0c;用new就可…

【Java|golang】1753. 移除石子的最大得分

你正在玩一个单人游戏&#xff0c;面前放置着大小分别为 a​​​​​​、b 和 c​​​​​​ 的 三堆 石子。 每回合你都要从两个 不同的非空堆 中取出一颗石子&#xff0c;并在得分上加 1 分。当存在 两个或更多 的空堆时&#xff0c;游戏停止。 给你三个整数 a 、b 和 c &a…

攻防世界新手练习区——unseping

目录 知识点 解读题目源码&#xff1a; 命令绕过 知识点 PHP代码审计PHP序列化和反序列化PHP中魔术方法命令执行绕过方式 解读题目源码&#xff1a; 这道题首先一上来就是一段PHP代码&#xff0c;其中看到unserialize()就知道考的是反序列化&#xff0c;但是我们再往上看代码…

rust编程-struct结构体(chapter 5.1 结构体定义和实例化)

目录 1. 结构体定义和实例化 1.1 struct介绍 1.2 使用字段简写进行实例化 1.3 从其它对象实例化新结构体对象 1.4 使用无命名字段的struct类型 1.5 没有任何字段的structs结构体 1.6 结构体字段的值所有权 结构(struct)是一种自定义数据类型&#xff0c;可以将多个相关类…

存量时代下 用低代码开发平台提升你的CEM

随着人口及流量红利的逐步见顶&#xff0c;我国经济从增量市场迈入存量市场。在充分竞争的存量市场环境下&#xff0c;传统的初级竞争模式无法支撑产业的发展&#xff0c;相反还会让企业陷入持续烧钱的恶性循环中&#xff0c;获客难度的提升无疑加速了体验经济时代的到来&#…

modbus介绍、环境搭建测试与qt下串口/Tcp的demo工程测试

一、modbus的介绍 1.简介 Modbus是一种串行通信协议&#xff0c;于1979年为使用可编程逻辑控制器&#xff08;PLC&#xff09;通信而发表。Modbus已经成为工业领域通信协议的业界标准&#xff08;De facto&#xff09;&#xff0c;并且现在是工业电子设备之间常用的连接方式&a…

SSM框架项目实战-CRM(客户关系管理1)

目录​​​​​​​ 1 项目介绍 1.1 crm简介 1.2 业务流程 1.3 crm的技术架构 2 物理模型设计 2.1 crm表的结构 2.2 主键字段 2.2 外键字段 2.3 关于日期和时间的字段 3 搭建项目环境 3.1 添加maven依赖 3.2 添加配置文件 3.3 添加页面和静态资源 ​编辑 4 首页…

超标量处理器设计——第九章_执行

参考《超标量处理器》姚永斌著 文章目录超标量处理器设计——第九章_执行9.1 概述9.2 FU类型9.2.1 ALU9.2.2 AGU9.2.3 BRU条件码分支正确性检查9.3 旁路网络9.3.1 简单设计的旁路网络9.3.2 复杂设计的旁路网络9.4 操作数的选择9.5 Cluster9.5.1 Cluster IQ9.5.2 Cluster Bypass…

Python 自动化测试(四):数据驱动

在实际的测试工作中&#xff0c;通常需要对多组不同的输入数据&#xff0c;进行同样的测试操作步骤&#xff0c;以验证我们的软件质量。这种测试&#xff0c;在功能测试中非常耗费人力物力&#xff0c;但是在自动化中&#xff0c;却比较好实现&#xff0c;只要实现了测试操作步…

前端基础_fillStyle和strokeStyle属性

fillStyle和strokeStyle属性 在前面的章节&#xff0c;在绘制图形时只用到默认的线条和填充样式。而在本节中将会探讨canvas全部的可选项&#xff0c;来绘制出更加吸引人的内容。如果想要给图形上色&#xff0c;有两个重要的属性可以做到&#xff1a;fillStyle和strokeStyle。…