(4.6.12.5)说说Android应用的persistent属性

说说Android应用的persistent属性

 

侯 亮

 

 

1 启动persistent应用

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

[html]  view plain  copy
 
  1. <application android:name="PhoneApp"  
  2. android:persistent="true"  
  3. android:label="@string/dialerIconLabel"  
  4. android:icon="@drawable/ic_launcher_phone">  


    在系统启动之时,AMS的systemReady()会加载所有persistent为true的应用。

[java]  view plain  copy
 
  1. public void systemReady(final Runnable goingCallback)   
  2. {  
  3.     . . . . . .  
  4.     . . . . . .  
  5.     try{  
  6.         List apps = AppGlobals.getPackageManager().  
  7.                         getPersistentApplications(STOCK_PM_FLAGS);  
  8.         if(apps != null)  
  9.         {  
  10.             intN = apps.size();  
  11.             inti;  
  12.                
  13.             for(i=0; i<N; i++)   
  14.             {  
  15.                 ApplicationInfo info = (ApplicationInfo)apps.get(i);  
  16.                 if(info != null&&  
  17.                         !info.packageName.equals("android"))  
  18.                 {  
  19.                     addAppLocked(info,false);  
  20.                 }  
  21.             }  
  22.         }  
  23.     }  
  24.     catch(RemoteException ex) {  
  25.         // pm is in same process, this will never happen.  
  26.     }  

其中的STOCK_PM_FLAGS的定义如下:

[java]  view plain  copy
 
  1. // The flags that are set for all calls we make to the package manager.  
  2. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;  


上面代码中的getPersistentApplications()函数的定义如下:

[java]  view plain  copy
 
  1. public List<ApplicationInfo> getPersistentApplications(int flags)   
  2. {  
  3.     final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();  
  4.    
  5.     // reader  
  6.     synchronized (mPackages)   
  7.     {  
  8.         final Iterator<PackageParser.Package> i = mPackages.values().iterator();  
  9.         final int userId = UserId.getCallingUserId();  
  10.         while (i.hasNext())   
  11.         {  
  12.             final PackageParser.Package p = i.next();  
  13.             if (p.applicationInfo != null  
  14.                 && (p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0  
  15.                 && (!mSafeMode || isSystemApp(p)))   
  16.             {  
  17.                 PackageSetting ps = mSettings.mPackages.get(p.packageName);  
  18.                 finalList.add(PackageParser.generateApplicationInfo(p, flags,  
  19.                         ps != null ? ps.getStopped(userId) : false,  
  20.                         ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,  
  21.                         userId));  
  22.             }  
  23.         }  
  24.     }  
  25.    
  26.     return finalList;  
  27. }  

     在PKMS中,有一个记录所有的程序包信息的哈希表(mPackages),每个表项中含有ApplicationInfo信息,该信息的flags(int型)数据中有一个专门的bit用于表示persistent。getPersistentApplications()函数会遍历这张表,找出所有persistent包,并返回ArrayList<ApplicationInfo>。


     从代码里可以看出,带persistent标志的系统应用(即flags中设置了FLAG_SYSTEM)是一定会被选上的,但如果不是系统应用的话,则要进一步判断当前是否处于“安全模式”,一旦处于安全模式,那么就算应用设置了persistent属性,也不会被选中。

     随后systemReady()开始遍历选中的ApplicationInfo,并对包名不为“android”的结点执行addAppLocked()。addAppLocked()的代码如下:

[java]  view plain  copy
 
  1. <pre name="code" class="java">final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated)   
  2. {  
  3.     ProcessRecord app;  
  4.     if (!isolated) {  
  5.         app = getProcessRecordLocked(info.processName, info.uid);  
  6.     } else {  
  7.         app = null;  
  8.     }  
  9.    
  10.     if (app == null) {  
  11.         app = newProcessRecordLocked(null, info, null, isolated);  
  12.         mProcessNames.put(info.processName, app.uid, app);  
  13.         if (isolated) {  
  14.             mIsolatedProcesses.put(app.uid, app);  
  15.         }  
  16.         updateLruProcessLocked(app, truetrue);  
  17.     }  
  18.    
  19.     // This package really, really can not be stopped.  
  20.     try {  
  21.         AppGlobals.getPackageManager().setPackageStoppedState(  
  22.                 info.packageName, false, UserId.getUserId(app.uid));  
  23.     } catch (RemoteException e) {  
  24.     } catch (IllegalArgumentException e) {  
  25.         Slog.w(TAG, "Failed trying to unstop package "  
  26.                 + info.packageName + ": " + e);  
  27.     }  
  28.    
  29.     if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))  
  30.             == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {  
  31.         app.persistent = true;  
  32.         app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;  
  33.     }  
  34.     if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {  
  35.         mPersistentStartingProcesses.add(app);  
  36.         startProcessLocked(app, "added application", app.processName);  
  37.     }  
  38.    
  39.     return app;  
  40. }  

 
 

