Android编程权威指南总结(九)

本文详细解析如何在Android中使用隐式Intent创建一个替代默认启动器的应用——NerdLauncher,涵盖Intent过滤器、在运行时创建Intent、任务与回退栈、进程与任务的区别,以及如何将NerdLauncher设为主屏幕。
摘要由CSDN通过智能技术生成

第二十四章      深入学习intent和任务

      本章将使用隐式intent创建一个替换Android默认启动器的应用。新建应用名为NerdLauncher。NerdLauncher应用能列出设备上的其他应用。点选任意列表项会启动相应应用。完成该应用能帮你深入理解intentintent过滤器,搞清楚Android应用间是如何交互的。

一、创建 NerdLauncher 项目

二、解析隐式intent

      NerdLaucher应用会列出设备上的可启动应用。(可启动应用是指点击主屏幕或启动器界面上的图标就能打开的应用)。要实现该功能,它会使用PackageManager获取所有可启动主activity。可启动主 activity 都带有包含 MAIN 操作和 LAUNCHER 类别的 intent 过滤器。

private void setupAdapter() {
    Intent startupIntent = new Intent(Intent.ACTION_MAIN);
    startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    PackageManager pm = getActivity().getPackageManager();
    List<ResolveInfo> activities = pm.queryIntentActivities(startupIntent, 0);
    Log.i(TAG, "Found " + activities.size() + " activities.");
}

      调用上面的 setupAdapter() 方法,可以看到 PackageManager 返回多少个Activity。

      注意:MAIN/LAUNCHER  intent 过滤器可能无法与通过 startActivity(...) 方法发送的 MAIN/LAUNCHER   隐式intent相匹配。事实上,startActivity(Intent) 方法意味着“启动匹配隐式 intent 的默认 activity”,而不是想当然的“启动匹配隐式 intent 的 activity”。调用startActivity(Intent) 方法(或 startActivityForResult(...) 方法)发送隐式 intent 时,操作系统会悄悄为目标 intent 添加 Intent.CATEGORY_DEFAULT 类别。

      因此,如果希望intent过滤器匹配startActivity(...)方法发送的隐式intent,就必须在对应的intent过滤器中包含DEFAULT类别。

      定义了 MAIN/LAUNCHER  intent 过滤器的activity是应用的主要入口点。它只负责做好作为应用主要入口点要处理的工作。它通常不关心自己是否为默认的主要入口点,所以可以不包含CATEGORY_DEFAULT类别。

      前面说过,MAIN/LAUNCHER intent过滤器并不一定包含CATEGORY_DEFAULT类别,因此不能保证可以与startActivity(...)方法发送的隐式intent匹配。所以,我们转而使用intent直接向PackageManager查询带有MAIN/LAUNCHER intent过滤器的activity

      在PackageManager返回的ResolveInfo对象中,可以获取activity标签和其他一些元数据。

      使用 ResolveInfo.loadLabel(PackageManager) 方法,对 ResolveInfo 对象中的 activity 标签按首字母排序:

/**
 * activities:获取到的activity的List
 */
Collections.sort(activities, new Comparator<ResolveInfo>() {
    @Override
    public int compare(ResolveInfo o1, ResolveInfo o2) {
        PackageManager pm = getActivity().getPackageManager();
        return String.CASE_INSENSITIVE_ORDER.compare(
                o1.loadLabel(pm).toString(),
                o2.loadLabel(pm).toString()
        );
    }
});

三、在运行时创建显示 Intent

      要创建启动 activity 的显式 intent,需要从 ResolveInfo 对象中获取 activity 的包名与类名。这些信息可以从 ResolveInfo 对象的 ActivityInfo 中获取。

ActivityInfo activityInfo = mResolveInfo.activityInfo;
Intent i = new Intent(Intent.ACTION_MAIN)
        .setClassName(activityInfo.applicationInfo.packageName,
activityInfo.name);
startActivity(i);

四、任务与回退栈

      任务是一个activity栈。栈底部的activity通常称为activity。栈顶的activity用户能看得到。如果按后退键,栈顶activity会弹出栈外。如果用户看到的是基activity,按后退键,系统就会回到主屏幕。

      默认情况下,新activity都在当前任务中启动。在当前任务中启动activity的好处是,用户可以在任务内而不是在应用层级间导航返回。

1、在任务间切换

      比如,用户先打开微信,之后按Home键返回主屏幕,再打开淘宝,然后长按Home键(每个手机操作不一样,不一定都是长按Home键),会出现微信和淘宝的类似缩略图一样的卡片,点击微信就会回到之前打开的微信页面。

      手机上每个应用显示项(就是Lollipop系统所说的卡片)就代表着一个应用任务。当前任务显示的是处于回退栈顶部activity的快照。你可以点击任意显示项切换至对应应用的当前activity。要清除应用任务,用户只需滑动移除卡片即可。清除任务会从应用回退栈中清除所有activity

