1. 程式人生 > >說說Android應用的persistent屬性(轉)

說說Android應用的persistent屬性(轉)

bsp 是什麽 添加 mpat ltrace cleanup activity 異步 rip

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屬性(轉)