Android 进程间通信机制(三) 系统进程与应用进程通信

一. 概述

        Android中有一个重要的系统进程(system_server),运行着系统中非常重要服务(AMS, PMS, WMS等), 针对Activity而言,系统进程需要不断地调度Activity执行,管理Activity的状态; 每一个APK都需要运行在一个应用进程中,有自己独立的内存空间, 针对Activity而言,应用进程需要执行Activity生命周期函数(onCreate, onStart, …onDestroy)的具体逻辑。

        应用进程需要频繁与系统进程通信,譬如Activity生命周期的各个方法都是需要经过系统进程调度的,只是在应用进程进行回调,这就需要从系统到应用的跨进程调用; 应用进程有需要将当前Activity的状态告诉系统进程,以便系统将Activity驱动到下一个状态,这就需要从应用到系统的跨进程调用。

        应用进程与系统进程相互通信的手段,就是利用前面文章介绍的Binder机制, 本文要分析的不是Binder机制的内在原理,而是应用进程与系统进程建立在Binder之上通信的业务逻辑,Android为此设计了两个Binder接口:

IApplicationThread:  作为系统进程请求应用进程的接口
IActivityManager:     作为应用进程请求系统进程的接口。

本文内容基于Android10的源码分析总结.

示例图:

             左侧为系统进程                                                                                                    右侧为应用进程

二 . AMS是什么?

比如在你的应用中(进程A)中启动一个Activity界面,肯定会调用startActivity这个方法, 然后跨进程和AMS通信, 接着AMS会管理这个Activity的生命周期方法,  先来说一说AMS是什么的?

1. 从java角度来看,AMS就是一个java对象

它实现了Ibinder接口,所以它是一个用于进程间通信的接口,这个对象初始化是在systemServer.java 的run()方法里面.

public Lifecycle(Context context) { 
    super(context); 
    mService = new ActivityManagerService(context); 
} 
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

2. AMS是一个服务
 ActivityManagerService从名字就可以看出,它是一个服务,用来管理Activity,而且是一个系统服务,就是包管理服务,电池管理服务,震动管理服务等。

3. AMS是一个Binder

AMS实现了Ibinder接口,所以它是一个Binder,这意味着他不但可以用于进程间通信,还是一个线程,因为一个Binder就是一个线程。
 

如果我们运行一个Hello world的安卓应用程序, 这个进程至少要启动4个线程

1. main线程,只是程序的主线程,也是日常用到的最多的线程,也叫UI线程,因为android的组
件是非线程安全的,所以只允许UI/MAIN线程来操作。

2. GC线程,java有垃圾回收机制,每个java程序都有一个专门负责垃圾回收的线程.

3. Binder1  就是我们的ApplicationThread,这个类实现了Ibinder接口,用于进程之间通信,具体
来说,就是我们程序和AMS通信的工具.

4.  Binder2 就是我们的ViewRoot.W对象,他也是实现了IBinder接口,就是用于我们的应用程序和
wms通信的工具。

 static class W extends IWindow.Stub {
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;

        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
        }
.....
.....
}

WMS就是WindowManagerServicer ,和AMS差不多的概念,不过他是管理窗口的系统服务。

三.  两个接口

既然 IApplicationThread 和 IActivityManager 是两个接口, 我们来看看哪些类实现它们

3.1 IApplicationThread接口

只要继承了android.os.IInterface 则说明它是一个Binder对象, 是用于进程间通信的接口.

public interface IApplicationThread extends android.os.IInterface{
}

1. 先看IApplicationThread , 在 AS中 ctrl + T 快捷键(Eclipse风格)

 实现这个接口的类为: ApplicationThread   它是 ActivityThread.java的内部类

private class ApplicationThread extends IApplicationThread.Stub {
......
......
}

上图中,例外的 (Defaut in IApplicationThread)   (Proxy in stub in IApplicationThread) (Stub in IApplicationThread) 是 系统编译 IApplicationThread.aidl 文件后自动生成的代码文件,  里面的核心内容就是 1. 静态抽象类 Stub;  2. Stub的静态内部类Proxy ;

在看看 IApplicationThread.aidl 的写法, 接口文件前修饰关键字为oneway

/**
 * 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.

    系统进程与应用进程通信的系统专用API
 *
 * {@hide}
 */
oneway interface IApplicationThread {
.....
}

oneway 表示在远程调用时(是异步调用,即客户端不会被阻塞), 它只是发送事务数据并立即返回. 

