AIDL中 Binder 的具体流转过程(源码讲解)

news2024/11/14 11:01:55

前言

本篇文章承接上篇 Binder对象的流转(系统服务的调用过程、AIDL的使用过程)
上一篇简单笼统地说明了流转的大致方向与手段,此篇文章我们将深入代码层面,看看 Binder 到底是怎么实现的。

一、一个简单的AIDL示例

客户端:

bindService(serviceIntent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.i(TAG, "onServiceConnected() service=" + service);
        iShopAidlInterface = IShopAidlInterface.Stub.asInterface(service);
        try {
            iShopAidlInterface.buyProduct1(9988, "大连市甘井子区");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}, BIND_AUTO_CREATE);

服务端:

public class ShopService extends Service {
    private String TAG = "ShopService";
    public ShopService() {
    }

    private IShopAidlInterface.Stub mBinder = new IShopAidlInterface.Stub() {
        @Override
        public void buyProduct1(int productNo, String address) throws RemoteException {
            Log.i(TAG, "SERVER: receive productNo: " + productNo + ", address: " +address);
            Log.i(TAG, "YOU buy succeed!");
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

客户端调用 bindService 方法,绑定服务,在 ServiceConnection 的 onServiceConnected() 回调中得到了服务端返回的 Binder 对象。然后调用了 buyProduct1() 实现了 IPC 跨进程的业务调用。
今天要探究的问题就是,这中间发生了什么,怎么实现的?

二、Binder对象是怎么回传到 onServiceConnected(ComponentName name, IBinder service) 里的?

调用流程

ContextWrapper.java - bindService()
	ContextImpl.java - bindService() - bindServiceCommon() { getServiceDispatcher(); ActivityManager.bindService();}
ContextImpl.java
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        ******** 关键点一 ********
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        service.prepareToLeaveProcess(this);
        ******* 关键点二 ********
        int res = ActivityManager.getService().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
        return res;
}  

关键点一

LoadedApk.java
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
     sd = new ServiceDispatcher(c, context, handler, flags);
     return sd.getIServiceConnection();
}

static final class ServiceDispatcher {
    private final ServiceDispatcher.InnerConnection mIServiceConnection;
    private final ServiceConnection mConnection;
}

private static class InnerConnection extends IServiceConnection.Stub {
    final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

    InnerConnection(LoadedApk.ServiceDispatcher sd) {
        mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
    }

    public void connected(ComponentName name, IBinder service, boolean dead)
            throws RemoteException {
        LoadedApk.ServiceDispatcher sd = mDispatcher.get();
        if (sd != null) {
            sd.connected(name, service, dead);
        }
    }
}

private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices 
        = new ArrayMap<>();

getServiceDispatcher() new出一个ServiceDispatcher,这个 ServiceDispatcher 的作用就是将绑定服务后得到的 Binder 对象上发到 ServiceConnection 中,使得客户端可以在回调中获取 Binder 对象。
ServiceDispatcher 维护在 mServices 成员变量中,这里的 Context 其实就是 bindService 所在的Activity,就是说一个Activity对应一个 ArrayMap,ArrayMap中 一个 ServiceConnection 对应 一个 ServiceDispatcher。

InnerConnection extends IServiceConnection.Stub很明显,InnerConnection 是一个 IPC 的服务端,AMS 使用 InnerConnection的proxy对象,连接上 InnerConnection 时,调用 InnerConnection 的 connected(ComponentName name, IBinder service, boolean dead) 方法,将 Binder 对象传上来。

public void connected(ComponentName name, IBinder service, boolean dead) {
    mActivityThread.post(new RunConnection(name, service, 0, dead));
}
 private final class RunConnection implements Runnable {
	doConnected(mName, mService, mDead);
}
public void doConnected(ComponentName name, IBinder service, boolean dead) {
    mConnection.onServiceConnected(name, service);
}

这样我们的客户端就可以得到 Binder 对象了,onServiceConnected(ComponentName name, IBinder service)
请添加图片描述
到此为止,红框中的前半部分流程对应的代码就讲完了。

三、服务端是如何生成对象,并返回给 AMS 的?

关键点二

ActivityManager.getService().bindService()

ActivityManagerService.java - bindService()
	ActiveServices.java - bindServiceLocked()
	bringUpServiceLocked() { realStartServiceLocked() } // 先启动目标service
	requestServiceBindingLocked() { // 再bind()
	    r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
	}

IApplicationThread.aidl - scheduleBindService()

注意此处:app.thread.scheduleBindService()ProcessRecord app,IApplicationThread thread,也就是调用了目标进程的 applicationThread 去 bindService()

/**
 * System private API for communicating with the application.  This is given to
 * the activity manager by an application  when it starts up, for the activity
 * manager to tell the application about things it needs to do.
 *
 * {@hide}
 */
oneway interface IApplicationThread {
    void scheduleBindService(IBinder token,
            in Intent intent, boolean rebind, int processState);

我们看一下 IApplicationThread 的解释:这是一个向外公布的与此应用沟通的系统私有的api,当应用启动时,会将此对象给到 AMS,AMS 通过此对象告诉 APP 需要做什么。说白了,就是IApplicationThread.aidl 为AMS 提供了一个 跨进程调用的工具(手段)。AMS通过这种方法,让目标进程 执行一些动作如 createService、bindService等。

这个 IApplicationThread 的实现服务端在:

ActivityThread.java 的内部类 ApplicationThread
private class ApplicationThread extends IApplicationThread.Stub {
	public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;
            sendMessage(H.BIND_SERVICE, s);
        }
}

class H extends Handler {
	public void handleMessage(Message msg) {
	    switch (msg.what) {
            case BIND_SERVICE:
                    handleBindService((BindServiceData)msg.obj);
                    break;
}

private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } 
        }
    }
