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

news/2024/10/28 1:25:01/

一. 概述

        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.Stubimplements 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.Stubimplements 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);}@UnsupportedAppUsageprivate 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() 方法

@Overridepublic 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) {....//1attachApplicationLocked(thread, callingPid, callingUid, startSeq);....
}

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

@GuardedBy("this")private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {// 2thread.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;//3sendMessage(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;//4handleBindApplication(data);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);//processLinkTurboMonitor();break;

调用handleBindApplication方法:

 private void handleBindApplication(AppBindData data) {.....try {//5mInstrumentation.callApplicationOnCreate(app);}

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

   //Instrumentation.java中public void callApplicationOnCreate(Application app) {app.onCreate();}//Application.javapublic void onCreate() {}

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

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


http://www.ppmy.cn/news/33256.html

相关文章

html+css 实现 熊猫样式

效果 html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible"…

如何通过openssl生成公钥和私钥?

1、生成RSA秘钥的方法 生成RSA秘钥的方法&#xff1a; openssl genrsa -des3 -out privkey.pem 2048 注&#xff1a;建议用2048位秘钥&#xff0c;少于此可能会不安全或很快将不安全。 这个命令会生成一个2048位的秘钥&#xff0c;同时有一个des3方法加密的密码&#xff0c…

Intel I210网卡

I210 supports AVB and ETF (Earliest TxTime First, Time-Based Scheduling), but does not support TSN (802.1Qbv). I225 (IGC) supports TSN, each Tx queue has the start_time and end_time, they are within [0, cycle_time]. 1 Intel I210网卡 1.1 PHY配置 Intel的LAN芯…

Python快速入门:类、文件操作、正则表达式

类、文件操作、正则表达式1. 类2. 文件操作3. 正则表达式1. 类 类是用来描述具有相同的属性和方法的集合&#xff0c;定义了该集合中每个对象共有的属性和方法&#xff0c;对象是类的实例&#xff0c;可以调用类的方法。 定义类时&#xff0c;如有父类&#xff0c;则写在类名…

Linux中的标准IO【上】

标准IO fopen() FILE * fopen(const char * restrict path, const char * restrict mode);第一个参数表示被打开文件路径&#xff0c;第二个参数表示打开文件模式—模式不同&#xff0c;对同一个文件有不同的更改r和r模式下不存在文件则也无法创建&#xff0c;其余模式若文件本…

js类型转换

类型转换 1.字符串转换 字符串转换在原来值的基础上加上 "" let num 1 num String(num) // "1"String(false) // "false"2.数字转换 在算数函数和表达式中&#xff0c;会自动进行数字转换。其自动完成的数字转换为隐式转换&#xff0c;也可…

CSDN-猜年龄、纸牌三角形、排他平方数

猜年龄 原题链接&#xff1a;https://edu.csdn.net/skill/practice/algorithm-a413078fb6e74644b8c9f6e28896e377/2258 美国数学家维纳(N.Wiener)智力早熟&#xff0c;11岁就上了大学。他曾在1935~1936年应邀来中国清华大学讲学。 一次&#xff0c;他参加某个重要会议&#xf…

学习其他人的代码,成为更好的程序员

学习其他人的代码&#xff0c;成为更好的程序员1. 广泛阅读2. 分析代码3. 记笔记4. 实验5. 分享你的发现6. 结论参考如何成为一名更好的Python程序员??? 学习编码是一个持续的过程&#xff0c;需要实践、实验和向他人学习的意愿。提高编码技能的最佳方法之一是学习他人的代…