说说Android应用的persistent属性

侯 亮

1 启动persistent应用

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

<applicationandroid:name="PhoneApp"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">

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

publicvoid systemReady(finalRunnable goingCallback)
{
    . . . . . .
    . . . . . .
    try{
        List apps = AppGlobals.getPackageManager().
                        getPersistentApplications(STOCK_PM_FLAGS);
        if(apps != null)
        {
            intN = apps.size();
            inti;

            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.
staticfinal int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

publicList<ApplicationInfo>
getPersistentApplications(
intflags)

{

    finalArrayList<ApplicationInfo>
finalList =
newArrayList<ApplicationInfo>();

    //
reader

    synchronized(mPackages)

    {

        finalIterator<PackageParser.Package>
i = mPackages.values().iterator();

        finalint

userId = UserId.getCallingUserId();

        while(i.hasNext())

        {

            finalPackageParser.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));

            }

        }

    }

    returnfinalList;

}

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

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

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

finalProcessRecord
addAppLocked(ApplicationInfo info,
booleanisolated)

{

    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);

    }

    returnapp;

}

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

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

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

privatefinal

boolean

attachApplicationLocked(IApplicationThread thread,
intpid)
{

        //
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,

                    newConfiguration(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的,它会尝试重新启动这个应用。

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

?


1

2

3

AppDeathRecipient
adr =
newAppDeathRecipient(app,
pid, thread);

thread.asBinder().linkToDeath(adr,0);

app.deathRecipient
= adr;

其中的thread就是IApplicationThread代理。

AppDeathRecipient的定义如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

privatefinal

class

AppDeathRecipient
implementsIBinder.DeathRecipient

{

    finalProcessRecord
mApp;

    finalint

mPid;

    finalIApplicationThread
mAppThread;

    AppDeathRecipient(ProcessRecord
app,
intpid,

            IApplicationThread
thread)

    {

        if(localLOGV)

            Slog.v(TAG,"New
death recipient "

+
this

                   +"
for thread "

+ thread.asBinder());

        mApp
= app;

        mPid
= pid;

        mAppThread
= thread;

    }

    publicvoid

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()会尝试再次启动对应的应用进程。代码截选如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

privatefinal

void

cleanUpApplicationRecordLocked(ProcessRecord app,

                                                  booleanrestarting,

                                                  booleanallowRestart,
intindex)

{

    .
. . . . .

    .
. . . . .

    if(!app.persistent
|| app.isolated)

    {

        .
. . . . .

        mProcessNames.remove(app.processName,
app.uid);

        mIsolatedProcesses.remove(app.uid);

        .
. . . . .

    }

    elseif

(!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);

    }

    elseif

(app.pid >
0&&
app.pid != MY_PID)

    {

        .
. . . . .

    }

    .
. . . . .

}

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

3 补充知识点

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

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

?


1

2

3

4

booleanisAllowedWhileBooting(ApplicationInfo
ai)

{

    return(ai.flags
& ApplicationInfo.FLAG_PERSISTENT) !=
0;

}

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

finalProcessRecord
startProcessLocked(String processName,

                                       ApplicationInfo
info,
booleanknownToBeDead,

                                       intintentFlags,

                                       String
hostingType, ComponentName hostingName,

                                       booleanallowWhileBooting,

                                       booleanisolated)

{

    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);

        returnapp;

    }

    startProcessLocked(app,
hostingType, hostingNameStr);

    return(app.pid
!=
0)
? app :
null;

}

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

?


1

2

3

4

5

6

7

8

9

10

if (mProcessesReady
|| isAllowedWhileBooting(info) || allowWhileBooting)

{

    startProcessLocked(app,
hostingType, hostingNameStr);

    return(app.pid
!=
0)
? app :
null;

}

else

{

    .
. . . . .

    returnapp;

}

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

时间: 2024-07-29 07:32:19

说说Android应用的persistent属性的相关文章

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

1 启动persistent应用 在Android系统中,有一种永久性应用.它们对应的AndroidManifest.xml文件里,会将persistent属性设为true,比如: <application android:name="PhoneApp" android:persistent="true" android:label="@string/dialerIconLabel" android:icon="@drawable

android中xmlns:tools属性详解

第一部分 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果. 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了text.因此为了在ide中预览效果,你必须在xml中为TextView控件设置android:text属性 <TextView android:id="@+id/text_main" android:layout_width="match_parent" and

Android控件常见属性

1.宽/高android:layout_width android:layout_height// 取值match_parent //匹配父控件wrap_content //自适应,根据内容 如果指定宽度,请用单位dp 2.控件在父控件中的对齐位置android:layout_gravity 3.控件中文本的对齐方式android:gravity 4.控件内元素的排列方式android:orientation 取值:horizontal 水平 vertical 垂直 5.文字大小 android

[转]Android ImageView的scaleType属性与adjustViewBounds属性

Android ImageView的scaleType属性与adjustViewBounds属性 ImageView的scaleType的属性有好几种,分别是matrix(默认).center.centerCrop.centerInside.fitCenter.fitEnd.fitStart.fitXY android:scaleType="center" 保持原图的大小,显示在ImageView的中心.当原图的size大于ImageView的size,超过部分裁剪处理. androi

android中xml tools属性详解

第一部分 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果. 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了text.因此为了在ide中预览效果,你必须在xml中为TextView控件设置android:text属性 1 2 3 4 5 6 7 <TextView   android:id="@+id/text_main"   android:layout_width="matc

android中xml tools属性详解(转)

第一部分 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果. 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了text.因此为了在ide中预览效果,你必须在xml中为TextView控件设置android:text属性 1 2 3 4 5 6 7 <TextView   android:id="@+id/text_main"   android:layout_width="matc

Android中Edittext的属性

//此为转载别人的,挺不错的 1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: 把该EditText设为:android:password="true" // 以”.”形式显示文本 (2)在代码里设置: 通过设置EditText的setTransformationMethod()方法来实现隐藏密码或这显示密码. editText.setTransformationMethod(PasswordTransformationMethod.getInstance()

Android开发之EditText属性详解

1.EditText输入的文字为密码形式的设置 (1)通过.xml里设置: 把该EditText设为:android:password="true" // 以"."形式显示文本 (2)在代码里设置: 通过设置EditText的setTransformationMethod()方法来实现隐藏密码或这显示密码. editText.setTransformationMethod(PasswordTransformationMethod.getInstance());//设

Android中RelativeLayout各个属性的含义

androidlayout android:layout_above="@id/xxx"        --将控件置于给定ID控件之上 android:layout_below="@id/xxx"        --将控件置于给定ID控件之下 android:layout_toLeftOf="@id/xxx"     --将控件的右边缘和给定ID控件的左边缘对齐 android:layout_toRightOf="@id/xxx&quo