?
    在AMS中,所谓的“add App”主要是指“添加一个与App进程对应的ProcessRecord节点”。当然,如果该节点已经添加过了,那么是不会重复添加的。在添加节点的动作完成以后,addAppLocked()还会检查App进程是否已经启动好了,如果尚未开始启动,此时就会调用startProcessLocked()启动这个进程。既然addAppLocked()试图确认App“正在正常运作”或者“将被正常启动”,那么其对应的package就不可能处于stopped状态,这就是上面代码调用setPackageStoppedState(...,false,...)的意思。


    现在,我们就清楚了,那些persistent属性为true的应用,基本上都是在系统启动伊始就启动起来的。

    因为启动进程的过程是异步的,所以我们需要一个缓冲列表(即上面代码中的mPersistentStartingProcesses列表)来记录那些“正处于启动状态,而又没有启动完毕的”ProcessRecord结点。一旦目标进程启动完毕后,目标进程会attach系统,于是走到AMS的attachApplicationLocked(),在这个函数里,会把目标进程对应的ProcessRecord结点从mPersistentStartingProcesses缓冲列表里删除。

[java]  view plain  copy
 
  1. private final boolean attachApplicationLocked(IApplicationThread thread, intpid) {  
  2.    
  3.         // Find the application record that is being attached...  either via  
  4.         // the pid if we are running in multiple processes, or just pull the  
  5.         // next app record if we are emulating process with anonymous threads.  
  6.         ProcessRecord app;  
  7.            
  8.         . . . . . .  
  9.         thread.asBinder().linkToDeath(adr,0);  
  10.         . . . . . .  
  11.         thread.bindApplication(processName, appInfo, providers,  
  12.                     app.instrumentationClass, profileFile, profileFd, profileAutoStop,  
  13.                     app.instrumentationArguments, app.instrumentationWatcher, testMode,  
  14.                     enableOpenGlTrace, isRestrictedBackupMode || !normalMode,   
  15.                     app.persistent,  
  16.                     newConfiguration(mConfiguration), app.compat,   
  17.                     getCommonServicesLocked(),  
  18.                     mCoreSettingsObserver.getCoreSettingsLocked());  
  19.         . . . . . .  
  20.         . . . . . .  
  21.         // Remove this record from the list of starting applications.  
  22.         mPersistentStartingProcesses.remove(app);  
  23.         . . . . . .  

?

2 如何保证应用的持久性(persistent)

    我们知道,persistent一词的意思是“持久”,那么persistent应用的意思又是什么呢?简单地说,这种应用会顽固地运行于系统之中,从系统一启动,一直到系统关机。

    为了保证这种持久性,persistent应用必须能够在异常出现时,自动重新启动。在Android里是这样实现的。每个ActivityThread中会有一个专门和AMS通信的binder实体——final ApplicationThread mAppThread。这个实体在AMS中对应的代理接口为IApplicationThread。

    当AMS执行到attachApplicationLocked()时,会针对目标用户进程的IApplicationThread接口,注册一个binder讣告监听器,一旦日后用户进程意外挂掉,AMS就能在第一时间感知到,并采取相应的措施。如果AMS发现意外挂掉的应用是persistent的,它会尝试重新启动这个应用。

    注册讣告监听器的代码如下:

[java]  view plain  copy
 
  1. AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);  
  2. thread.asBinder().linkToDeath(adr,0);  
  3. app.deathRecipient = adr;  

其中的thread就是IApplicationThread代理。

    AppDeathRecipient的定义如下:

[java]  view plain  copy
 
  1. private final class AppDeathRecipient implementsIBinder.DeathRecipient  
  2. {  
  3.     final ProcessRecord mApp;  
  4.     final int mPid;  
  5.     final IApplicationThread mAppThread;  
  6.    
  7.     AppDeathRecipient(ProcessRecord app, intpid,  
  8.             IApplicationThread thread)   
  9.     {  
  10.         if(localLOGV)  
  11.             Slog.v(TAG,"New death recipient " + this  
  12.                    +" for thread " + thread.asBinder());  
  13.         mApp = app;  
  14.         mPid = pid;  
  15.         mAppThread = thread;  
  16.     }  
  17.    
  18.     publicvoidbinderDied()   
  19.     {  
  20.         if(localLOGV)  
  21.             Slog.v(TAG,"Death received in " + this  
  22.                    +" for thread " + mAppThread.asBinder());  
  23.         synchronized(ActivityManagerService.this)  
  24.         {  
  25.             appDiedLocked(mApp, mPid, mAppThread);  
  26.         }  
  27.     }  
  28. }  

