在我们开发系统级的App时,很有可能就会用persistent属性。当在AndroidManifest.xml中将persistent属性设置为true时,那么该App就会具有如下两个特性:

  • 在系统刚起来的时候,该App也会被启动起来

  • 该App被强制杀掉后,系统会重启该App。这种情况只针对系统内置的App,第三方安装的App不会被重启。

1. persistent属性的定义

persistent属性定义在frameworks/base/core/res/res/values/attrs_manifest.xml中:

<!-- Flag to control special persistent mode of an application.  This shouldnot normally be used by applications; it requires that the system keepyour application running at all times. -->
<attr name="persistent" format="boolean" />

官方解释:

是一个用于控制应用程序特殊持久模式的标志。通常情况下不应被应用程序使用,它要求系统始终保持应用程序的运行。

2. persistent属性的使用

persistent用在AndroidManifest.xml的application标签上:

<applicationandroid:persistent="true|false"></application>

默认值为false。

3. persistent属性的原理分析

下面我们就从源码的角度来分析persistent属性的工作原理。

备注:本文的源码是Android6.0,不同的Android版本可能略有所不同。

3.1 persistent属性的解析

属性的解析主要发生在App安装或者系统启动的时候,解析代码的位置在:

/frameworks/base/core/java/android/content/pm/PackageParser.java

深入到PackageParser.java的parseBaseApplication中:

   private boolean parseBaseApplication(Package owner, Resources res,XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)throws XmlPullParserException, IOException {final ApplicationInfo ai = owner.applicationInfo;final String pkgName = owner.applicationInfo.packageName;TypedArray sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifestApplication);...省略...if ((flags&PARSE_IS_SYSTEM) != 0) {if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_persistent,false)) {ai.flags |= ApplicationInfo.FLAG_PERSISTENT;}}...省略...
}

在解析完系统中App的包信息后,会将解析好的信息保存在PMS中的mPackages的map中,ApplicationInfo的flag中有一个bit位用于保存该App是否是persistent。

这里主要是将persistent的flag设置为ApplicationInfo.FLAG_PERSISTENT。

3.2 系统启动persistent为true的App

在系统启动时,会启动persistent属性为true的App,代码位置在:

/frameworks/base//services/core/java/com/android/server/am/ActivityManagerService.java

在系统启动时,AMS中的systemReady()方法会将所有在AndroidManifest中设置了persistent为true的App进程拉起来。

深入到AMS的systemReady()方法中:

public void systemReady(final Runnable goingCallback) {...省略...synchronized (this) {if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {try {List apps = AppGlobals.getPackageManager().getPersistentApplications(STOCK_PM_FLAGS);//注释1if (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, null /* ABI override */);//注释2}}}} catch (RemoteException ex) {// pm is in same process, this will never happen.}}...省略...}...省略...

注释说明:

注释1:调用PackageManagerServices的getPersistentApplications方法获取所有在AndroidManifest中设置了persistent属性为true的App

注释2:调用ActivityManagerServcies的addAppLocked方法去启动App

深入到PackageManagerServices的getPersistentApplications方法中:

public List<ApplicationInfo> getPersistentApplications(int flags) {final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();// readersynchronized (mPackages) {final Iterator<PackageParser.Package> i = mPackages.values().iterator();final int userId = UserHandle.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);if (ps != null) {ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,ps.readUserState(userId), userId);if (ai != null) {finalList.add(ai);}}}}}return finalList;
}

getPersistentApplications方法会遍历mPackages中所有的App,从判断条件中可以看到只有当在解析persistent属性时,ApplicationInfo的flag设置成了FLAG_PERSISTENT,且是系统App;或者是在非安全模式下,才会被选中。

可以看出被选中的情形有两种:

  • 系统App,只要ApplicationInfo的flag设置成了FLAG_PERSISTENT

  • 第三方安装的App,不仅要ApplicationInfo的flag设置成了FLAG_PERSISTENT,还需要在非安全模式下

继续回到ActivityManagerServcies的addAppLocked方法中:

final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,String abiOverride) {ProcessRecord app;//传进来的isolated是false,所以就会调用getProcessRecordLocked方法,//但由于是第一次启动,所以所有的返回都是app = nullif (!isolated) {app = getProcessRecordLocked(info.processName, info.uid, true);} else {app = null;}if (app == null) {//为新的app创建新的ProcessRecord对象app = newProcessRecordLocked(info, null, isolated, 0);updateLruProcessLocked(app, false, null);updateOomAdjLocked();}// This package really, really can not be stopped.try {//因为是开机第一次启动,所以新的App的启动状态就是将要被启动的状态//所以将App的停止状态stoped设置为falseAppGlobals.getPackageManager().setPackageStoppedState(info.packageName, false, UserHandle.getUserId(app.uid));} catch (RemoteException e) {} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ info.packageName + ": " + e);}//如果是系统App,且persistent属性为true,则异常死亡后会重启if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {app.persistent = true;app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;}//如果App已启动,则不处理,否则调用startProcessLocked方法启动App//启动App是异步的,因此会将正在启动,但还没启动完成的App添加到mPersistentStartingProcesses列表中,当启动完成后再移除if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {mPersistentStartingProcesses.add(app);//启动AppstartProcessLocked(app, "added application", app.processName, abiOverride,null /* entryPoint */, null /* entryPointArgs */);}return app;
}

在App启动完成后,会在ActivityThread中调用ActivityManagerService的attachApplicationLocked()方法,将该App从mPersistentStartingProcesses移除,并注册一个死亡讣告监听器AppDeathRecipient,用于在App异常被杀后的处理工作。

深入到ActivityManagerService的attachApplicationLocked()方法中:

private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {...省略...final String processName = app.processName;try {//注册死亡讣告监听器AppDeathRecipientAppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);thread.asBinder().linkToDeath(adr, 0);app.deathRecipient = adr;} catch (RemoteException e) {app.resetPackageList(mProcessStats);//如果注册死亡讣告监听器失败,也会重新启动App进程startProcessLocked(app, "link fail", processName);return false;}...省略...// Remove this record from the list of starting applications.mPersistentStartingProcesses.remove(app);...省略...

到此,persistent属性为true的App在开机时就会启动,并且会注册死亡讣告监听器AppDeathRecipient。

3.3 系统重新启动被强制kill掉的带有persistent属性的App

上面可知,进程在启动时,会为App注册一个死亡讣告,当App被杀掉后,就会调用AppDeathRecipient的binderDied方法:

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

binderDied又会调用appDiedLocked()方法:

final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,boolean fromBinderDied) {...省略...// Clean up already done if the process has been re-started.if (app.pid == pid && app.thread != null &&app.thread.asBinder() == thread.asBinder()) {boolean doLowMem = app.instrumentationClass == null;boolean doOomAdj = doLowMem;...省略...handleAppDiedLocked(app, false, true);...省略...
}

handleAppDiedLocked又会调用handleAppDiedLocked()方法:

private final void handleAppDiedLocked(ProcessRecord app,boolean restarting, boolean allowRestart) {int pid = app.pid;boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,false /*replacingPid*/);...省略...
}

继续调用cleanUpApplicationRecordLocked()方法:

private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,boolean restarting, boolean allowRestart, int index, boolean replacingPid) {...省略...//非persistent的App被kill后,就会被清理掉if (!app.persistent || app.isolated) {if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,"Removing non-persistent process during cleanup: " + app);if (!replacingPid) {removeProcessNameLocked(app.processName, app.uid);}if (mHeavyWeightProcess == app) {mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,mHeavyWeightProcess.userId, 0));mHeavyWeightProcess = null;}} else if (!app.removed) {// This app is persistent, so we need to keep its record around.// If it is not already on the pending app list, add it there// and start a new process for it.//该app是persistent的,需要对其进行重启,并把它添加到正在启动的列表中//设置restart=trueif (mPersistentStartingProcesses.indexOf(app) < 0) {mPersistentStartingProcesses.add(app);restart = true;}}...省略...//通过这个判断添加决定是否重启App进程//通过前面的过滤,persistent属性的App,restart=true,!app.isolated=trueif (restart && !app.isolated) {// We have components that still need to be running in the// process, so re-launch it.if (index < 0) {ProcessList.remove(app.pid);}addProcessNameLocked(app);//启动App进程startProcessLocked(app, "restart", app.processName);return true;} else if (app.pid > 0 && app.pid != MY_PID) {// Goodbye!boolean removed;synchronized (mPidsSelfLocked) {mPidsSelfLocked.remove(app.pid);mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);}mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);if (app.isolated) {mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);}app.setPid(0);}return false;
}

到此,带有persistent属性为true的App,就会在强制kill掉进程后,还会重启。

重启persistent应用的调用关系图如下:

非常感谢您的耐心阅读,希望我的文章对您有帮助。欢迎点评、转发或分享给您的朋友或技术群。

谈谈Android中的persistent属性相关推荐

  1. 说说Android应用的persistent属性

    2019独角兽企业重金招聘Python工程师标准>>> 说说Android应用的persistent属性 侯 亮 1 启动persistent应用 在Android系统中,有一种永久 ...

  2. android中 menu的属性详细解释

    android中 menu的属性详细解释 <?xml version="1.0" encoding="utf-8"?> <menu xmlns ...

  3. Android 中自定义控件和属性(attr.xml,declare-styleable,TypedArray)的方法和使用

    一. 在res/values 文件下定义一个attrs.xml 文件.代码如下: <?xml version="1.0" encoding="utf-8" ...

  4. android中xml tools属性详解

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

  5. android中xmlns:tools属性详解

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

  6. android中用代码设置edittext属性为密码,Android中EditText常用属性设置

    EditText继承关系:View–>TextView–>EditText 常用属性如下:android:layout_gravity="center_vertical" ...

  7. 谈谈android中的内存泄漏

    写在前面 内存泄漏实际上很多时候,对于开发者来说不容易引起重视.因为相对于crash来说,android中一两个地方发生内存泄漏的时候,对于整体没有特别严重的影响.但是我想说的是,当内存泄漏多的时候, ...

  8. 谈谈 Android 中的 PathClassLoader 和 DexClassLoader

    [这是 ZY 第 13 篇原创技术文章] 预备知识 了解 android 基本 ClassLoader 知识 看完本文可以达到什么程度 了解 PathClassLoader 和 DexClassLoa ...

  9. 谈谈Android中的HandlerThread

    一.简介 在详细讲解这篇文章之前,应该有部分的Android初级开发者并没用使用过HandlerThread,最常使用的消息传递机制应该离不开Handler,那么Handler跟HandlerThre ...

  10. Android中的windowSoftInputMode属性详解

    如何实现软键盘不自动弹出,使用的方法是设置android:windowSoftInputMode属性.那么,这个属性到底是干什么的,他有什么作用呢?今天这篇文章,就是探索android:windowS ...

最新文章

  1. 前端优化系列之一:dns预获取 dns-prefetch 提升页面载入速度
  2. 用了 Elasticsearch 后,查询起飞了!
  3. LeetCode-笔记-394. 字符串解码
  4. SOA Notes
  5. jboss jta mysql_JBoss平台下JTA与JMS实验软件架构8
  6. escape与encodeURI、encodeURIComponent的区别
  7. 概率论-2.1 随机变量及其分布(重点:右连续的来源)
  8. eclipse new creation file type
  9. 数位 DP 入门 (不要 62+windy 数)
  10. 今天在群里面讨论了驱动机制的学习
  11. 用户控件中得到CurrentUser
  12. 独角兽公司超级创始人早期的30个特质
  13. 信息安全——非对称密码体制
  14. Python基础:函数
  15. Apache Calcite论文概要
  16. markdown的基本使用方法 1
  17. 信阳发现多例蜱虫病病例
  18. 小学时的游戏——24点
  19. 吴忌寒入选福布斯2019最年轻亿万富豪榜;黑客已将价值近千万EOS偷跑 | 1分钟链圈...
  20. mysqld.service holdoff time over, scheduling restart.

热门文章

  1. 采用生产者消费者模式爬取毛豆新车网
  2. AJAX IE7清除缓存
  3. BZOJ 3097: Hash Killer I
  4. 3097: Hash Killer I
  5. 【BZOJ】5294: [Bjoi2018]二进制-动态DP线段树
  6. eclipse项目名前出现红色感叹号,小红叉解决(转)
  7. 《天才在左,疯子在右》读后感
  8. 微软 edge浏览器自动关闭问题处理
  9. java中的math.abs_在Java中什么意思 Math.abs(x)及同类的的公式
  10. 计算机触摸板设置方法,笔记本的触摸板怎么用_笔记本电脑触摸板的使用教程-win7之家...