oneway修饰了的方法不可以有返回值,也不可以有带out或inout的参数。可以去看看 IApplicationThread.aidl文件中,定义的方法, 都是void类型, 没有返回值.

这也对应 AMS 给应用进程发送消息后, 肯定继续做自己的事情,  不会被阻塞,  因为AMS是四大组件的管理者,同一时刻好多事情等着做了, 要是和普通的[(in  out inout), 他们是同步调用, 调用方执行 mRemote.transact 时, 会挂起, 然后等待服务端reply值] 一样的话,  那么系统AMS这么重要的服务岂不是卡的要死. 系统运行也不流畅.

google设计它的使命是什么呢?

IApplicationThread接口的具体业务实现类是ApplicationThread, 它是ActivityThread的一个内部类,ApplicationThread负责响应系统进程发起的请求,这些请求大部分都是需要调度在应用进程的主线程执行,而ActivityThread是应用进程的主线程,通过Handle往主线程发送消息,ApplicationThread就轻松将具体执行任务的工作转交给了主线程。

3.2 IActivityManager接口

同样使用ctrl + T 查看实现类

  实现这个接口的类为: ActivityManagerService.java ,  AMSEx.java 为 AMS的继承类

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

.....

}

 public static abstract class Stub extends android.os.Binder implements android.app.IActivityManager
  {
    .....
}

根据上面"AMS是什么"介绍, 其实你也可以把AMS理解成运行在 system_server进程中的一个线程.

当然 WMS  PMS 也可以理解成运行在system_server进程中的线程.

Binder 在进程间通信传递的数据限制大小为 1M - 8K

根据以前的文章(Android 进程间通信机制(二) mmap 原理)查看dev/binder分配内存的命令:

cat  proc/进程号/maps  | grep  dev/binder 

我们可以查看 system_server进程 dev/binder 驱动文件分配的内存空间也是为 1M - 8K

Android 系统底层基于 Linux Kernel, 当 Kernel 启动过程会创建 init 进程, 该进 程是所有用户空间的鼻祖, init 进程会启动 servicemanager(binder 服务管家), Zygote 进程(Java 进程的鼻祖). Zygote 进程会创建 system_server 进程以及各 种 app 进程,下图是这几个系统重量级进程之间的层级关系。

用自己的手机查看进程号: 

[号外]:  servicemanager 进程分配dev/binder的内存空间为 128K , 结论支撑代码为

frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024);

google设计128k的理由,  推测是和servicemanager进程通信的业务包括

请求服务(如 ServiceManager.getService(Context.USAGE_STATS_SERVICE))    和 

添加服务等轻量级的工作

ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_HIGH);
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this),
                        /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));

所以在binder分配的空间上只需要128K就可以满足. 

四.  握手通信全流程

4.1 从应用进程到系统进程

在ActivityThread创建的时候,会将自己的ApplicationThread绑定到AMS中, 源码流程如下:

1. 首先在 ActivityThread.java 的main方法中

 public static void main(String[] args) {

        ActivityThread thread = new ActivityThread();

        .....
        //步骤一
        thread.attach(false, startSeq);

}



 @UnsupportedAppUsage
    private void attach(boolean system, long startSeq) {


        final IActivityManager mgr = ActivityManager.getService();

        //步骤二 通过 IActivityManager接口跨进程通信, 把 mAppThread(ApplicationThread对象)传递到AMS中
        mgr.attachApplication(mAppThread, startSeq);


}

应用进程作为客户端,通过IAcitivtyManager接口发起了跨进程调用, 跨进程传递的参数mAppThread就是IApplicationThread的实例, 执行流程从应用进程进入到系统进程

2 接下来就到AMS中去了,调用 AMS.attachApplication() 方法

@Override
    public void attachApplication(IApplicationThread thread, long startSeq) {
        if (thread == null) {
            throw new SecurityException("Invalid application interface");
        }
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }

AMS作为IActivityManager接口的服务端实现,会响应客户端的请求,最终AMS.attachApplication()函数会被执行, 该函数接收跨进程传递过来的IApplicationThread实例,将其绑定到系统进程。 具体的绑定操作细节此处不表,我们只需要知道AMS中维护了所有进程运行时的信息(ProcessRecord),一旦发生了应用进程的绑定请求, ProcessRecord.thread就被赋值成应用进程的IApplicationThread实例,这样一来,在AMS中就能通过该实例发起向应用进程的调用。

4.2 从系统进程到应用进程

在AMS.attachApplication()的过程中,会有一些信息要传递给应用进程,以便应用进程的初始化.

