說說Android應用的persistent屬性 java
侯 亮 android
在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的應用。 app
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()函數的定義以下: ide
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; }
從代碼裏能夠看出,帶persistent標誌的系統應用(即flags中設置了FLAG_SYSTEM)是必定會被選上的,但若是不是系統應用的話,則要進一步判斷當前是否處於「安全模式」,一旦處於安全模式,那麼就算應用設置了persistent屬性,也不會被選中。 函數
隨後systemReady()開始遍歷選中的ApplicationInfo,並對包名不爲「android」的結點執行addAppLocked()。addAppLocked()的代碼以下: ui
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; }
如今,咱們就清楚了,那些persistent屬性爲true的應用,基本上都是在系統啓動伊始就啓動起來的。 this
由於啓動進程的過程是異步的,因此咱們須要一個緩衝列表(即上面代碼中的mPersistentStartingProcesses列表)來記錄那些「正處於啓動狀態,而又沒有啓動完畢的」ProcessRecord結點。一旦目標進程啓動完畢後,目標進程會attach系統,因而走到AMS的attachApplicationLocked(),在這個函數裏,會把目標進程對應的ProcessRecord結點從mPersistentStartingProcesses緩衝列表裏刪除。 spa
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); . . . . . .
咱們知道,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應用」的示意圖:
在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時,啓動接受廣播的目標進程。
有關Android應用的persistent屬性,咱們就先說這麼多。但願對你們有點兒幫助。
如需轉載本文內容,請註明出處。