1 启动persistent应用

在Android系统中,有一种永久性应用。它们对应的AndroidManifest.xml文件里,会将persistent属性设为true,比如:

<application android:name="PhoneApp"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">

在系统启动之时,AMS的systemReady()会加载所有persistent为true的应用。

public void systemReady(final Runnable goingCallback)
{
. . . . . .
. . . . . .
try {
List apps = AppGlobals.getPackageManager().
getPersistentApplications(STOCK_PM_FLAGS);
if (apps != null)
{
int N = apps.size();
int i; for (i=0; i<N; i++)
{
ApplicationInfo info = (ApplicationInfo)apps.get(i);
if (info != null &&
!info.packageName.equals("android"))
{
addAppLocked(info, false);
}
}
}
}
catch (RemoteException ex) {
// pm is in same process, this will never happen.
}

其中的STOCK_PM_FLAGS的定义如下:

// The flags that are set for all calls we make to the package manager.
static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;

上面代码中的getPersistentApplications()函数的定义如下:

public List<ApplicationInfo> getPersistentApplications(int flags)
{
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); // reader
synchronized (mPackages)
{
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
final int userId = UserId.getCallingUserId();
while (i.hasNext())
{
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p)))
{
PackageSetting ps = mSettings.mPackages.get(p.packageName);
finalList.add(PackageParser.generateApplicationInfo(p, flags,
ps != null ? ps.getStopped(userId) : false,
ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
userId));
}
}
} return finalList;
}

在PKMS中,有一个记录所有的程序包信息的哈希表(mPackages),每个表项中含有ApplicationInfo信息,该信息的flags(int型)数据中有一个专门的bit用于表示persistent。getPersistentApplications()函数会遍历这张表,找出所有persistent包,并返回ArrayList<ApplicationInfo>。

从代码里可以看出,带persistent标志的系统应用(即flags中设置了FLAG_SYSTEM)是一定会被选上的,但如果不是系统应用的话,则要进一步判断当前是否处于“安全模式”,一旦处于安全模式,那么就算应用设置了persistent属性,也不会被选中。

随后systemReady()开始遍历选中的ApplicationInfo,并对包名不为“android”的结点执行addAppLocked()。addAppLocked()的代码如下:

final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated)
{
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(info.processName, info.uid);
} else {
app = null;
} if (app == null) {
app = newProcessRecordLocked(null, info, null, isolated);
mProcessNames.put(info.processName, app.uid, app);
if (isolated) {
mIsolatedProcesses.put(app.uid, app);
}
updateLruProcessLocked(app, true, true);
} // This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserId.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
} if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
== (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_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", app.processName);
} return app;
}

在AMS中,所谓的“add App”主要是指“添加一个与App进程对应的ProcessRecord节点”。当然,如果该节点已经添加过了,那么是不会重复添加的。在添加节点的动作完成以后,addAppLocked()还会检查App进程是否已经启动好了,如果尚未开始启动,此时就会调用startProcessLocked()启动这个进程。既然addAppLocked()试图确认App“正在正常运作”或者“将被正常启动”,那么其对应的package就不可能处于stopped状态,这就是上面代码调用setPackageStoppedState(...,false,...)的意思。

现在,我们就清楚了,那些persistent属性为true的应用,基本上都是在系统启动伊始就启动起来的。

因为启动进程的过程是异步的,所以我们需要一个缓冲列表(即上面代码中的mPersistentStartingProcesses列表)来记录那些“正处于启动状态,而又没有启动完毕的”ProcessRecord结点。一旦目标进程启动完毕后,目标进程会attach系统,于是走到AMS的attachApplicationLocked(),在这个函数里,会把目标进程对应的ProcessRecord结点从mPersistentStartingProcesses缓冲列表里删除。

