跳转至

应用启动

随着系统服务启动接近尾声,应用开始被激活,包括主屏幕。正如我在第 2 章所解释的,活动管理器通过发送类型为 Intent.CATEGORY_HOME 的 intent 结束其初始化,这会导致启动器应用启动并显示主屏幕。但这只是故事的一部分。系统服务的启动实际上会导致相当多的应用启动。以下是刚启动的 2.3/Gingerbread 模拟器镜像上 ps 命令的部分输出:

# ps
...
root      32    1     60832  16240 c009b74c afd0b844 S zygote
media     33    1     17976  1056  ffffffff afd0b6fc S /system/bin/mediaserver
bluetooth 34    1     1256   220   c009b74c afd0c59c S /system/bin/dbus-daemon
root      35    1     812    232   c02181f4 afd0b45c S /system/bin/installd
keystore  36    1     1744   212   c01b52b4 afd0c0cc S /system/bin/keystore
root      38    1     824    268   c00b8fec afd0c51c S /system/bin/qemud
shell     40    1     732    200   c0158eb0 afd0b45c S /system/bin/sh
root      41    1     3364   168   ffffffff 00008294 S /sbin/adbd
system    61    32    124096 26352 ffffffff afd0b6fc S system_server
app_19    113   32    80336  17400 ffffffff afd0c51c S com.android.inputmethod.latin
radio     121   32    87112  17972 ffffffff afd0c51c S com.android.phone
system    122   32    73160  18452 ffffffff afd0c51c S com.android.systemui
app_26    132   32    76608  20812 ffffffff afd0c51c S com.android.launcher
app_1     169   32    85368  20584 ffffffff afd0c51c S android.process.acore
app_12    234   32    70752  15748 ffffffff afd0c51c S com.android.quicksearchbox
app_8     242   32    73108  16908 ffffffff afd0c51c S android.process.media
app_10    266   32    70928  16572 ffffffff afd0c51c S com.android.providers.calendar
app_29    300   32    72764  17484 ffffffff afd0c51c S com.android.email
app_18    315   32    70272  15428 ffffffff afd0c51c S com.android.music
app_22    323   32    69712  15220 ffffffff afd0c51c S com.android.protips
app_3     335   32    71432  16756 ffffffff afd0c51c S com.cooliris.media
...

所有具有 Java 风格进程名的进程实际上都是在系统启动时无需任何用户干预而自动启动的应用。各种系统机制根据它们各自清单文件的内容导致这些应用启动。

这是一个可喜的变化,因为与应用激活控制相比,控制启动所需的内部工作要比控制我们上面看到的启动的许多其他方面少得多。相反,这完全取决于为与 AOSP 打包而精心制作的应用。当然,在某些情况下,你会想要修改一个库存应用以使其行为或启动方式不同,但至少我们进入了应用世界,在这个世界里,功能更加松散耦合,文档也更易于获取。

这就引出了我们接下来要讨论的内容:什么触发了库存应用被激活。

输入法

最早启动的应用类型之一是输入法。输入法管理器服务(Input Method Manager Service)的构造函数会遍历并激活所有具有 android.view.InputMethod intent 过滤器的应用服务。例如,这就是 LatinIME 应用(作为 com.android.inputmethod.latin 进程运行)被激活的方式。

正如你可以通过阅读 Android 开发者博客上的"创建输入法"博客文章所看到的,输入法实际上是精心设计的服务。

持久化应用

在其清单文件的 <application> 元素的 android:persistent="true" 属性的应用将在启动时由活动管理器自动生成。事实上,如果这样的应用死亡了,活动管理器也会自动重新启动它。

正如我之前解释的,与普通应用不同,标记为持久化的应用不受活动管理器的生命周期管理。相反,它们在整个系统生命周期中保持活力。这允许使用此类应用来实现特殊功能。例如,作为 com.android.systemuicom.android.phone 进程运行的状态栏和电话应用就是持久化应用。

尽管应用开发文档确实解释了 android:persistent 的作用,但该属性的使用保留给在 AOSP 内构建的应用。

主屏幕

通常只有一个主屏幕应用,它对 Intent.CATEGORY_HOME intent 做出反应,该 intent 由活动管理器在系统服务启动结束时发送。development/samples/Home/ 中有一个示例主应用,但实际激活的主应用在 packages/apps/Launcher2/ 中。以下是 2.3/Gingerbread 中启动器的主 activity 及其 intent 过滤器(4.2/Jelly Bean 的基本相同):

<activity
    android:name="com.android.launcher2.Launcher"
    android:launchMode="singleTask"
    android:clearTaskOnLaunch="true"
    android:stateNotNeeded="true"
    android:theme="@style/Theme"
    android:screenOrientation="nosensor"
    android:windowSoftInputMode="stateUnspecified|adjustPan">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.MONKEY"/>
    </intent-filter>
</activity>

显然,如果你想像 Launcher2 一样启动一个自定义应用作为主屏幕,你需要移除 Launcher2 并添加你自己的、能够对该相同 intent 做出反应的应用。如果有多个应用对该 intent 做出反应,用户将获得一个对话框,询问他们想要使用哪个主屏幕。

请注意,这个 intent 不仅在启动时发送。根据系统的状态,活动管理器将在需要将主屏幕带到前台时发送此 intent。

BOOT_COMPLETED intent

活动管理器还会在启动时广播 Intent.BOOT_COMPLETED intent。这是一个应用常用来接收系统已完成启动通知的 intent。AOSP 中的许多库存应用实际上依赖这个 intent,如媒体提供者(Media Provider)、日历提供者(Calendar provider)、Mms 应用和电子邮件应用。以下是 2.3/Gingerbread 中媒体提供者使用的广播接收器及其 intent 过滤器(4.2/Jelly Bean 的非常相似):

<receiver android:name="MediaScannerReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

为了接收此 intent,应用必须明确请求相应的权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

APPWIDGET_UPDATE intent

除了应用之外,应用小组件服务(App Widget Service)——它本身是一个系统服务——注册自己接收 Intent.BOOT_COMPLETED。它使用收到此 intent 作为触发器,通过发送 Intent.APPWIDGET_UPDATE 来激活系统中所有的应用小组件。因此,如果你在你的应用中开发了一个应用小组件,你的代码将在此时被激活。关于如何编写你自己的应用小组件的更多信息,请参阅 Android 开发者文档中的应用小组件部分。

AOSP 中有多个库存应用有应用小组件,如快速搜索框(Quick Search Box)、音乐、提示和媒体。例如,以下是快速搜索框在其清单文件中声明的应用小组件:

<receiver android:name=".SearchWidgetProvider"
          android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider" android:resource="@xml/search_widget_info" />
</receiver>