IBinder binder = s.onBind(data.intent);
ActivityManager.getService().publishService(data.token, data.intent, binder);

调用目标的 onBind 方法,返回 IBinder 对象,再通过 AMS 的 publishService 方法,将 binder 公布出去。

ActivityManagerService.java
public void publishService(IBinder token, Intent intent, IBinder service) {
      mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}

ActiveServices.java
 void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
      try {
          c.conn.connected(r.name, service, false);
      } catch (Exception e) {
          Slog.w(TAG, "Failure sending service " + r.name +
                " to connection " + c.conn.asBinder() +
                " (in " + c.binding.client.processName + ")", e);
      }
}              

到此处,再与第一节最后讲的关键点一的逻辑衔接上。
完成闭环,将服务端返回的对象,通过 AMS 传给了 客户端。也即:
请添加图片描述

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

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

相关文章

网关全局过滤器:Java中的强大工具

文章目录 网关过滤器简介网关过滤器的作用过滤器的生命周期实际应用示例权限过滤器解析 总结 网关过滤器简介 网关过滤器是一个位于应用程序和底层服务之间的组件&#xff0c;它截取进出网络请求&#xff0c;并提供对请求和响应进行处理的机制。它可以在请求到达目标服务之前或…

测试老鸟的职业生涯,从初识到功能再到自动化测试,他都经历了啥...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 张工&#xff1a;…

Qt/C++编写onvif工具(搜索/云台/预置位/OSD/录像存储)

一、前言 从最初编写这个工具开始的时间算起来&#xff0c;至少5年多&#xff0c;一直持续完善到今天&#xff0c;这个工具看起来小也不小大也不大&#xff0c;但是也是经历过无数个现场的洗礼&#xff0c;毫不夸张的说&#xff0c;市面上能够遇到的主流的厂商的设备&#xff…

深入探究 ReentrantLock 的应用和原理

博主介绍&#xff1a; ✌博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家✌ Java知识图谱点击链接&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; &#x1f495;&#x1f495; 感兴趣的同学可以收…

微信开发者工具-导入小程序项目会自动切换到小游戏打开出错的解决方案

微信开发者工具导入小程序项目会自动切换到小游戏打开出错&#xff0c; 提示Error 提示appid错误&#xff0c;如下图 错误 Error: INVALID_TOKEN...表示网络已断开&#xff0c; 检查开发工具是否连接到网络&#xff0c; 或注销重新登录开发工具试试 提示缺少文件 如果提示缺…

智能安全用电技术电气火灾监控的应用介绍 安科瑞 许敏

摘要&#xff1a;智能安全用电技术在智慧监狱的应用&#xff0c;可以提升监狱智能化管控水平和降低能耗。文章以智能安全用电技术为入手点&#xff0c;简要分析了监狱用电现状&#xff0c;论述了智能安全用电技术在智慧监狱中的具体应用&#xff0c;对智能安全用电技术在智慧监…

【面试题12】HTTP协议三次握手和四次挥手分别是什么

文章目录 一、概览二、三次握手2.1 第一步&#xff1a;客户端向服务端发送 SYN&#xff08;同步&#xff09;包2.2 第二步&#xff1a;服务端返回 ACK&#xff08;确认&#xff09;包和 SYN 包2.3 第三步&#xff1a;客户端返回 ACK&#xff08;确认&#xff09;包 三、四次挥手…

今日分享:音频格式转换软件

小林最近特别喜欢唱歌&#xff0c;不过总是遇到一些麻烦&#xff0c;例如自己录制的音频格式无法在其他设备上播放&#xff0c;或者想把某个歌曲转成适合自己播放的格式。这时候就需要一个好用的音频格式转换器啦&#xff01;小林找了一圈&#xff0c;终于找到了几款可爱又好用…

大数据应用——工程实践III