public void attachApplication(IApplicationThread thread, long startSeq) {
    ....
    //1
    attachApplicationLocked(thread, callingPid, callingUid, startSeq);
    ....
}

此时,AMS会反转角色,即系统进程作为客户端,通过IApplicationThread接口向应用进程发起调用。

@GuardedBy("this")
    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        
      // 2
      thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions);
}

2中的 thread就是 实现了IApplicationThread的接口对象,通过它跨进程通信,就跳转到ActivityThread.bindApplication 方法中去了

 public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs, 
.......
.......
            data.contentCaptureOptions = contentCaptureOptions;
            //3
            sendMessage(H.BIND_APPLICATION, data);

}

接下来会到消息处理的地方

  public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    //4
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    //processLinkTurboMonitor();
                    break;

调用handleBindApplication方法:

 private void handleBindApplication(AppBindData data) {

    .....
    try {
         //5
         mInstrumentation.callApplicationOnCreate(app);
            
}

最终会走到 Application的 onCreate()方法中去

   //Instrumentation.java中

     public void callApplicationOnCreate(Application app) {
        app.onCreate();
    }


    //Application.java
    public void onCreate() {
    }

        ApplicationThread作为IApplicationThread接口的服务端实现,运行在应用进程中, 然后ApplicationThread.bindApplication()会被执行,完成一些简单的数据封装(AppBindData)后,通过Handler抛出BIND_APPLICATION消息。这一抛,就抛到了主线程上,

ActivityThread.handleBindApplication()会被执行,接着就到了各位观众较为熟悉的Application.onCreate()函数。历经应用进程和系统进程之间的一个完整来回,总算是创建了一个应用程序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【动态规划】最长上升子序列(单调队列、贪心优化)

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…

jvm-题库

1、JVM内存模型 JVM内存区域总共分为两种类型 线程私有区域&#xff1a;程序计数器、本地方法栈和虚拟机栈 线程共享区域&#xff1a;堆&#xff08;heap&#xff09;和方法区 特征 线程私有区域&#xff1a;依赖用户的线程创建而创建、销毁而销毁&#xff0c;因用户每次访问都…

带头双向循环链表

在前面我们学习了单链表&#xff0c;发现单链表还是有一些不够方便&#xff0c;比如我们要尾插&#xff0c;需要遍历一遍然后找到它的尾&#xff0c;这样时间复炸度就为O(N),现在我们引入双向带头链表就很方便了&#xff0c;我们先看看它的结构。通过观察&#xff0c;我们发现一…

Vue全新一代状态管理库 Pinia【一篇通】

文章目录前言1. Pinia 是什么&#xff1f;1.1 为什么取名叫 Pinia?1.2. 为什么要使用 Pinia ?2. 安装 Pinia2.1.创建 Store2.1.1. Option 类型 Store2.1.2 Setup 函数类型 Store2.1.3 模板中使用3. State 的使用事项&#xff08;Option Store &#xff09;3.1 读取 State3.2 …

EEPROM芯片(24c02)使用详解(I2C通信时序分析、操作源码分析、原理图分析)

1、前言 (1)本文主要是通过24c02芯片来讲解I2C接口的EEPROM操作方法&#xff0c;包含底层时序和读写的代码&#xff1b; (2)大部分代码是EEPROM芯片通用的&#xff0c;但是其中关于某些时间的要求&#xff0c;是和具体芯片相关的&#xff0c;和主控芯片和外设芯片都有关系&…

一天吃透TCP面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

vue模板语法

src目录文件说明&#xff1a; 1&#xff0c;数据绑定{{}} 数据绑定最常见的形式就是使用{{}}&#xff08;双花括号&#xff09;语法的文本插值 在template中使用{{}}文本插值语法中&#xff0c;设置一个变量&#xff0c;再在script中引入data函数&#xff0c;在return中进行数…

接口测试和性能测试有什么区别?我敢打赌你一定不知道

目录 一、什么是接口测试 二、接口测试原理 三、接口测试步骤 四、什么是性能测试 五、性能测试步骤 六、接口测试和性能测试的区别 一、什么是接口测试 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点…

2023最全Python+Selenium环境搭建教程-你绝对想不到有这么简单!

还有视频版本结合项目实战介绍&#xff0c;轻松学习&#xff01; PythonSelenium自动化测试环境搭建Web自动化测试全套教程_哔哩哔哩_bilibiliPythonSelenium自动化测试环境搭建Web自动化测试全套教程共计180条视频&#xff0c;包括&#xff1a;1、Web自动化测试需求和挑战、2…

深度学习-Tensorflow使用Keras进行模型训练

本文以FasionMNIST/加州房价数据集为例&#xff0c;介绍KerasAPI进行分类问题/回归问题模型训练的方法Tensorflow版本Tensorflow和keara都需要2.0及以上版本import tensorflow as tf from tensorflow import keras print(tf.__version__) print(keras.__version__)分类MLP构建数…

AI_Papers周刊:第六期

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 2023.03.13—2023.03.19 文摘词云 Top Papers Subjects: cs.CL 1.UPRISE: Universal Prompt Retrieval for Improving Zero-Shot Evaluation 标题&#xff1a;UPRISE&#xff1a;改进零样本评估…

要是早看到这篇文章,你起码少走3年弯路,20年老程序员的忠告

文章目录前言一、程序员的薪资是怎么样的&#xff1f;二、我现在的情况适合做程序员吗&#xff1f;三、大学期间到底应该学些什么&#xff1f;四、工作还是考研&#xff1f;五、总结前言 我是龙叔&#xff0c;一名工作了20多年的退休老程序员。 如果你在工作之前看到这篇文章…

【AI大比拼】文心一言 VS ChatGPT-4

摘要&#xff1a;本文将对比分析两款知名的 AI 对话引擎&#xff1a;文心一言和 OpenAI 的 ChatGPT&#xff0c;通过实际案例让大家对这两款对话引擎有更深入的了解&#xff0c;以便大家选择合适的 AI 对话引擎。 亲爱的 CSDN 朋友们&#xff0c;大家好&#xff01;近年来&…

libcurl库访问人工智能平台之人脸识别

一、前言上一篇文章我们调用libcurl库去访问了百度&#xff0c;访问的是http协议的百度云主页。那么现在我们要基于翔云人工智能平台来实现人脸识别&#xff0c;具体的操作大概就是我们在linux下调用libcurl库去访问翔云人工智能平台&#xff0c;然后实现我们想要的两张人脸图片…

FPGA纯verilog实现RIFFA的PCIE通信,提供工程源码和软件驱动

目录1、前言2、RIFFA简介RIFFA概述RIFFA架构RIFFA驱动3、vivado工程详解4、上板调试验证并演示5、福利&#xff1a;工程代码的获取1、前言 PCIE是目前速率很高的外部板卡与CPU通信的方案之一&#xff0c;广泛应用于电脑主板与外部板卡的通讯&#xff0c;PCIE协议极其复杂&…

【Linux】基本指令介绍

前言从今天开始&#xff0c;我们一起来学习Linux的相关知识&#xff0c;今天先来介绍怎么登录Linux&#xff0c;并且介绍一些Linux的基本指令。使用 XShell 远程登录 Linux很多同学的 Linux 启动进入图形化的桌面. 这个东西大家以后就可以忘记了. 以后的工作中 没有机会 使用图…

蓝桥杯刷题冲刺 | 倒计时21天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;马上就要蓝桥杯了&#xff0c;最后的这几天尤为重要&#xff0c;不可懈怠哦&#x1f43e; 文章目录1.迷宫1.迷宫 题目 链接&#xff1a; 迷宫 - 蓝桥云课 (lanqiao.cn) 本题为填空题&#xff0c;只…

Three.js——learn02

Three.js——learn02Three.js——learn02通过轨道控制器查看物体OrbitControls核心代码index2.htmlindex.cssindex2.jsresult添加辅助器1.坐标轴辅助器AxesHelper核心代码完整代码2.箭头辅助器ArrowHelper核心代码完整代码3.相机视锥体辅助器CameraHelper核心代码完整代码Three…

近期投简历、找日常实习的一些碎碎念(大二---测试岗)

嘿嘿嘿&#xff0c;我又回来了&#xff0c;相信不少兄弟已经发现我似乎已经断更了好久&#xff0c;哈哈&#xff0c;我是尝试去找实习&#xff0c;投简历面试去了。 先说一下背景。 目录 背景 求职进行中 简历 投递和沟通 收获和感受 背景 博主&#xff0c;大二软件工程…

Arthas工具的基本使用

介绍 Arthas 是Alibaba开源的Java诊断工具&#xff0c;深受开发者喜爱。在线排查问题&#xff0c;无需重启&#xff1b;动态跟踪Java代码&#xff1b;实时监控JVM状态。Arthas支持JDK 6&#xff0c;支持Linux/Mac/Windows&#xff0c;采用命令行交互模式&#xff0c;同时提供丰…
最新文章