2、启动新任务

      有时你需要在当前任务中启动activity,而有时又需要在新任务中启动activity。当前,从 NerdLauncher 项目启动的任何activity都会添加到 NerdLauncher 任务中。

      我们需要 NerdLauncher 在新任务中启动activity,如下图。这样,点击 NerdLauncher 启动器中的应用项可以让应用拥有自己的任务,用户就可以在运行的应用间自由切换了。

      为了在启动新activity时启动新任务,需要为intent添加一个标志:

这样就会看到 CriminalIntent 应用处于一个单独的任务中。

      如果从 NerdLauncher 应用中再次启动 CriminalIntent 应用,也不会创建第二个 CriminalIntent 任务。FLAG_ACTIVITY_NEW_TASK 标志控制每个 activity 仅创建一个任务。已经有了一个运行的任务之后,Android 会自动切换到原来的任务,而不是创建新的任务。

五、使用 NerdLauncher 应用作为设备主屏幕

      打开 NerdLauncher 项目的 AndroidManifest. xml,向 intent 主过滤器添加以下节点定义:

<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />

      添加 HOME 和 DEFAULT 类别定义后,NerdLauncher 应用的 activity 会成为可选的主界面。按主屏幕键可以看到,在弹出的对话框中,NerdLauncher 变成了主界面可选项。

六、深入学习进程与任务

      对象需要内存和虚拟机的支持才能生存。进程是操作系统创建的、供应用对象生存以及应用运行的地方。

      进程通常会占用由操作系统管理着的系统资源,如内存、网络端口以及打开的文件等。进程还拥有至少一个(可能多个)执行线程。在Android系统中,每个进程都需要一个虚拟机来运行。

      尽管存在未知的异常情况,但总的来说,Android世界里的每个应用组件都仅与一个进程相关联。应用伴随着自己的进程一起完成创建,该进程同时也是应用中所有组件的默认进程。

      每一个activity实例都仅存在于一个进程之中,同一个任务关联。这也是进程与任务的唯一相似之处。任务只包含 activity,这些 activity 通常来自于不同的应用进程;而进程则包含了应用的全部运行代码和对象。

      进程与任务很容易让人混淆,主要原因在于它们不仅在概念上有某种重叠,而且通常会被人以应用名提及。例如,从NerdLauncher 启动器中启动 CriminalIntent 应用时,操作系统创建了一个 CriminalIntent 进程以及一个以 CrimeListActivity 为基栈 activity 的新任务。在 overview screen(前面提到的长按 Home 键出现的应用卡片) 中,可以看到这个任务就被标名为CriminalIntent

      包含 activity 的任务和它赖以生存的进程有可能会不同。例如:

      打开 CriminalIntent 应用,选择任意 crime 项(或添加一条crime记录),然后点击 CHOOSE SUSPECT 按钮。这会打开联系人应用让你选择目标联系人。随即,联系人列表 activity 会被加入 CriminalIntent 应用任务中。如果此时按后退键在不同 activity 间切换的话,用户可能意识不到他们正在进程间切换。

      然而,联系人 activity 实例实际是在联系人应用进程的内存空间创建的,而且也是在该应用进程里的虚拟机上运行的,下图:

      为进一步了解进程和任务的概念,让 CriminalIntent 应用运行着,进入联系人列表界面。(继续之前,请确保在overview screen里看不到联系人应用。)按主屏幕键回到主屏幕,从中启动联系人应用。然后从联系人列表选取任意联系人,或添加新联系人。

      在这个操作过程中,系统会在联系人应用进程中创建新的联系人列表 activity 和联系人明细界面实例。也会创建联系人应用新任务。这个新任务会引用联系人列表和联系人明细界面activity实例,下图:

      总结来说,就是:一个任务中的 activity 可能不都是同一个进程,一个进程中的 Activity 实例可能不都是同一个任务创建的。

七、并发文档

      有了并发文档,就可以为运行的应用动态创建任意数目的任务。在 Lollipop(5.0) 之前,应用任务只能预先定义好,而且还要在manifest文件中指明。

      如果需要应用启动多个任务,比如打开编辑多份文档,可采用两种方式:给 intent 打上 Intent. FLAG_ACTIVITY_NEW_DOCUMENT 标签,再调用startActivity(...) 方法;或者在 manifest 文件中,为 activity 设置如下 documentLaunchMode

android:documentLaunchMode="intoExisting"

      使用上述方法,一份文档只会对应一个任务。(如果发送带有和已存在任务相同数据的 intent,系统就不会再创建新任务。)如果无论如何都想创建新任务,那就给 intent 同时打上 Intent.FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_MULTIPLE_TASK 标签,或者把 manifest 文件中的 documentLaunchMode 属性值改为 always

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值