说说Android应用的persistent属性
转载地址:http://my.oschina.net/youranhongcha/blog
摘要 在Android系统中,有一种永久性应用。它们对应的AndroidManifest.xml文件里,会将persistent属性设为true。
说说Android应用的persistent属性
侯 亮
1 启动persistent应用
在Android系统中,有一种永久性应用。它们对应的AndroidManifest.xml文件里,会将persistent属性设为true,比如:
1
2
3
4
|
< application android:name = "PhoneApp" android:persistent = "true" android:label = "@string/dialerIconLabel" android:icon = "@drawable/ic_launcher_phone" > |
在系统启动之时,AMS的systemReady()会加载所有persistent为true的应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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的定义如下:
1
2
|
// 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()函数的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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()的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
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缓冲列表里删除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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的,它会尝试重新启动这个应用。
注册讣告监听器的代码如下:
1
2
3
|
AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread); thread.asBinder().linkToDeath(adr, 0 ); app.deathRecipient = adr; |
其中的thread就是IApplicationThread代理。
AppDeathRecipient的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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()会尝试再次启动对应的应用进程。代码截选如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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()函数,其代码如下:
1
2
3
4
|
boolean isAllowedWhileBooting(ApplicationInfo ai) { return (ai.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 ; } |
从这个函数可以看到,将persistent属性设为true的应用,是允许在boot的过程中启动的。我们可以查看前文提到的startProcessLocked()函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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 ; } |
其中的最后几句可以改写为以下更易理解的形式:
1
2
3
4
5
6
7
8
9
10
|
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属性,我们就先说这么多。希望对大家有点儿帮助。
说说Android应用的persistent属性的更多相关文章
- 说说Android应用的persistent属性(转)
1 启动persistent应用 在Android系统中,有一种永久性应用.它们对应的AndroidManifest.xml文件里,会将persistent属性设为true,比如: <appli ...
- android中xml tools属性详解
第一部分 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果. 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了 ...
- Android读取自定义View属性
Android读取自定义View属性 attrs.xml : <?xml version="1.0" encoding="utf-8"?> < ...
- android中xmlns:tools属性详解
今天读到一篇总结的非常棒的文章,写的逻辑很清晰也很实用,很少见到如此棒的文章了.就原文转发过来,我把格式给整理了一下,分享给园子里的各位朋友!好久没写博客了,就为2015年的11月留份纪念吧.希望对你 ...
- Mono for Android布局控件属性小结
1. layout_weight 用于给一个线性布局中的诸多视图的重要度赋值. 所有的视图都有一个layout_weight值,默认为零,意思是需要显示 多大的视图就占据多大的屏幕空 间.若赋一个高于 ...
- 【Android】使用persist属性来调用脚本文件
Android系统中有许多属性,属性由两个部分组成:name & value,可以使用这些属性来记录系统设置或进程之间的信息交换.Android系统在启动过程时会按序从以下几个文件中加载系统属 ...
- Android开发之EditText属性详解
1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: 把该EditText设为:android:password="true" // 以”.”形式显示文本 ( ...
- android控件的属性
android控件的属性 本节描述android空间的位置,内容等相关属性及属性的含义 第一类:属性值为true或false android:layout_centerHrizontal 水平居中 ( ...
- 【转】 Android开发之EditText属性详解
原文网址:http://blog.csdn.net/qq435757399/article/details/7947862 1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: ...
随机推荐
- Java学习笔记之Scanner报错java.util.NoSuchElementException
转载自:IT学习者-螃蟹 一个方法A使用了Scanner,在里面把它关闭了.然后又在方法B里调用方法A之后就不能再用Scanner了Scanner in = new Scanner(System.in ...
- c++中string的用法
之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必 担心内存是否足够.字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至 ...
- Struts2实现国际化
public class I18nAction extends ActionSupport { private static final long serialVersionUID = -693330 ...
- 字符串编码问题(Ascii、Unicode、UCS-2、GBK、UTF-8)
1.字符编码的发展 第一阶段:ASCII阶段,(American Standard Code for Information Interchange, "美国信息交换标准码),计算机当时只支 ...
- HDU4815/计数DP
题目链接[http://acm.hdu.edu.cn/showproblem.php?pid=4815] 简单说一下题意: 有n道题,每到题答对得分为a[ i ],假如A不输给B的最小概率是P,那么A ...
- Alamofire 框架浅析
下面是 Github 主页上对 Alamofire 的描述 Elegant HTTP Networking in Swift 为什么这次我选择阅读 Alamofire 的源代码而不是 AFNetwor ...
- AI 人工智能 探索 (八)
绑定下,用来释放内存 布局框架.链接:http://pan.baidu.com/s/1eQzSXZO 密码:25ir 这次 我采用 ngui 来设定界面.除工具栏模块外,其他各类ui模块都是 内存池动 ...
- PARTITION BY 和 group by
sum() over (PARTITION BY ...) 是一个分析函数. 他执行的效果跟普通的sum ...group by ...不一样,它计算组中表达式的累 ...
- Hadoop: LongWritable cannot be cast to org.apache.hadoop.io.IntWritable
写MR Job的时候遇到一个坑爹的异常: LongWritable cannot be cast to org.apache.hadoop.io.IntWritable 当写Map的时候,key的默认 ...
- java 基础的几种算法
1:冒泡排序:2个之间进行循环筛选 public void sort(int[] a) { int temp = 0; for (int i = a.length - 1; i > 0; i ...