private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {

        // Find the application record that is being attached...  either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app; . . . . . .
thread.asBinder().linkToDeath(adr, 0);
. . . . . .
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
enableOpenGlTrace, isRestrictedBackupMode || !normalMode,
app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
. . . . . .
. . . . . .
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
. . . . . .

2 如何保证应用的持久性(persistent)

我们知道,persistent一词的意思是“持久”,那么persistent应用的意思又是什么呢?简单地说,这种应用会顽固地运行于系统之中,从系统一启动,一直到系统关机。

为了保证这种持久性,persistent应用必须能够在异常出现时,自动重新启动。在Android里是这样实现的。每个ActivityThread中会有一个专门和AMS通信的binder实体——final ApplicationThread mAppThread。这个实体在AMS中对应的代理接口为IApplicationThread。

当AMS执行到attachApplicationLocked()时,会针对目标用户进程的IApplicationThread接口,注册一个binder讣告监听器,一旦日后用户进程意外挂掉,AMS就能在第一时间感知到,并采取相应的措施。如果AMS发现意外挂掉的应用是persistent的,它会尝试重新启动这个应用。

注册讣告监听器的代码如下:

AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;

其中的thread就是IApplicationThread代理。

AppDeathRecipient的定义如下:

private final class AppDeathRecipient implements IBinder.DeathRecipient
{
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread; AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread)
{
if (localLOGV)
Slog.v(TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
} public void binderDied()
{
if (localLOGV)
Slog.v(TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this)
{
appDiedLocked(mApp, mPid, mAppThread);
}
}
}

当其监听的binder实体死亡时,系统会回调AppDeathRecipient的binderDied()。这个回调函数会辗转重启persistent应用,调用关系如下:

一般情况下,当一个应用进程挂掉后,AMS当然会清理掉其对应的ProcessRecord,这就是cleanUpApplicationRecordLocked()的主要工作。然而,对于persistent应用,cleanUpApplicationRecordLocked()会尝试再次启动对应的应用进程。代码截选如下:

private final void cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting,
boolean allowRestart, int index)
{
. . . . . .
. . . . . .
if (!app.persistent || app.isolated)
{
. . . . . .
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
. . . . . .
}
else if (!app.removed)
{
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
. . . . . .
. . . . . .
if (restart && !app.isolated)
{
mProcessNames.put(app.processName, app.uid, app);
startProcessLocked(app, "restart", app.processName);
}
else if (app.pid > 0 && app.pid != MY_PID)
{
. . . . . .
}
. . . . . .
}

现在我们可以画一张关于“启动persistent应用”的示意图:

3 补充知识点

3.1 persistent应用可以在系统未准备好时启动

在AMS中,有一个isAllowedWhileBooting()函数,其代码如下:

boolean isAllowedWhileBooting(ApplicationInfo ai)
{
return (ai.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
}

从这个函数可以看到,将persistent属性设为true的应用,是允许在boot的过程中启动的。我们可以查看前文提到的startProcessLocked()函数:

final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead,
int intentFlags,
String hostingType, ComponentName hostingName,
boolean allowWhileBooting,
boolean isolated)
{
ProcessRecord app; if (!isolated)
{
app = getProcessRecordLocked(processName, info.uid);
}
else
{
// If this is an isolated process, it can't re-use an existing process.
app = null;
} . . . . . .
. . . . . . if (!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
return app;
} startProcessLocked(app, hostingType, hostingNameStr);
return (app.pid != 0) ? app : null;
}

其中的最后几句可以改写为以下更易理解的形式:

if  (mProcessesReady || isAllowedWhileBooting(info) || allowWhileBooting)
{
startProcessLocked(app, hostingType, hostingNameStr);
return (app.pid != 0) ? app : null;
}
else
{
. . . . . .
return app;
}

也就是说,当系统已经处于以下几种情况时,多参数的startProcessLocked()会进一步调用另一个只有三个参数的startProcessLocked():
1)系统已经处于ready状态;
2)想要启动persistent应用;
3)参数中明确指定可以在boot过程中启动应用。

补充说一下,一般情况下,当AMS调用startProcessLocked()时,传入的allowWhileBooting参数都为false。比如说,当系统需要启动“某个content provider或者某个service或者某个特定activity”时,此时传给startProcessLocked()的allowWhileBooting参数是写死为false的。只有一种特殊情况下会在该参数中传入true,那就是当系统发出的广播intent中携带有Intent.FLAG_RECEIVER_BOOT_UPGRADE标记时,此时允许在系统未ready时,启动接受广播的目标进程。

4 结束

有关Android应用的persistent属性,我们就先说这么多。希望对大家有点儿帮助。

转自:http://my.oschina.net/youranhongcha

