牛年犇犇犇,刚刚完成
AMS
一半的学习进度,篇幅确实有点大,不过先更上两篇吧
我们不止一次提到Android
一直想弱化进程的存在,但是,Android
毕竟是建立在Linux
系统之上,基础的运行环境还是由进程组成。
我们前面已经介绍,所有的Android
的应用程序都是由Zygote
进程fork
而来,因此,构成应用进程的底层基础,像虚拟机、动态库等都是相同的。
有了从Zygote
中继承来的通用基础环境,Android
还在Java层
建立一套框架来管理运行的组件来进一步弱化进程的存在感。
不过由于每个应用的上层配置都不相同,因此,这个框架不能提前在Zygote
中完全建立好后继承,只能在应用启动时创建。
而这套框架就构成了Android
应用开发的基础,而这套框架的核心就是大名鼎鼎的ActivityManagerServie
。
应用进程的组成
学习
ActivityManagerServie
前我们先简单了解下Android
应用进程的组成
组成Android
应用进程的核心是ActivityThread
类,这个类包含了应用框架中其他重要的类。相关类变量声明如下:
public final class ActivityThread extends ClientTransactionHandler {
...
// 一个Binder实体对象,AMS 通过它来调用应用的接口
final ApplicationThread mAppThread = new ApplicationThread();
Application mInitialApplication;
// 用来保存应用中的 Activity 对象
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
// 用来保存应用中的 Service 对象
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
// 用来保存应用中的 ContentProvider 对象
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();
// 用来保存包含代码的的应用
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
// 用来保存只包含资源文件的应用
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
// 应用资源管理对象
private final ResourcesManager mResourcesManager;
// 上下文对象
private ContextImpl mSystemContext;
private ContextImpl mSystemUiContext;
}
我们先来看下ApplicationThread
类
ApplicationThread
的作用
ApplicationThread
是ActivityThread
的一个内部类,它和ActivityThread
一样,虽然名字中都包含了Thread
,但是它们并不是继承自线程类Thread
。
ApplicationThread
是一个Binder
服务类,Android
的ActivityManagerService
操作应用就是通过ApplicationThread
来完成的。
我们先看下AIDL
文件内容,路径frameworks/base/core/java/android/app/IApplicationThread.aidl
,片段如下:
oneway interface IApplicationThread {
......
void scheduleSleeping(IBinder token, boolean sleeping);
......
}
请留意
AIDL
中的oneway
关键字,修饰interface
意味着修饰接口中的所有方法
用
oneway
修饰表示一个异步过程,调用时不需要等待返回值
ApplicationThread
类的片段如下:
private class ApplicationThread extends IApplicationThread.Stub {
......
public final void scheduleSleeping(IBinder token, boolean sleeping) {
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
......// 省略大量接口
}
ApplicationThread
中接口的实现都是把Binder调用
转换成消息来排队处理。- 以上面的
scheduleSleeping()
为例,只是发送了一个SLEEPING
的消息 - 所有消息的处理都集中在
Handler
对象mH
的handleMessage()
方法中集中处理。处理部分如下:public void handleMessage(Message msg) { switch (msg.what) { ... case SLEEPING: handleSleeping((IBinder)msg.obj, msg.arg1 != 0); break; ... } }
- 最后通过
ActivityThread
的handleSleeping()
来执行
- 最后通过
在源码中,ApplicationThread
中的接口通常都是以schedule*
开头,而真正处理的方法以handle*
开头,很容易分辨。
为什么要这样设计呢?
主要是避免方法在调用和执行时出现耗时过长影响整个系统的运行:
- 对于调用端:还记得
IApplicationThread.aidl
中的oneway
么- 用
oneway
修饰就表明客户端进行Binder
调用时不会等待服务端结果的返回(这部分可以去看之前的Binder-3-原理部分)
- 用
- 对于服务端:通过
sendMessage
的方式将Binder调用
转化为消息的异步处理- 消息处理部分交给了
H mH
这个Handler
- 在
ActivityThread
的静态main()
方法中,会通过Looper.loop()
进入消息的循环处理
- 消息处理部分交给了
理解应用的Context
Context
的字面含义是上下文环境。应用的上层代码通过Context
类提供的接口来操作Android
的组件和资源。在Android
的应用程序中,Context
无处不在,很多接口都要求使用Context
对象作为参数。
Android
的应用本质上是一系列组件加上资源文件的容器,但是,这些组件的使用方式各不相同,而且比较复杂,Context
类的作用就是把这些细节封装起来,这样,即使开发人员不了解组件的运行原理也能使用它们。从某种意义上将,Context
包含了整个应用框架的操作接口。
对于应用开发者来说,大部分代码都在Activity
或者Service
的继承类中实现。为了方便应用使用,这两个类的父类也是Context
。
Context
是一个抽象类,Android
相关类的继承关系设计如下:
Android
中Context
有两个实现类
ContextImpl
:Context
真正的实现类- 目前
Android
中唯一的业务实现类 - 集成了应用框架的一些核心对象,如
ActivityThread
对象
- 目前
ContextWrapper
:方法的实现只是转调成员变量mBase
的方法mBase
本身也是Context
类型对象,它的具体类型是ContextImpl
上面这种设计模式称为proxy
模式或者wrapper
模式,它的好处就是将接口和实现分离,可以动态的切换多种实现。
Activity
类有些特殊(毕竟承载了应用的显示部分),Android
又为它抽象出来了一个ContextThemeWrapper
类
- 这样的目的是为了实现
Activity
中单独的Theme
- 一个应用中可以有一套全局的
Theme
,同时每个Activity
还可以有自己的Theme
Application
类
Application
类也是继承ContextWrapper
Application
类可以看做是应用本身的抽象,一个应用进程只有一个Application
对象,在应用启动时由框架创建。
Application
本身没有实现太多功能,它的主要作用是提供一些回调接口来通知应用进程状态的变化。相关的方法如下:
public class Application extends ContextWrapper implements ComponentCallbacks2 {
// 在应用创建时调用
public void onCreate();
// 在应用销毁时调用
public void onTerminate();
// 在系统配置发生变化时调用
public void onConfigurationChanged(Configuration newConfig);
// 在系统内存不足时调用
public void onLowMemory();
// 在系统要求应用释放多余内存时调用
public void onTrimMemory(int level);
}
应用中可以定义Application
的子类。子类必须在AndroidManifest.xml
的<application/>
标签中定义,这样系统才能在生成应用对象时使用子类来替换Application
类。
<application/>
标签在Android SDK
文档中有详细的介绍,可以先简单阅读标签的属性描述,不然到后面理解ActivityManagerService
的业务逻辑时可能会有障碍。
以allowTaskReparenting
为例,它的描述是这样的:
- 当下一次将启动 Activity 的任务转至前台时,Activity 是否能从该任务转移至与其有相似性的任务
- “true”表示可以转移,“false”表示仍须留在启动它的任务处。
- 默认值为“false”。
- 对于
<activity>
标签有其自己的allowTaskReparenting
属性,该属性可以替换此处设置的值。allowTaskReparenting
的精彩讲解可以看这里,扔物线大神视频
剩下的属性大家可以参考官方说明。传送门:
<application/>
标签
Android 框架的核心-AMS
服务
ActivityManagerService
是Android Framework
的核心,它管理着Android系统的4大组件:Activity
、Service
、ContentProvider
和Broadcast
。
前面已经介绍了,Android希望模糊进程的作用,取而代之以组件的概念,ActivityManagerService
正是这一理念的实现。ActivityManagerService
除了管理组件外,同时也管理和调度所有的用户进程
为了方便,
ActivityManagerService
后面就用AMS
来表示了哈
AMS
在SystemServer
中的启动流程
我们已经知道
AMS
服务运行在SystemServer
进程中。
AMS
在SystemServer
的主要流程如下:
private void startBootstrapServices() {
// 创建 AMS 服务
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
......
// 调用 AMS 的 setSystemProcess
mActivityManagerService.setSystemProcess();
}
private void startOtherServices() {
// 调用 AMS 的 systemReady
mActivityManagerService.systemReady(...);
}
上面的代码重点分为三部分:
-
通过
SystemServiceManager.startService
方法进行AMS
创建- 方法会根据传入的
Class
对象来创建对应类的实例对象 - 这里传入的
ActivityManagerService.Lifecycle
类,类结构如下:
public static final class Lifecycle extends SystemService { private final ActivityManagerService mService; public Lifecycle(Context context) { super(context); // 创建 ActivityManagerService 实例对象 mService = new ActivityManagerService(context); } ...... }
- 可以看到,在
Lifecycle
的构造方法中初始化了AMS
对象
- 方法会根据传入的
-
调用
AMS
的setSystemProcess
方法 -
调用
AMS
的systemReady
方法
这三部分我们详细来看下
AMS
的初始化
AMS
的构造方法如下:
public ActivityManagerService(Context systemContext) {
......
mContext = systemContext;
mFactoryTest = FactoryTest.getMode();
// 获取运行在 SystemServer 中的 ActivityThread 对象
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
......
// 创建用于处理消息的线程和Handler对象
// 这里创建了两个消息处理线程:mHandlerThread 和 mProcStartHandlerThread
mHandlerThread = new ServiceThread(TAG,
THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = mInjector.getUiHandler(this);
mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
mProcStartHandlerThread.start();
mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
......
// 创建管理广播的数据结构
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", BROADCAST_BG_TIMEOUT, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
// 创建管理组件 Service 的对象
mServices = new ActiveServices(this);
// 创建管理组件 Provider 的对象
mProviderMap = new ProviderMap(this);
mAppErrors = new AppErrors(mUiContext, this);
// 获取系统的 system 和 data 目录
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
// 创建 BatteryStatsService 服务
mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
......
// 创建 ProcessStatsService 服务
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
// 创建 AppOpsService 服务
mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
// 打开 urigrants.xml 文件
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
// 创建 UserController ,UserController 对应的构造方法中会设置 USER_SYSTEM 用户作为第一个用户
mUserController = new UserController(this);
// 创建 VrController ,用来进行部分 VR 状态的监听和切换工作
mVrController = new VrController(this);
// 获取 opengles 的版本
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
......
// 创建 Activity 管理对象
mStackSupervisor = createStackSupervisor();
......
// 创建 Intent 防火墙
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
......
// 创建 CPU 使用情况的统计线程
mProcessCpuThread = new Thread("CpuTracker") {
...
};
......
// 将服务添加到 WatchDog 进行监听
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
......
}
AMS
构造方法的主要作用是创建出了4大组件Activity
、Service
、ContentProvider
和Broadcast
的管理对象和一些辅助服务,没有很复杂的逻辑,注释很详细啦
AMS
的setSystemProcess
方法
SystemServer
中创建完AMS
对象后,会调用它的setSystemProcess
方法,代码如下:
public void setSystemProcess() {
try {
// =============== 将一些监测服务添加到 ServiceManager ===============
// AMS 服务
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
// ProcessStats 是可以用来dump每个进程内存使用情况的服务
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
// MemBinder 是可以用来dump每个进程内存信息的服务
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH);
// GraphicsBinder 是可以用来dump每个进程图形加速状态的服务
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
// DbBinder 是可以用来dump每个进程数据库状态的服务
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
// CPU 信息
ServiceManager.addService("cpuinfo", new CpuBinder(this),
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
}
// PermissionController 是用来检查 Binder 调用权限的服务
ServiceManager.addService("permission", new PermissionController(this));
// ProcessInfoService 是可以用来获取进程相关信息的服务
ServiceManager.addService("processinfo", new ProcessInfoService(this));
// 获取 framework-res.apk 的 ApplicationInfo 对象
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
// 将 SystemServer 进程本身添加到 AMS process 的管理中
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Unable to find android system package", e);
}
// 启动应用操作监听
mAppOpsService.startWatchingMode(......);
}
SystemServer
调用setSystemProcess
方法的主要工作是向ServiceManager
注册一些服务。需要注意的是:
- 通过
getApplicationInfo
方法获取了framework-res.apk
的ApplicationInfo
对象 - 然后使用
newProcessRecordLocked()
方法将ApplicationInfo
对象转化为ProcessRecord
对象,并将其添加到AMS
的进程管理体系中- 在
SystemServer
章节的createSystemContext()
介绍过,SystemServer
可以当做是framework-res.apk
的应用进程。 - 这样,对于
AMS
而言,它对系统进程的管理就不会存在遗漏了
- 在
AMS
的systemReady
方法
SystemServer
在启动完所有服务之后,将调用AMS
的systemReady
方法。这个方法时Android进入用户交互阶段前最后进行的准备工作。
systemReady
方法比较长,我们一点一点来分析
AMS
相关服务的准备阶段
相关代码如下:
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
synchronized(this) {
if (mSystemReady) {
if (goingCallback != null) {
goingCallback.run();
}
return;
}
......
// 通知 VrController 注册 VrModeStateListener
mVrController.onSystemReady();
// 通知 UserController 装载用户的 Profile 信息
mUserController.onSystemReady();
// 通知 RecentTasks 重建带有 persisitent 标记的 Task
mRecentTasks.onSystemReadyLocked();
// 通知 AppOpsService 启动 APP 权限监听
mAppOpsService.systemReady();
// 系统相关服务准备完成
mSystemReady = true;
}
......
// 查找待清理进程
ArrayList<ProcessRecord> procsToKill = null;
synchronized(mPidsSelfLocked) {
for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
ProcessRecord proc = mPidsSelfLocked.valueAt(i);
// 此处判断进程是否有 FLAG_PERSISTENT 标志
if (!isAllowedWhileBooting(proc.info)){
if (procsToKill == null) {
procsToKill = new ArrayList<ProcessRecord>();
}
// 将没有 FLAG_PERSISTENT 标志的进程添加到清理集合中
procsToKill.add(proc);
}
}
}
// 清理集合中的进程
synchronized(this) {
if (procsToKill != null) {
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
removeProcessLocked(proc, true, false, "system update done");
}
}
// 进程清理完成
mProcessesReady = true;
}
Slog.i(TAG, "System now ready");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis());
......
}
上面注释比较详细,简单总结主要流程如下:
- 调用
VrController
、UserController
、RecentTasks
、AppOpsService
服务的*SystemReady()
方法进行对应服务的初始化工作- 对于上面的4个服务,任何一个的知识点都可以单独写一篇博文了,这里我们先略过哈,大局为重
- 从
mPidsSelfLocked
集合中查找非FLAG_PERSISTENT
标记的进程,并将其清理mPidsSelfLocked
集合存放的是已经运行了的进程- 这些基本都是通过
ActivityThread.attach()
加入mPidsSelfLocked
集合的 FLAG_PERSISTENT
标记的进程说明后面很快还会启动,没有必要清理
在
SystemServer
启动的过程会执行PMS
的updatePackagesIfNeeded()
方法,这个方法会发送一个ACTION_PRE_BOOT_COMPLETED
的广播。
所以,如果我们的应用响应这个ACTION_PRE_BOOT_COMPLETED
并且加入FLAG_PERSISTENT
标志,那么就可以存活下来
FactoryMode
检测阶段
相关代码如下
synchronized(this) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
// 处于工厂模式,则查找对应的应用程序
ResolveInfo ri = mContext.getPackageManager()
.resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
STOCK_PM_FLAGS);
CharSequence errorMsg = null;
if (ri != null) {
ActivityInfo ai = ri.activityInfo;
ApplicationInfo app = ai.applicationInfo;
// 判断找到的程序是否为系统应用
if ((app.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
mTopAction = Intent.ACTION_FACTORY_TEST;
mTopData = null;
mTopComponent = new ComponentName(app.packageName,
ai.name);
} else {
errorMsg = mContext.getResources().getText(
com.android.internal.R.string.factorytest_not_system);
}
} else {
errorMsg = mContext.getResources().getText(
com.android.internal.R.string.factorytest_no_action);
}
if (errorMsg != null) {
......
// 错误消息不为空,发送对应的信息
Message msg = Message.obtain();
msg.what = SHOW_FACTORY_ERROR_UI_MSG;
msg.getData().putCharSequence("msg", errorMsg);
mUiHandler.sendMessage(msg);
}
}
}
对于Android
产品,如手机、电视等,需要进入工厂模式来执行检测程序,工厂模式下运行的程序需要响应Intent.ACTION_FACTORY_TEST
。
因此,当检测到处于工厂模式时,会去查找响应该Intent
的程序,并放置到mTopComponent
中,这样将会启动对应的测试应用。如果找不到,则发送异常Message
应用启动阶段
相关代码如下:
// 更新设置信息,包括是否支持画中画、多窗口等属性
retrieveSettings();
final int currentUserId = mUserController.getCurrentUserId();
synchronized (this) {
readGrantedUriPermissionsLocked();
}
......
// 执行回调参数
if (goingCallback != null) goingCallback.run();
// 通知其他服务,当前用户开始运行
mSystemServiceManager.startUser(currentUserId);
synchronized (this) {
// 启动带有 FLAG_PERSISTENT 标记的应用
startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
mBooting = true;
......
// 启动 Home 应用
startHomeActivityLocked(currentUserId, "systemReady");
......
long ident = Binder.clearCallingIdentity();
try {
// 发送 ACTION_USER_STARTED 广播
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,...);
// 发送 ACTION_USER_STARTING 广播
intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,...);
} catch (Throwable t) {
Slog.wtf(TAG, "Failed sending first user broadcasts", t);
} finally {
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
......
}
这一段的代码主要作用是
- 更新设置,判断是否支持画中画、多窗口等信息
- 执行
systemReady
方法参数goingCallback
的回调方法run()
- 回调方法是在
SystemServer
中定义的
- 回调方法是在
- 启动带有
FLAG_PERSISTENT
标记的应用- 具体启动应用是通过
addAppLocked()
方法,后面详细介绍
- 具体启动应用是通过
- 启动
Home
应用- 查找并启动能够响应
android.intent.action.MAIN
的Activity
- 查找并启动能够响应
Process
管理
虽然Android
在应用开发中可以弱化进程的概念,但是在AMS
中,还必须管理和调度进程。AMS
对进程的管理,主要体现在两个方面:
- 动态调整进程在
mLruProcesses
列表的位置- 核心方法:
updateLruProcessLocked
- 核心方法:
- 调整进程的
oom_adj
的值- 核心方法:
updateOomAdjLocked
- 核心方法:
这两项调整和Android
的内存回收机制有关。当内存不足时,系统会关闭一些进程来释放内存。
进程的启动
前面介绍了,AMS
中启动带有FLAG_PERSISTENT
标记的应用是通过addAppLocked()
方法,代码如下:
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, String abiOverride) {
ProcessRecord app;
// isolated 为 true 表示要启动一个新的进程
if (!isolated) {
// 在已经启动的进程列表中查找
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
info.uid, true);
} else {
app = null;
}
if (app == null) {
// 新建一个 ProcessRecord 对象
app = newProcessRecordLocked(info, customProcess, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
try {
// 将包的stopped状态设置为false
// 如果为ture那么所有广播都无法接收,除非带有标记FLAG_INCLUDE_STOPPED_PACKAGES的广播
// 正经的广播都不会带有这个标记
AppGlobals.getPackageManager().setPackageStoppedState(info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
......
}
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// 设置 persistent 标记
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
// 启动进程
startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
abiOverride);
}
return app;
}
addAppLocked()
方法流程如下:
- 先根据参数
isolated
来决定是否启动一个新进程- 如果
isolated
为true
,即使系统中可能已经有一个同名的进程存在,也会通过newProcessRecordLocked()
再新建一个进程- 通过
updateLruProcessLocked()
方法更新运行中的进程的状态 - 通过
updateOomAdjLocked()
方法来更新进程的优先级
- 通过
- 如果
isolated
为false
,会通过getProcessRecordLocked()
方法在当前运行的进程列表中查找
- 如果
- 接着检查应用是否具有
persistent
标记- 如果有,设置
persistent
和maxAdj
属性
- 如果有,设置
- 最后,通过
startProcessLocked()
来启动进程
updateLruProcessLocked()
、updateOomAdjLocked()
这两个方法是进程管理的核心,等下细聊。
我们先看下startProcessLocked()
的启动逻辑:
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
......
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != MY_PID) {
synchronized (mPidsSelfLocked) {
// 移除集合中的进程ID,避免重复
mPidsSelfLocked.remove(app.pid);
// 移除消息队列中的 PROC_START_TIMEOUT_MSG 消息,避免干扰
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
}
......
mProcessesOnHold.remove(app);
...//省略大量的启动参数准备过程
final String entryPoint = "android.app.ActivityThread";
return startProcessLocked(hostingType, hostingNameStr, entryPoint, ...);
}
private boolean startProcessLocked(hostingType, hostingNameStr, entryPoint,...) {
......
if (mConstants.FLAG_PROCESS_START_ASYNC) {
// 异步启动的情况,通过 mProcStartHandler 来处理
mProcStartHandler.post(() -> {
......
// 启动应用
final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint, ...);
synchronized (ActivityManagerService.this) {
// 发送一个延时消息用来监听应用启动
handleProcessStartedLocked(app, startResult, startSeq);
}
......
});
return true;
} else {
// 启动应用
final ProcessStartResult startResult = startProcess(hostingType, entryPoint, ...);
// 发送一个延时消息用来监听应用启动
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
return app.pid > 0;
}
}
private ProcessStartResult startProcess(String hostingType, String entryPoint, ...) {
......
final ProcessStartResult startResult;
if (hostingType.equals("webview_service")) {
// startWebView 是 Process 的静态方法
// 启动的是 WebViewZygote,应该是为了和本地应用有所区分,不先管它。
startResult = startWebView(entryPoint,...);
} else {
// 通过 Process.start() 函数启动应用
startResult = Process.start(entryPoint, ...);
}
...
return startResult;
}
注释比较清楚。对于startProcessLocked()
方法,主要做了两件事:
-
通过
startProcess()
方法来启动应用进程startProcess()
方法最后是通过Process.start()
方法来完成。执行结果就是创建了一个新进程,并在其中执行ActivityThread
的main()
方法- 关于
Process.start()
方法在深入Android系统(七)Zygote进程中已经介绍过了,这里就不展开啦
-
然后通过
handleProcessStartedLocked()
方法来检测应用启动是否超时
handleProcessStartedLocked()
方法如下:
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
static final int PROC_START_TIMEOUT = 10*1000;
private boolean handleProcessStartedLocked(...) {
......
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(pid, app);
if (!procAttached) {
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
// 发送一个延时消息
mHandler.sendMessageDelayed(msg, usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
return true;
}
handleProcessStartedLocked()
方法主要是发送了一个延时消息PROC_START_TIMEOUT_MSG
Android
的想法应该是:当应用启动后去移除这个消息,如果消息在延时结束前没有被移除,就认为应用的启动出了问题。
那么,在哪里移除的呢??
我们已经知道,应用的启动入口在ActivityThread
的main()
方法。而在main()
方法的执行逻辑中会执行一个attach()
方法(这个方法也很重要),方法如下:
private void attach(boolean system, long startSeq) {
......
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
......
}
attach()
方法中会调用AMS
的attachApplication()
方法,而在attachApplication()
中执行了 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
666,把
Handler
使用的是淋漓尽致啊
进程列表的调整-LRU
LRU
是Least Recently Used
的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
在
Android
中进程启动后都会在AMS
的成员变量mLruProcesses
中保存其ProcessRecord
信息。mLruProcesses
以LRU
顺序存储了当前运行的应用程序进程信息,mLruProcesses
中的第一个元素就是最近最少使用的进程对应的ProcessRecord
。
AMS
中会通过updateLruProcessLocked()
来调整进程在mLruProcesses
列表中的位置,代码如下:
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
// app.activities.size() > 0 说明本进程中有活动的 Activity。app.activities 元素的类型是 ActivityRecord
// app.hasClientActivities 为 true 表示绑定了本进程Service的客户进程中有活动的 Activity
// app.treatLikeActivity 为 true 表示 Service 启动时带有 BIND_TREAT_LIKE_ACTIVITY 标记,这个属性和键盘逻辑关系比较大
// app.recentTasks.size() > 0 说明本进程中有活动的任务栈。app.recentTasks 元素的类型是 TaskRecord
final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
|| app.treatLikeActivity || app.recentTasks.size() > 0;
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// 如果当前进程中存在 Activity 并且 Activity 的状态没有发生过变化
// 不做处理,直接返回
return;
}
mLruSeq++; // 执行次数 +1
final long now = SystemClock.uptimeMillis();
app.lastActivityTime = now; // 更新 lastActivityTime 时间
if (hasActivity) {
// 如果 hasActivity 为 true 并且在 mLruProcesses 列表的最后一个位置
// 不需要再进行额外的操作,直接退出
final int N = mLruProcesses.size();
if (N > 0 && mLruProcesses.get(N-1) == app) {
return;
}
} else {
// hasActivity 为 false,说明进程中没有 Activity
// 如果位置位于 mLruProcessServiceStart-1 说明没有问题
if (mLruProcessServiceStart > 0
&& mLruProcesses.get(mLruProcessServiceStart-1) == app) {
return;
}
}
// 获取当前应用在集合中的位置
int lrui = mLruProcesses.lastIndexOf(app);
if (app.persistent && lrui >= 0) {
// 如果集合中已经存在并且应用属于 persistent 类型,则不需要做优化处理,直接返回
return;
}
if (lrui >= 0) {
// 如果 mLruProcesses 集合中已经存在,并且不是 persistent 类型,先将其从集合中移除
// 并同时调整 mLruProcessActivityStart 和 mLruProcessServiceStart 的位置
if (lrui < mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui < mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
mLruProcesses.remove(lrui);
}
int nextIndex;
if (hasActivity) {
// 当前集合中所有的进程数量,请注意此时 app 已经不在集合中了
final int N = mLruProcesses.size();
if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
&& mLruProcessActivityStart < (N - 1)) {
// 进程中没有 Activity,但是它的 Service 客户进程有 Activity
// 将进程插入到集合的尾部
mLruProcesses.add(N - 1, app);
// 检查集合中进程的uid,将与 app 相同uid的进程位置调整到一起
final int uid = app.info.uid;
for (int i = N - 2; i > mLruProcessActivityStart; i--) {
ProcessRecord subProc = mLruProcesses.get(i);
if (subProc.info.uid == uid) {
// 如果app的前一个进程的uid与其相同,检继续查前一个进程
if (mLruProcesses.get(i - 1).info.uid != uid) {
// 两个进程uid不一致进行位置互换,这里不太清楚目的
ProcessRecord tmp = mLruProcesses.get(i);
mLruProcesses.set(i, mLruProcesses.get(i - 1));
mLruProcesses.set(i - 1, tmp);
i--;
}
} else {
// A gap, we can stop here.
break;
}
}
} else {
// 如果包含 Activity 直接添加到集合的尾部
mLruProcesses.add(app);
}
nextIndex = mLruProcessServiceStart;
} else if (hasService) {
mLruProcesses.add(mLruProcessActivityStart, app);
nextIndex = mLruProcessServiceStart;
mLruProcessActivityStart++;
} else {
// 没有 Service 的情况
int index = mLruProcessServiceStart;
......
mLruProcesses.add(index, app);
nextIndex = index-1;
mLruProcessActivityStart++;
mLruProcessServiceStart++;
}
// 将和本进程 Service 关联的客户进程的位置调整到本进程之后
for (int j=app.connections.size()-1; j>=0; j--) {
ConnectionRecord cr = app.connections.valueAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
&& !cr.binding.service.app.persistent) {
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
}
}
// 将和本进程 ContentProvider 关联的客户进程的位置调整到本进程之后
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
}
}
}
updateLruProcessLocked()
方法中调整进程很重要的一个依据是进程中有没有活动的Activity
Activity
用来显示UI
,关系着用户体验,因此Android
尽量不关闭运行Activity
组件的进程。除了进程本身存在Activity
对象外,如果和进程中运行的Service
相关联的客户进程中有Activity
,也算本进程拥有Activity
。
- 这样做的目的是为了将来杀死进程释放内存做准备。
- 如果一个进程的关联进程有
Activity
对象存在,那么它的重要性也和真正拥有Activity
对象的进程相当。 - 如果杀死它,将可能导致另一个进程出现异常
如果一个进程拥有Activity
,通常会把它放入队列的最高端的位置(也就是集合索引值大的方向)
否则,只会把它放到所有没有Activity
的进程的前面,也就是mLruProcessActivityStart
所表示的位置。
调整完某个进程的位置之后,还需要调整和该进程相关联进程的位置
进程的关联进程有两种类型:
- 一种是绑定了本进程
Service
的进程 - 一种是连接了本进程的
ContentProvider
的进程
如果关联进程本身有Activity
是不会调整的,需要调整的是那些没有Activity
的进程,在updateLruProcessInternalLocked()
方法中会进行这种调整,我们简单看下:
private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
// 如果待调整进程包含 Activity ,不做调整,直接返回
return index;
}
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui < 0) {
// 如果进程不在 mLruProcesses 集合中,直接返回
return index;
}
if (lrui >= index) {
// 如果进程当前位置高于要调整的位置,不作调整,直接返回
return index;
}
if (lrui >= mLruProcessActivityStart) {
// 如果进程当前位置比 Activity 进程的其实位置还要高,不做调整,直接返回
return index;
}
// 把进程调整到 index-1 的位置
mLruProcesses.remove(lrui);
if (index > 0) {
index--;
}
mLruProcesses.add(index, app);
return index;
}
注释比较详细,不多解释了哈。
ProcessList
类
前面介绍了进程列表的调整逻辑,在学习
oom_adj
调整前我们先看下ProcessList
类的内容
ProcessList
类中定义了大量AMS
中用到的常量,部分定义如下:
public final class ProcessList {
// 定义进程发生 crash 的最小时间间隔,如果进程在小于这个时间内发生 crash,会被认为是异常进程
static final int MIN_CRASH_INTERVAL = 60*1000;
......
// 处于某种不可知状态的进程的 oom_adj 值
static final int UNKNOWN_ADJ = 1001;
// cached 进程的 oom_adj 的最大值和最小值定义
static final int CACHED_APP_MAX_ADJ = 906;
static final int CACHED_APP_MIN_ADJ = 900;
// 位于 B 列表的服务进程的 oom_adj 值。位于 B 列表的都是一些旧的、过时的服务进程
static final int SERVICE_B_ADJ = 800;
// 当前 Activity 的前一个 Activity 所处进程的 oom_adj 值
static final int PREVIOUS_APP_ADJ = 700;
// Home 进程的 oom_adj 值
static final int HOME_APP_ADJ = 600;
// 只包含组件 Service 的进程的 oom_adj 值
static final int SERVICE_ADJ = 500;
// heavy-weight 进程的 oom_adj 值
static final int HEAVY_WEIGHT_APP_ADJ = 400;
// 正在执行 backup 任务进程的 oom_adj 值
static final int BACKUP_APP_ADJ = 300;
// 不在前台但是包含有用户可感知组件的进程的 oom_adj 值(例如播放音乐的后台进程)
static final int PERCEPTIBLE_APP_ADJ = 200;
// 仅包含 Activity 的可见进程的 oom_adj 值
static final int VISIBLE_APP_ADJ = 100;
......
// 定义用于内存回收的 oom_adj 阈值
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
};
// 定义用于低配置(HVGA或更低,或内存小于512MB)设备内存回收的内存阈值,单位:KB
private final int[] mOomMinFreeLow = new int[] {
12288, 18432, 24576,
36864, 43008, 49152
};
// 用于定义高配置(高分辨率或内存1GB左右)设备内存回收的内存阈值,单位:KB
private final int[] mOomMinFreeHigh = new int[] {
73728, 92160, 110592,
129024, 147456, 184320
};
......
}
ProcessList
中定义的常量有很多和进程类型相关的,像cached进程
、service进程
等。关于Android
进程的分类官方将其分为了4类,简要如下:
foreground process
:前台进程,是用户目前执行操作所需的进程。在不同的情况下,进程可能会因为其所包含的各种应用组件而被视为前台进程visible process
:可见进程,正在进行用户当前知晓的任务,因此终止该进程会对用户体验造成明显的负面影响service process
:服务进程,包含一个已使用startService()
方法启动的Service
cached process
:缓存进程,是目前不需要的进程,因此,如果其他地方需要内存,系统可以根据需要自由地终止该进程
进程具体的类型描述及终止规则就不详细介绍了,具体大家可以参考官网:进程和应用生命周期,很详细哟
ProcessList
中同样也定义了一个和oom_adj
相关的方法,用来将oom_adj
的数值发送给lmkd
,如下:
public static final void setOomAdj(int pid, int uid, int amt) {
if (pid <= 0) {
return;
}
if (amt == UNKNOWN_ADJ)
return;
long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf);
}
调整进程的oom_adj
值
Android
基于各个应用进程四大组件的状态对应用进程进行重要性评估
,也就是计算更新oom_adj
值。并在系统内存紧张时根据重要性由低到高来选择杀死应用进程,以达到释放内存的目的
AMS
中调整进程oom_adj
值的方法是updateOomAdjLocked()
方法,简要代码如下:
final void updateOomAdjLocked() {
// 获取位于前台的 Activity 和它所在的进程
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
final int N = mLruProcesses.size();
......
// 计算 slot 数值。这里就用到了 ProcessList 类中的一些常量
// 根据常量数值,计算结果为:(906-900+1)/2=3
int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
- ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
// 计算 empty进程 的数量
int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
if (numEmptyProcs > cachedProcessLimit) {
// 如果空进程的数量超过了 cache进程 的限制值,更新为限制值
numEmptyProcs = cachedProcessLimit;
}
// 计算一个 slot 可以存放的empty进程数
int emptyFactor = numEmptyProcs/numSlots;
if (emptyFactor < 1) emptyFactor = 1;
// 计算一个 slot 可以存放的cache进程数
int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
if (cachedFactor < 1) cachedFactor = 1;
......
// 初始化 adj 数值
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
......
// 从 mLruProcesses 集合的后面向前处理
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
// 计算进程的 oom_adj 值
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
......
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
// 计算出的 oom_adj 值大于 ProcessList.UNKNOWN_ADJ(系统定义的最大值)的情况
switch (app.curProcState) {
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// 上面三种进程状态按照cached进程的参数进行调整
app.curRawAdj = curCachedAdj;
// 修正 adj 数值
app.curAdj = app.modifyRawOomAdj(curCachedAdj);
if (curCachedAdj != nextCachedAdj) {
stepCached++;
if (stepCached >= cachedFactor) { // 一个 slot 中放不下了
stepCached = 0;
curCachedAdj = nextCachedAdj;
nextCachedAdj += 2; // 使用一个新的 slot,即将 adj 值增加 2
if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
break;
default:
// 其他进程状态按照empty进程的参数进行设置
app.curRawAdj = curEmptyAdj;
app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += 2;
if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
break;
}
}
}
}
......
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
// 更新进程的 oom_adj 值
applyOomAdjLocked(app, true, now, nowElapsed);
......
}
}
......
}
updateOomAdjLocked()
方法中
- 先计算出了
slot
和每个slot
需要容纳的进程数量,包括:cached进程
和empty进程
- 这样做的目的是为了将系统中的
cached进程
和empty进程
分成不同的级别 - 每个级别拥有相同的
oom_adj
值 - 级别和级别间隔的
oom_adj
值为2
- 这样做的目的是为了将系统中的
- 然后通过调用
computeOomAdjLocked()
方法来计算进程的oom_adj
的值oom_adj
值的计算过程这里也就先简单略过啦,感兴趣的同学可以前去学习一下
- 如果计算后该进程的
curAdj
仍然大于系统定义的最大oom_adj
值(ProcessList.UNKNOWN_ADJ
),表明该进程属于cached进程
或者empty进程
- 根据进程状态变量
curProcState
来区分进程类型 - 确认进程类型后,会按照从低到高的原则把进程放到某个
slot
级别中 - 如果该级别的进程数满了,就进入下一个级别(
oom_adj
数值+2
)
- 根据进程状态变量
我们需要注意的是,计算oom_adj
值的时候是从mLruProcesses
列表的尾部开始计算的
这就意味着排在后面(数组索引值大的)的进程如果变成了cahced进程
和empty进程
,将会比它前面的同类型进程有更小的oom_adj
值
而oom_adj
值越大越可能被关闭,这也就是为什么要经常调整进程在mLruProcesses
列表的位置
而对于applyOomAdjLocked()
方法,最核心的地方在于执行了ProcessList.setOomAdj()
方法,跟踪方法调用会发现
- 最终是往名为
lmkd
的Socket
发送数据,数据内容为pid
、uid
及对应的adj
值 lmkd
由lmkd进程
创建,所以后面的事情就由lmkd
来处理
关于lmkd
我们就不深入了,大家可以前去官网了解:低内存终止守护程序
官方简介:Android 低内存终止守护程序 (
lmkd
) 进程可监控运行中的 Android 系统的内存状态,并通过终止最不必要的进程来应对内存压力大的问题,使系统以可接受的性能水平运行