Android關(guān)鍵字persistent原理分析

在Android程序開發(fā)時我們會接觸到一些系統(tǒng)為了某些功能而定義的關(guān)鍵屬性,例如在AndroidManifest.xml文件中

經(jīng)??吹降膒ersistent、process等,下面是自己對persistent關(guān)鍵字的分析,直奔主題。

一、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" />



通過官方注釋我知道該屬性用于是否讓你的應(yīng)用一直處于運(yùn)行狀態(tài)(通常說的常駐內(nèi)存)。設(shè)置
該屬性為true的app具有如下特點(diǎn):

  • 在系統(tǒng)啟動的時候會被系統(tǒng)啟動起來

  • 在該app被強(qiáng)制殺掉后系統(tǒng)會重新啟動該app,這種情況只針對系統(tǒng)內(nèi)置app,第三方安裝的app不會被重啟

2、使用

persistent屬性是用于application標(biāo)簽上的,用法為:

AndroidManifest.xml


<application

    android:persistent="true|false"

>



</application>



persistent的值默認(rèn)為false

二、原理分析

通過第一點(diǎn)對persistent的功能說明后我們通過源碼來分析一下它的工作原理

1、persistent屬性的解析

該屬性的解析主要在app被安裝或者系統(tǒng)啟動的時候發(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)會將解析好的所有包信息存放到PKMS中的mPackages的map中,而ApplicationInfo的flag中有一個bit位用于保存該app是否是persistent的。這里只是把保存persistent的flag設(shè)置為FLAG_PERSISTENT。在AndroidManifest設(shè)置了persistent為true的app是否能夠在被異常殺死后能夠得到重啟的權(quán)力需要取決于該app對應(yīng)的ProcessRecord的persistent屬性,該屬性只有在你的app既在AndroidManifest中配置了persistent=“true”,又是系統(tǒng)內(nèi)置app時才會被設(shè)置為true。

2、系統(tǒng)啟動時啟動persistent為true的app

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

在系統(tǒng)啟動時ActivityManagerService的systemReady()方法會將所有在AndroidManifest設(shè)置了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中調(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方法去啟動他們。這樣在AndroidManifest設(shè)置了persistent為true的app就隨著系統(tǒng)的啟動而啟動了。
下面看一下getPersistentApplications方法,該方法調(diào)用了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設(shè)置了persistent為true的應(yīng)用。從代碼中可以看到,persistent為true并且是系統(tǒng)app的話一定會被選中,但是如果是第三方安裝的應(yīng)用的話只能在非“安全模式”下才會被選中。

之后調(diào)用addAppLocked方法啟動app:







    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,

            String abiOverride) {

        ProcessRecord app;

        //傳遞進(jìn)來的isolated=false,所有一定會調(diào)用getProcessRecordLocked方法,但是由于是第一次啟動,所有返回的app = null

        if (!isolated) {

            app = getProcessRecordLocked(info.processName, info.uid, true);

        } else {

            app = null;

        }



        if (app == null) {

             //為新的app創(chuàng)建新的ProcessRecord對象

            app = newProcessRecordLocked(info, null, isolated, 0);

            updateLruProcessLocked(app, false, null);

            updateOomAdjLocked();

        }



        // This package really, really can not be stopped.

        try {

            //由于是開機(jī)第一次啟動,所以新的app的啟動狀態(tài)是將要被啟動狀態(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);

        }

      

        //在這里對persistent的app進(jìn)行過濾,只有既是系統(tǒng)app,persistent為true的app才會在

       //異常死亡之后被重啟

        if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {

            app.persistent = true;

            app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;

        }

        //如果該app已經(jīng)啟動了,則不用處理,否則調(diào)用startProcessLocked方法啟動app。

        //由于啟動app是異步進(jìn)行的,會將正在啟動而還沒有啟動完成的app添加到

        //mPersistentStartingProcesses列表中。當(dāng)啟動完成后 再移除

        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;

    }

}

接下來調(diào)用startProcessLocked方法啟動app進(jìn)程,在app啟動完成后會在ActivityThread中調(diào)用AMS的attachApplication,將該app從mPersistentStartingProcesses中移除,并注冊一個死亡訃告監(jiān)聽器AppDeathRecipient,用于在app異常被殺后的處理工作。


private final boolean attachApplicationLocked(IApplicationThread thread,

            int pid) {



......

try {

            //注冊死亡訃告監(jiān)聽器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被異常結(jié)束后系統(tǒng)重新啟動persistent為true的app

進(jìn)程啟動時為app注冊了一個死亡訃告,當(dāng)該app被殺掉之后會調(diào)用AppDeathRecipient的binderDied方法,該方法會調(diào)用appDiedLocked方法進(jìn)行善后處理,系統(tǒng)在進(jìn)程死掉之后會對死掉的進(jìn)程進(jìn)行清理和資源回收,但是在這個過程中如果你的app是persistent的話會被重啟:

binderDied

  |

  |——appDiedLocked

                 |

                 |——handleAppDiedLocked

                                      |

                                      |——cleanUpApplicationRecordLocked

在cleanUpApplicationRecordLocked中對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的,需要對其進(jìn)行重啟,并把它添加到正在啟動的列表中,并

            //設(shè)置restart=true

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

                mPersistentStartingProcesses.add(app);

                restart = true;

            }

        }

....

//經(jīng)過上面的過濾,會調(diào)用這個分支條件重啟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;



}





?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,745評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • Application 標(biāo)簽 android:allowTaskReparenting android:allow...
    Shawn_Dut閱讀 8,180評論 2 61
  • 繼任女君 那一天,我在青丘完成了繼任青丘女君的大典,終于在阿爹的臉上看到一絲笑容 ,這一刻,我是他最驕傲的女兒……...
    新新想我閱讀 468評論 0 1
  • 詞/chaizi 躲在成年人的軀殼 灰色的霾,遮住了藍(lán) 裹緊外套 提起背包 套在生活的方程式里 你看不見我 視線繞...
    ZICOLOR閱讀 304評論 0 0

友情鏈接更多精彩內(nèi)容