当其监听的binder实体死亡时,系统会回调AppDeathRecipient的binderDied()。这个回调函数会辗转重启persistent应用,调用关系如下:

(4.6.12.5)说说Android应用的persistent属性_第1张图片


    一般情况下,当一个应用进程挂掉后,AMS当然会清理掉其对应的ProcessRecord,这就是cleanUpApplicationRecordLocked()的主要工作。然而,对于persistent应用,cleanUpApplicationRecordLocked()会尝试再次启动对应的应用进程。代码截选如下:

[java]  view plain  copy
 
  1. private final void cleanUpApplicationRecordLocked(ProcessRecord app,  
  2.                                                   boolean restarting,  
  3.                                                   boolean allowRestart,int index)  
  4. {  
  5.     . . . . . .  
  6.     . . . . . .  
  7.     if (!app.persistent || app.isolated)   
  8.     {  
  9.         . . . . . .  
  10.         mProcessNames.remove(app.processName, app.uid);  
  11.         mIsolatedProcesses.remove(app.uid);  
  12.         . . . . . .   
  13.     }  
  14.     else if(!app.removed)   
  15.     {  
  16.         if(mPersistentStartingProcesses.indexOf(app) < 0) {  
  17.             mPersistentStartingProcesses.add(app);  
  18.             restart = true;  
  19.         }  
  20.     }  
  21.     . . . . . .  
  22.     . . . . . .  
  23.     if (restart && !app.isolated)   
  24.     {  
  25.         mProcessNames.put(app.processName, app.uid, app);  
  26.         startProcessLocked(app,"restart", app.processName);  
  27.     }  
  28.     else if(app.pid > 0&& app.pid != MY_PID)   
  29.     {  
  30.         . . . . . .  
  31.     }  
  32.     . . . . . .  
  33. }  

    现在我们可以画一张关于“启动persistent应用”的示意图:


 

3 补充知识点

3.1 persistent应用可以在系统未准备好时启动

    在AMS中,有一个isAllowedWhileBooting()函数,其代码如下:

[java]  view plain  copy
 
  1. boolean isAllowedWhileBooting(ApplicationInfo ai)   
  2. {  
  3.     return (ai.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;  
  4. }  

    从这个函数可以看到,将persistent属性设为true的应用,是允许在boot的过程中启动的。我们可以查看前文提到的startProcessLocked()函数:

[java]  view plain  copy
 
  1. final ProcessRecord startProcessLocked(String processName,  
  2.                                        ApplicationInfo info, boolean knownToBeDead,  
  3.                                        int intentFlags,  
  4.                                        String hostingType, ComponentName hostingName,   
  5.                                        boolean allowWhileBooting,  
  6.                                        boolean isolated)  
  7. {  
  8.     ProcessRecord app;  
  9.        
  10.     if(!isolated)  
  11.     {  
  12.         app = getProcessRecordLocked(processName, info.uid);  
  13.     }  
  14.     else  
  15.     {  
  16.         // If this is an isolated process, it can't re-use an existing process.  
  17.         app = null;  
  18.     }  
  19.    
  20.     . . . . . .  
  21.     . . . . . .  
  22.        
  23.     if(!mProcessesReady  
  24.         && !isAllowedWhileBooting(info)  
  25.         && !allowWhileBooting) {  
  26.         if(!mProcessesOnHold.contains(app)) {  
  27.             mProcessesOnHold.add(app);  
  28.         }  
  29.         if(DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);  
  30.         return app;  
  31.     }  
  32.    
  33.     startProcessLocked(app, hostingType, hostingNameStr);  
  34.     return (app.pid != 0) ? app : null;  
  35. }  

?

其中的最后几句可以改写为以下更易理解的形式:

[java]  view plain  copy
 
  1. if (mProcessesReady || isAllowedWhileBooting(info) || allowWhileBooting)   
  2. {  
  3.     startProcessLocked(app, hostingType, hostingNameStr);  
  4.     return (app.pid != 0) ? app : null;  
  5. }  
  6. else  
  7. {  
  8.     . . . . . .  
  9.     returnapp;  
  10. }  

?

    也就是说,当系统已经处于以下几种情况时,多参数的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属性,我们就先说这么多。希望对大家有点儿帮助。

你可能感兴趣的:((4.6.12.5)说说Android应用的persistent属性)