Android關(guān)鍵字persistent詳細(xì)分析
Android關(guān)鍵字persistent原理分析
在Android程序開發(fā)時(shí)我們會(huì)接觸到一些系統(tǒng)為了某些功能而定義的關(guān)鍵屬性,例如在AndroidManifest.xml文件中
經(jīng)常看到的persistent、process等,下面是自己對(duì)persistent關(guān)鍵字的分析,直奔主題。
persistent屬性作用
該屬性的定義在frameworks/base/core/res/res/values/attrs_manifest.xml中,其定義如下:
<attr name="persistent" format="boolean" />
通過(guò)官方注釋我知道該屬性用于是否讓你的應(yīng)用一直處于運(yùn)行狀態(tài)(通常說(shuō)的常駐內(nèi)存)。設(shè)置 該屬性為true的app具有如下特點(diǎn):
- 在系統(tǒng)啟動(dòng)的時(shí)候會(huì)被系統(tǒng)啟動(dòng)起來(lái)
- 在該app被強(qiáng)制殺掉后系統(tǒng)會(huì)重新啟動(dòng)該app,這種情況只針對(duì)系統(tǒng)內(nèi)置app,第三方安裝的app不會(huì)被重啟
使用
persistent屬性是用于application標(biāo)簽上的,用法為:
AndroidManifest.xml
<application android:persistent="true|false"> </application>
persistent的值默認(rèn)為false
二、原理分析
通過(guò)第一點(diǎn)對(duì)persistent的功能說(shuō)明后我們通過(guò)源碼來(lái)分析一下它的工作原理
1、persistent屬性的解析
該屬性的解析主要在app被安裝或者系統(tǒng)啟動(dòng)的時(shí)候發(fā)生
解析代碼:
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;
}
}
//.............
}
在解析完包信息之后系統(tǒng)會(huì)將解析好的所有包信息存放到PKMS中的mPackages的map中,而ApplicationInfo的flag中有一個(gè)bit位用于保存該app是否是persistent的。這里只是把保存persistent的flag設(shè)置為FLAG_PERSISTENT。在AndroidManifest設(shè)置了persistent為true的app是否能夠在被異常殺死后能夠得到重啟的權(quán)力需要取決于該app對(duì)應(yīng)的ProcessRecord的persistent屬性,該屬性只有在你的app既在AndroidManifest中配置了persistent=“true”,又是系統(tǒng)內(nèi)置app時(shí)才會(huì)被設(shè)置為true。
2、系統(tǒng)啟動(dòng)時(shí)啟動(dòng)persistent為true的app
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
在系統(tǒng)啟動(dòng)時(shí)ActivityManagerService的systemReady()方法會(huì)將所有在AndroidManifest設(shè)置了persistent為true的app拉起來(lái)
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中調(diào)用了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方法中首先是調(diào)用PackageManageServices的getPersistentApplications方法獲取到所有在AndroidManifest設(shè)置了persistent為true的app,然后調(diào)用addAppLocked方法去啟動(dòng)他們。這樣在AndroidManifest設(shè)置了persistent為true的app就隨著系統(tǒng)的啟動(dòng)而啟動(dòng)了。
下面看一下getPersistentApplications方法,該方法調(diào)用了PKMS中的getPersistentApplicationsInternal方法。
該方法會(huì)遍歷mPackages中的所有app,并找到其中在AndroidManifest設(shè)置了persistent為true的應(yīng)用。從代碼中可以看到,persistent為true并且是系統(tǒng)app的話一定會(huì)被選中,但是如果是第三方安裝的應(yīng)用的話只能在非“安全模式”下才會(huì)被選中。
之后調(diào)用addAppLocked方法啟動(dòng)app:
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
ProcessRecord app;
//傳遞進(jìn)來(lái)的isolated=false,所有一定會(huì)調(diào)用getProcessRecordLocked方法,但是由于是第一次啟動(dòng),所有返回的app = null
if (!isolated) {
app = getProcessRecordLocked(info.processName, info.uid, true);
} else {
app = null;
}
if (app == null) {
//為新的app創(chuàng)建新的ProcessRecord對(duì)象
app = newProcessRecordLocked(info, null, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
// This package really, really can not be stopped.
try {
//由于是開機(jī)第一次啟動(dòng),所以新的app的啟動(dòng)狀態(tài)是將要被啟動(dòng)狀態(tài),所以
//該app的停止?fàn)顟B(tài)stoped被設(shè)置為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);
}
//在這里對(duì)persistent的app進(jìn)行過(guò)濾,只有既是系統(tǒng)app,persistent為true的app才會(huì)在
//異常死亡之后被重啟
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
//如果該app已經(jīng)啟動(dòng)了,則不用處理,否則調(diào)用startProcessLocked方法啟動(dòng)app。
//由于啟動(dòng)app是異步進(jìn)行的,會(huì)將正在啟動(dòng)而還沒(méi)有啟動(dòng)完成的app添加到
//mPersistentStartingProcesses列表中。當(dāng)啟動(dòng)完成后 再移除
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
//啟動(dòng)該app
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
return app;
}
}
接下來(lái)調(diào)用startProcessLocked方法啟動(dòng)app進(jìn)程,在app啟動(dòng)完成后會(huì)在ActivityThread中調(diào)用AMS的attachApplication,將該app從mPersistentStartingProcesses中移除,并注冊(cè)一個(gè)死亡訃告監(jiān)聽器AppDeathRecipient,用于在app異常被殺后的處理工作。
3、app被異常結(jié)束后系統(tǒng)重新啟動(dòng)persistent為true的app
進(jìn)程啟動(dòng)時(shí)為app注冊(cè)了一個(gè)死亡訃告,當(dāng)該app被殺掉之后會(huì)調(diào)用AppDeathRecipient的binderDied方法,該方法會(huì)調(diào)用appDiedLocked方法進(jìn)行善后處理,系統(tǒng)在進(jìn)程死掉之后會(huì)對(duì)死掉的進(jìn)程進(jìn)行清理和資源回收,但是在這個(gè)過(guò)程中如果你的app是persistent的話會(huì)被重啟:
binderDied
|
|——appDiedLocked
|
|——handleAppDiedLocked
|
|——cleanUpApplicationRecordLocked
在cleanUpApplicationRecordLocked中對(duì)persistent為true的app進(jìn)行重啟
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的,需要對(duì)其進(jìn)行重啟,并把它添加到正在啟動(dòng)的列表中,并
//設(shè)置restart=true
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
....
//經(jīng)過(guò)上面的過(guò)濾,會(huì)調(diào)用這個(gè)分支條件重啟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;
}
總結(jié)
- persistent的聲明在AndroidManifest.xml中的 t屬性,默認(rèn)值為false
- persistent的聲明,必須該app是系統(tǒng)內(nèi)置應(yīng)用,并且在AndroidManifest.xml中的聲明android:persisten = “true”,才能生效
- persistent的聲明為true的內(nèi)置app被異常殺死的時(shí)候,系統(tǒng)會(huì)將其拉起重啟啟動(dòng)
以上就是Android關(guān)鍵字persistent詳細(xì)分析的詳細(xì)內(nèi)容,更多關(guān)于Android關(guān)鍵字persistent的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android?Flutter中異常處理的方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了Android?Flutter中異常處理的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-06-06
Kotlin 擴(kuò)展函數(shù)和擴(kuò)展屬性的使用方法
這篇文章主要介紹了Kotlin 擴(kuò)展函數(shù)和擴(kuò)展屬性的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
monkeyrunner之電腦安裝驅(qū)動(dòng)(5)
這篇文章主要為大家詳細(xì)介紹了monkeyrunner之電腦安裝驅(qū)動(dòng)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Flutter 控制屏幕旋轉(zhuǎn)的實(shí)現(xiàn)
這篇文章主要介紹了Flutter 控制屏幕旋轉(zhuǎn)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Android 使用 DowanloadManager 實(shí)現(xiàn)下載并獲取下載進(jìn)度實(shí)例代碼
這篇文章主要介紹了Android 使用 DowanloadManager 實(shí)現(xiàn)下載并獲取下載進(jìn)度實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06
Flutter實(shí)現(xiàn)App功能引導(dǎo)頁(yè)
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)App功能引導(dǎo)頁(yè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
Android Activity之間的數(shù)據(jù)傳遞方法總結(jié)
這篇文章主要給大家總結(jié)介紹了關(guān)于Android Activity之間的數(shù)據(jù)傳遞方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
Android數(shù)據(jù)加密之異或加密算法的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇Android數(shù)據(jù)加密之異或加密算法的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
Android實(shí)現(xiàn)圖片在屏幕內(nèi)縮放和移動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android控制圖片在屏幕內(nèi)縮放和移動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02