说说Android应用的persistent属性(转)的更多相关文章

  1. 说说Android应用的persistent属性

    转载地址:http://my.oschina.net/youranhongcha/blog 摘要 在Android系统中,有一种永久性应用.它们对应的AndroidManifest.xml文件里,会将 ...

  2. android中xml tools属性详解

    第一部分 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果. 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了 ...

  3. Android读取自定义View属性

    Android读取自定义View属性 attrs.xml : <?xml version="1.0" encoding="utf-8"?> < ...

  4. android中xmlns:tools属性详解

    今天读到一篇总结的非常棒的文章,写的逻辑很清晰也很实用,很少见到如此棒的文章了.就原文转发过来,我把格式给整理了一下,分享给园子里的各位朋友!好久没写博客了,就为2015年的11月留份纪念吧.希望对你 ...

  5. Mono for Android布局控件属性小结

    1. layout_weight 用于给一个线性布局中的诸多视图的重要度赋值. 所有的视图都有一个layout_weight值,默认为零,意思是需要显示 多大的视图就占据多大的屏幕空 间.若赋一个高于 ...

  6. 【Android】使用persist属性来调用脚本文件

    Android系统中有许多属性,属性由两个部分组成:name & value,可以使用这些属性来记录系统设置或进程之间的信息交换.Android系统在启动过程时会按序从以下几个文件中加载系统属 ...

  7. Android开发之EditText属性详解

    1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: 把该EditText设为:android:password="true" // 以”.”形式显示文本 ( ...

  8. android控件的属性

    android控件的属性 本节描述android空间的位置,内容等相关属性及属性的含义 第一类:属性值为true或false android:layout_centerHrizontal 水平居中 ( ...

  9. 【转】 Android开发之EditText属性详解

    原文网址:http://blog.csdn.net/qq435757399/article/details/7947862 1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: ...

随机推荐

  1. 沈南鹏@《遇见大咖》: A轮没投,投了8个月以后就证明了张一鸣是对了,在美国都没有张一鸣这种模式

    沈南鹏@<遇见大咖>: A轮没投,投了8个月以后就证明了张一鸣是对了,在美国都没有张一鸣这种模式

  2. (待解决)IDEA配置JDBC查询数据库PreparedStatement pstmt = dbconn.prepareStatement(sql)出现空指针错误

    package com.demo; import java.io.*; import java.sql.*; import java.util.*; import javax.servlet.*; i ...

  3. ios之数据持久化

    9.1 数据持久化概述 iOS中可以有四种持久化数据的方式: 属性列表.对象归档.SQLite3和Core Data 9.2 iOS应用程序目录结构 iOS应用程序运行在Mac os模拟器时候,有一下 ...

  4. 搭建Mysql主从复制

    mysql 主从复制流程图 Server version: 10.0.24-MariaDB-7 Ubuntu 16.04 Master 记录二进制文件 导出数据并记录二进制位置 导入数据,设置二进制位 ...

  5. vs2010 C# 如何将类做成DLL 再从另一个项目中使用这个类

    vs2010 C# 如何将类做成DLL 再从另一个项目中使用这个类 2011-10-20 12:00 486人阅读 评论(0) 收藏 举报 一.将类做成DLL 方法一: 你可以通过在命令行下用命令将以 ...

  6. 【ITOO 5】启用MSDTC后的各种问题,以及解决方案

    导读:在上篇博客中说到,通过启动微软的msdtc来实现分布式事务处理,随后,问题就来了,现在,一一总结这个过程中的问题. 一.已禁用对分布式事务管理器(MSDTC)的网络访问. 1.1,问题描述 已禁 ...

  7. B/S 开发和 C/S开发的区别

    导读:每天都从应用中心下载很多软件安装尝试,在自己的电脑上也装了很多软件,但是,就出现了一个问题,好比QQ,为什么有了APP,还要有网站应用呢?由此,结合到自己的学习,就衍生出一个问题:C/S 开发就 ...

  8. [codeforces724D]Dense Subsequence

    [codeforces724D]Dense Subsequence 试题描述 You are given a string s, consisting of lowercase English let ...

  9. Retrofit+RxJava联网工具类

    在python 进程.线程 (一)中简单的说过,CPython中的GIL使得同一时刻只能有一个线程运行,即并发执行.并且即使是多核CPU,GIL使得同一个进程中的多个线程也无法映射到多个CPU上运行, ...

  10. 通过一个用户管理实例学习路由react-router-dom知识

    我们通过一个用户管理实例来学习react-router-dom 这个实例包括9个小组件 App.js 引入组件 Home.js 首页组件 User.js 用户管理组件 -  UserList.js 用 ...