任务一&#xff1a;完成Hadoop集群部署前环境的准备工作 1.1 虚拟机环境准备 1. 安装虚拟机 2. 克隆虚拟机 3. 修改网络配置 4. 修改主机名和映射 5. 关闭防火墙 1.2 安装JDK 1.3 安装Hadoop 1.4 集群配置 1. 编写集群分发脚本xsync 2. 集群部署规划 表 1.1 hadoop101 …

一份老网工珍藏多年的网络配置笔记

我的网工朋友大家好 俗话说得好&#xff0c;好记性不如烂笔头。 学生时代&#xff0c;我们考试前最喜欢看的就是学霸笔记&#xff0c;但工作之后&#xff0c;却没有人会愿意借给你他们的珍藏笔记了。 今天&#xff0c;想给你分享一个老网工分享在网上的精选笔记&#xff0c;…

java 8 新特性讲解Optional类--Fork/Join 框架--新时间日期API--以及接口的新特性和注解

Optional类 到目前为止&#xff0c;臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前&#xff0c;为了解决空指针异常&#xff0c;Google公司著名的Guava项目引入了Optional类&#xff0c;Guava通过使用检查空值的方式来防止代码污染&#xff0c;它鼓励程序员写…

六、Docker容器数据卷

学习参考&#xff1a;尚硅谷Docker实战教程、Docker官网、其他优秀博客(参考过的在文章最后列出) 目录 前言一、容器数据卷1.1 容器数据卷1.2 怎么用1.3 能干什么 二、实践案例2.1 启动ubuntu容器实例&#xff0c;挂载目录&#xff1a;2.2 在ubuntu容器实例的/tmp/docker_data中…

Android 组件化架构思路

作者&#xff1a;往事一块六毛八 为什么要模块化/组件化 随着APP版本不断的迭代&#xff0c;新功能的不断增加&#xff0c;业务也会变的越来越复杂&#xff0c;APP业务模块的数量有可能还会继续增加&#xff0c;而且每个模块的代码也变的越来越多&#xff0c;这样发展下去单一…

GNS3报错“unable to open TAP device tap0 (No such file or directory)”解决

系统&#xff1a;MacOS 13.4 GNS3版本&#xff1a;2.2.35.1 Tunnelblick&#xff1a;3.8.8b 起因 在gns3中使用cloud节点创建 tap 接口连接路由器报错&#xff1a; 解决办法 借助 Tunnelblick工具创建 Tap 虚拟网卡。Tunnelblick下载地址&#xff1a; https://tunnelblic…

Kubernetes入门实战课-初始容器

Kubernetes入门实战课-初始容器 文章目录 Kubernetes入门实战课-初始容器课前准备初始容器Docker 的形态Docker 的安装Docker 的架构 容器的本质隔离原因与虚拟机区别隔离是怎么实现的 镜像创建容器镜像&#xff1a;如何编写正确、高效的Dockerfile镜像的内部机制是什么Dockerf…

MySQL数据库连接查询和存储过程

MySQL数据库连接查询和存储过程 一、连接查询1、内连查询2、左连接3、右链接 二、存储过程1、存储过程简介2、存储过程的优点3、语法3.1 参数分类3.2 不加参数的存储过程3.3 带参数的存储过程3.4删除存储过程3.5 事务和存储过程有什么区别&#xff1f; 三、总结1、连接查询2、存…

【Java项目中 利用Redis实现数据缓存】

文章目录 Java SpringBoot项目中 用Redis实现数据缓存1 环境搭建1.1 maven坐标1.2 配置文件1.3 配置类 2 实现缓存短信验证码3 缓存菜品数据4 Spring Cache 缓存框架4.1 Spring Cache介绍4.2 Spring Cache常用注解4.3 Spring Cache使用方式 Java SpringBoot项目中 用Redis实现数…

一文了解Moonbeam互连合约

什么是互连合约Connected Contracts 简单来说&#xff0c;互连合约是通过Moonbeam连接其他区块链上的一个或多个智能合约&#xff0c;其允许目标链上的用户通过跨链消息传递协议在一个应用中操作不同链上的资产或是服务。 通过互连合约方案&#xff0c;不同迥异的公链技术架构…

计算机内部总线详解

文章目录 总线概述地址总线位宽 数据总线位宽CPU性能指标 例题 总线概述 众所周知&#xff0c;总线就是用来帮助连接两个或多个计算机组件&#xff0c;用于数据传输&#xff0c;计算机内部存在三种类型的总线&#xff1a; 地址总线&#xff1a;用于传输指示计算机中的内存或外…

【uboot1】常用指令

文章目录 1.U-Boot命令之常用命令&#xff1a;7.U-Boot命令之EMMC和SD卡操作命令&#xff1a;一般EMMC和SD卡是同一个东西&#xff0c;没特殊说明&#xff0c;统一MMC来代指EMMC和SD卡8.U-Boot命令之内存操作命令&#xff1a;直接对DRAM进行读写操作&#xff0c;uboot命令中的数…