Android关键字persistent原理分析

在Android程序开发时我们会接触到一些系统为了某些功能而定义的关键属性,例如在AndroidManifest.xml文件中

经常看到的persistent、process等,下面是自己对persistent关键字的分析,直奔主题。

一、persistent属性作用

1、定义

该属性的定义在frameworks/base/core/res/res/values/attrs_manifest.xml中,其定义如下:


    <!-- Flag to control special persistent mode of an application.  This should

         not normally be used by applications; it requires that the system keep

         your application running at all times. -->

    <attr name="persistent" format="boolean" />



通过官方注释我知道该属性用于是否让你的应用一直处于运行状态(通常说的常驻内存)。设置
该属性为true的app具有如下特点:

  • 在系统启动的时候会被系统启动起来

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

2、使用

persistent属性是用于application标签上的,用法为:

AndroidManifest.xml


<application

    android:persistent="true|false"

>



</application>



persistent的值默认为false

二、原理分析

通过第一点对persistent的功能说明后我们通过源码来分析一下它的工作原理

1、persistent属性的解析

该属性的解析主要在app被安装或者系统启动的时候发生

解析代码:

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


private boolean parseBaseApplication(Package owner, Resources res,

            XmlResourceParser parser, int flags, String[] outError)

        throws XmlPullParserException, IOException {

final ApplicationInfo ai = owner.applicationInfo;

    //.......................

    

    if ((flags&PARSE_IS_SYSTEM) != 0) {

            if (sa.getBoolean(

                    com.android.internal.R.styleable.AndroidManifestApplication_persistent,

                    false)) {

                ai.flags |= ApplicationInfo.FLAG_PERSISTENT;

            }

        }





//.............








}

在解析完包信息之后系统会将解析好的所有包信息存放到PKMS中的mPackages的map中,而ApplicationInfo的flag中有一个bit位用于保存该app是否是persistent的。这里只是把保存persistent的flag设置为FLAG_PERSISTENT。在AndroidManifest设置了persistent为true的app是否能够在被异常杀死后能够得到重启的权力需要取决于该app对应的ProcessRecord的persistent属性,该属性只有在你的app既在AndroidManifest中配置了persistent=“true”,又是系统内置app时才会被设置为true。

2、系统启动时启动persistent为true的app

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

在系统启动时ActivityManagerService的systemReady()方法会将所有在AndroidManifest设置了persistent为true的app拉起来


 public void systemReady(final Runnable goingCallback) {

......

synchronized (this) {

            // Only start up encryption-aware persistent apps; once user is

            // unlocked we'll come back around and start unaware apps

            startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);



            // Start up initial activity.

            mBooting = true;

            // Enable home activity for system user, so that the system can always boot

            if (UserManager.isSplitSystemUser()) {

                ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);

                try {

                    AppGlobals.getPackageManager().setComponentEnabledSetting(cName,

                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,

                            UserHandle.USER_SYSTEM);

                } catch (RemoteException e) {

                    throw e.rethrowAsRuntimeException();

                }

            }

......

}

systemReady中调用了startPersistentApps() 方法




private void startPersistentApps(int matchFlags) {

        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;



        synchronized (this) {

            try {

                final List<ApplicationInfo> apps = AppGlobals.getPackageManager()

                        .getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();

                for (ApplicationInfo app : apps) {

                    if (!"android".equals(app.packageName)) {

                        addAppLocked(app, false, null /* ABI override */);

                    }

                }

            } catch (RemoteException ex) {

            }

        }

    }

在startPersistentApps方法中首先是调用PackageManageServices的getPersistentApplications方法获取到所有在AndroidManifest设置了persistent为true的app,然后调用addAppLocked方法去启动他们。这样在AndroidManifest设置了persistent为true的app就随着系统的启动而启动了。
下面看一下getPersistentApplications方法,该方法调用了PKMS中的getPersistentApplicationsInternal方法。



private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {

        final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();



        // reader

        synchronized (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) continue;



                final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)

                        && !p.applicationInfo.isDirectBootAware();

                final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)

                        && p.applicationInfo.isDirectBootAware();



                if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0

                        && (!mSafeMode || isSystemApp(p))

                        && (matchesUnaware || matchesAware)) {

                    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;

    }



该方法会遍历mPackages中的所有app,并找到其中在AndroidManifest设置了persistent为true的应用。从代码中可以看到,persistent为true并且是系统app的话一定会被选中,但是如果是第三方安装的应用的话只能在非“安全模式”下才会被选中。

之后调用addAppLocked方法启动app:







    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,

            String abiOverride) {

        ProcessRecord app;

        //传递进来的isolated=false,所有一定会调用getProcessRecordLocked方法,但是由于是第一次启动,所有返回的app = null

        if (!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被设置为false

            AppGlobals.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);

        }

      

        //在这里对persistent的app进行过滤,只有既是系统app,persistent为true的app才会在

       //异常死亡之后被重启

        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);

            //启动该app

            startProcessLocked(app, "added application", app.processName, abiOverride,

                    null /* entryPoint */, null /* entryPointArgs */);

        }



        return app;

    }

}

接下来调用startProcessLocked方法启动app进程,在app启动完成后会在ActivityThread中调用AMS的attachApplication,将该app从mPersistentStartingProcesses中移除,并注册一个死亡讣告监听器AppDeathRecipient,用于在app异常被杀后的处理工作。


private final boolean attachApplicationLocked(IApplicationThread thread,

            int pid) {



......

try {

            //注册死亡讣告监听器AppDeathRecipient

            AppDeathRecipient adr = new AppDeathRecipient(

                    app, pid, thread);

            thread.asBinder().linkToDeath(adr, 0);

            app.deathRecipient = adr;

        } catch (RemoteException e) {

            app.resetPackageList(mProcessStats);

            startProcessLocked(app, "link fail", processName);

            return false;

        }

......

}

3、app被异常结束后系统重新启动persistent为true的app

进程启动时为app注册了一个死亡讣告,当该app被杀掉之后会调用AppDeathRecipient的binderDied方法,该方法会调用appDiedLocked方法进行善后处理,系统在进程死掉之后会对死掉的进程进行清理和资源回收,但是在这个过程中如果你的app是persistent的话会被重启:

binderDied

  |

  |——appDiedLocked

                 |

                 |——handleAppDiedLocked

                                      |

                                      |——cleanUpApplicationRecordLocked

在cleanUpApplicationRecordLocked中对persistent为true的app进行重启



private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,

            boolean restarting, boolean allowRestart, int index, boolean replacingPid) {

...............
   //非persistent的app被杀死后就被清理掉


    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, app);

            }

            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=true

            if (mPersistentStartingProcesses.indexOf(app) < 0) {

                mPersistentStartingProcesses.add(app);

                restart = true;

            }

        }

....

//经过上面的过滤,会调用这个分支条件重启persistent为true的app

 if (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);

            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;



}





©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,047评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • Application 标签 android:allowTaskReparenting android:allow...
    Shawn_Dut阅读 7,717评论 2 61
  • 继任女君 那一天,我在青丘完成了继任青丘女君的大典,终于在阿爹的脸上看到一丝笑容 ,这一刻,我是他最骄傲的女儿……...
    新新想我阅读 401评论 0 1
  • 词/chaizi 躲在成年人的躯壳 灰色的霾,遮住了蓝 裹紧外套 提起背包 套在生活的方程式里 你看不见我 视线绕...
    ZICOLOR阅读 239评论 0 0