• 说说Android应用的persistent属性


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

    1
    </application>


    在系统启动之时,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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    public void systemReady(final Runnable goingCallback)
    {
        . . . . . .
        . . . . . .
        try{
            List apps = AppGlobals.getPackageManager().
                            getPersistentApplications(STOCK_PM_FLAGS);
            if(apps != null)
            {
                intN = apps.size();
                inti;
                  
                for(i=0; i<n; applicationinfo="" happen.="" in="" info="" is="" never="" pm="" pre="" remoteexception="" same="" this="" will=""><p> </p><p>
    其中的STOCK_PM_FLAGS的定义如下:</p><p> </p><pre class="brush:java;">// 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;</pre>
    <p> </p>
    <br>
    <p>上面代码中的getPersistentApplications()函数的定义如下:</p>
    <p> </p>
    <pre class="brush:java;">public List getPersistentApplications(int flags)
    {
        final ArrayList finalList = new ArrayList();
      
        // 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;
    }</packageparser.package></applicationinfo></applicationinfo></applicationinfo></pre>
    <br>
    <p> </p>
    在PKMS中,有一个记录所有的程序包信息的哈希表(mPackages),每个表项中含有ApplicationInfo信息,该信息的flags(int型)数据中有一个专门的bit用于表示persistent。getPersistentApplications()函数会遍历这张表,找出所有persistent包,并返回ArrayList。
    <p> </p>
    <p>从代码里可以看出,带persistent标志的系统应用(即flags中设置了FLAG_SYSTEM)是一定会被选上的,但如果不是系统应用的话,则要进一步判断当前是否处于“安全模式”,一旦处于安全模式,那么就算应用设置了persistent属性,也不会被选中。</p>
    <p>随后systemReady()开始遍历选中的ApplicationInfo,并对包名不为“android”的结点执行addAppLocked()。addAppLocked()的代码如下:</p>
    <p> </p>
    <pre class="brush:java;"></pre>
    <pre class="java" name="code">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; }</pre>
    <p> </p>
    <pre class="brush:java;"></pre>
    <p> </p>
    ? 在AMS中,所谓的“add App”主要是指“添加一个与App进程对应的ProcessRecord节点”。当然,如果该节点已经添加过了,那么是不会重复添加的。在添加节点的动作完成以后,addAppLocked()还会检查App进程是否已经启动好了,如果尚未开始启动,此时就会调用startProcessLocked()启动这个进程。既然addAppLocked()试图确认App“正在正常运作”或者“将被正常启动”,那么其对应的package就不可能处于stopped状态,这就是上面代码调用setPackageStoppedState(...,false,...)的意思。
    <p> </p>
    <p>现在,我们就清楚了,那些persistent属性为true的应用,基本上都是在系统启动伊始就启动起来的。</p>
    <p>因为启动进程的过程是异步的,所以我们需要一个缓冲列表(即上面代码中的mPersistentStartingProcesses列表)来记录那些“正处于启动状态,而又没有启动完毕的”ProcessRecord结点。一旦目标进程启动完毕后,目标进程会attach系统,于是走到AMS的attachApplicationLocked(),在这个函数里,会把目标进程对应的ProcessRecord结点从mPersistentStartingProcesses缓冲列表里删除。</p>
    <p> </p>
    <pre class="brush:java;">private final boolean attachApplicationLocked(IApplicationThread thread, intpid) {
      
            // 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,
                        newConfiguration(mConfiguration), app.compat,
                        getCommonServicesLocked(),
                        mCoreSettingsObserver.getCoreSettingsLocked());
            . . . . . .
            . . . . . .
            // Remove this record from the list of starting applications.
            mPersistentStartingProcesses.remove(app);
            . . . . . .</pre>
    <p> </p>
    ?
    <h1>2 如何保证应用的持久性(persistent)</h1>
    <p>我们知道,persistent一词的意思是“持久”,那么persistent应用的意思又是什么呢?简单地说,这种应用会顽固地运行于系统之中,从系统一启动,一直到系统关机。</p>
    <p>为了保证这种持久性,persistent应用必须能够在异常出现时,自动重新启动。在Android里是这样实现的。每个ActivityThread中会有一个专门和AMS通信的binder实体——final ApplicationThread mAppThread。这个实体在AMS中对应的代理接口为IApplicationThread。</p>
    <p>当AMS执行到attachApplicationLocked()时,会针对目标用户进程的IApplicationThread接口,注册一个binder讣告监听器,一旦日后用户进程意外挂掉,AMS就能在第一时间感知到,并采取相应的措施。如果AMS发现意外挂掉的应用是persistent的,它会尝试重新启动这个应用。</p>
    <p>注册讣告监听器的代码如下:</p>
    <p> </p>
    <pre class="brush:java;">AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
    thread.asBinder().linkToDeath(adr,0);
    app.deathRecipient = adr;</pre>
    <p> </p>
    <p>其中的thread就是IApplicationThread代理。</p>
    <p>AppDeathRecipient的定义如下:</p>
    <p> </p>
    <pre class="brush:java;">private final class AppDeathRecipient implementsIBinder.DeathRecipient
    {
        final ProcessRecord mApp;
        final int mPid;
        final IApplicationThread mAppThread;
      
        AppDeathRecipient(ProcessRecord app, intpid,
                IApplicationThread thread)
        {
            if(localLOGV)
                Slog.v(TAG,New death recipient  + this
                       + for thread  + thread.asBinder());
            mApp = app;
            mPid = pid;
            mAppThread = thread;
        }
      
        publicvoidbinderDied()
        {
            if(localLOGV)
                Slog.v(TAG,Death received in  + this
                       + for thread  + mAppThread.asBinder());
            synchronized(ActivityManagerService.this)
            {
                appDiedLocked(mApp, mPid, mAppThread);
            }
        }
    }</pre>
    <br>
    <p> </p>
    <p>当其监听的binder实体死亡时,系统会回调AppDeathRecipient的binderDied()。这个回调函数会辗转重启persistent应用,调用关系如下:</p>
    <p><img alt="" src="http://www.2cto.com/uploadfile/Collfiles/20150104/20150104091208196.gif" style=" 544px; height: 370px;"></p>
    <p> </p>
    <p>一般情况下,当一个应用进程挂掉后,AMS当然会清理掉其对应的ProcessRecord,这就是cleanUpApplicationRecordLocked()的主要工作。然而,对于persistent应用,cleanUpApplicationRecordLocked()会尝试再次启动对应的应用进程。代码截选如下:</p>
    <p> </p>
    <pre class="brush:java;">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)
        {
            . . . . . .
        }
        . . . . . .
    }</pre>
    <br>
    <p> </p>
    <p>现在我们可以画一张关于“启动persistent应用”的示意图:</p>
    <p><img alt="" src="http://www.2cto.com/uploadfile/Collfiles/20150104/20150104091209197.gif" style=" 623px; height: 385px;"></p>
    <p> </p>
    <h1>3 补充知识点</h1>
    <h2>3.1 persistent应用可以在系统未准备好时启动</h2>
    <p>在AMS中,有一个isAllowedWhileBooting()函数,其代码如下:</p>
    <pre class="brush:java;">boolean isAllowedWhileBooting(ApplicationInfo ai)
    {
        return (ai.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
    }
    </pre>
    <p>从这个函数可以看到,将persistent属性设为true的应用,是允许在boot的过程中启动的。我们可以查看前文提到的startProcessLocked()函数:</p>
    <p> </p>
    <pre class="brush:java;">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;
    }</pre>
    <p> </p>
    ?
    <p>其中的最后几句可以改写为以下更易理解的形式:</p>
    <p> </p>
    <pre class="brush:java;">if (mProcessesReady || isAllowedWhileBooting(info) || allowWhileBooting)
    {
        startProcessLocked(app, hostingType, hostingNameStr);
        return (app.pid != 0) ? app : null;
    }
    else
    {
        . . . . . .
        returnapp;
    }</pre>
    <br>
    <p> </p>
    ?<br>
    <br>
    <p>也就是说,当系统已经处于以下几种情况时,多参数的startProcessLocked()会进一步调用另一个只有三个参数的startProcessLocked():<br>
    1)系统已经处于ready状态;<br>
    2)想要启动persistent应用;<br>
    3)参数中明确指定可以在boot过程中启动应用。</p>
    <p>补充说一下,一般情况下,当AMS调用startProcessLocked()时,传入的allowWhileBooting参数都为false。比如说,当系统需要启动“某个content provider或者某个service或者某个特定activity”时,此时传给startProcessLocked()的allowWhileBooting参数是写死为false的。只有一种特殊情况下会在该参数中传入true,那就是当系统发出的广播intent中携带有Intent.FLAG_RECEIVER_BOOT_UPGRADE标记时,此时允许在系统未ready时,启动接受广播的目标进程。</p>
    <h1> </h1>
    </applicationinfo></n;>
     

    结伴旅游,一个免费的交友网站:www.jieberu.com

    推推族,免费得门票,游景区:www.tuituizu.com

  • 相关阅读:
    Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file'
    ptconfigdiff的使用
    freebsd上安装sudo
    vm9.02的序列号
    pttablechecksum
    "Makefile", line 3: Need an operator
    nc的使用
    vs2005自带的水晶报表破解方法
    [vs2008环境]绑定水晶报表的两种方式(Pull和Push)
    .NET环境下水晶报表使用总结
  • 原文地址:https://www.cnblogs.com/rabbit-bunny/p/4205214.html
Copyright © 2020-2023  润新知