前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入探究 Android Activity 启动模式:CLEAR_TOP 与 NEW_TASK 实战解析

深入探究 Android Activity 启动模式:CLEAR_TOP 与 NEW_TASK 实战解析

作者头像
陆业聪
发布2024-08-05 15:19:04
2320
发布2024-08-05 15:19:04
举报
文章被收录于专栏:大前端修炼手册

一、探究 FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK 的行为及其应用场景

在 Android 中,我们有时需要对 Activity 的启动模式进行精细的控制,以满足特定的需求。为此,Android 提供了一些标志来帮助我们实现这些控制,其中 FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK 就是这样两个标志。

1.1 FLAG_ACTIVITY_CLEAR_TOP

首先来看 FLAG_ACTIVITY_CLEAR_TOP。当我们为一个新启动的 Activity 设置了这个标志,系统会检查当前任务栈中是否已经存在相同的 Activity 实例。

  • 如果存在,那么这个 Activity 之上的所有 Activity 都会被销毁,使得这个 Activity 实例成为栈顶。
  • 如果不存在,系统会正常启动新的 Activity。

这个标志通常用于需要返回到任务栈中某个 Activity 的场景,如注销登录后返回到主页等。但是,如果我们没有与 FLAG_ACTIVITY_CLEAR_TOP 同时使用 FLAG_ACTIVITY_SINGLE_TOP,系统仍然会重新创建目标 Activity 实例。另外,如果任务栈中没有目标 Activity,这个标志将不起作用。

1.2 FLAG_ACTIVITY_NEW_TASK

1.2.1 任务和任务栈

在 Android 中,任务(Task)和任务栈(Task Stack)是用来管理应用的 Activity 生命周期和导航的重要概念。

  1. 任务(Task):任务是一个用户与应用进行交互的会话。它是由用户从启动应用开始,到用户离开应用结束的一系列操作过程。一个任务对应于一个应用程序,但一个应用程序可以有多个任务。任务中可以包含一个或多个 Activity,这些 Activity 按照它们打开的顺序排列,形成了任务栈。
  2. 任务栈(Task Stack):任务栈是用来管理一个任务中所有 Activity 的堆栈结构。新的 Activity 被放置(push)到栈的顶部,用户看到的总是位于栈顶的 Activity。当用户按下返回键时,当前的 Activity 会从栈顶被移除(pop),并销毁,之前的 Activity 会重新显示。任务栈遵循“后进先出”(LIFO)的原则。

这两个概念对于理解 Android 的 Activity 启动模式,以及如何控制 Activity 的导航和生命周期等都非常重要。

1.2.2 FLAG_ACTIVITY_NEW_TASK 的使用和注意事项

接下来,我们来看一看 FLAG_ACTIVITY_NEW_TASK。当我们为一个 Activity 设置了这个标志,这个 Activity 会成为新任务的根,也就是新任务的第一个 Activity。如果没有设置这个标志,Activity 会被插入到当前任务栈。

这个标志通常用于从非 Activity(如 Service、BroadcastReceiver)中启动 Activity,或者需要在新的任务栈中打开 Activity 的场景。然而,使用这个标志时需要注意,如果已经存在相同的任务,那么这个标志会使得 Activity 请求被路由到已经存在的任务中,而不会创建新的任务。此外,如果没有正确理解任务和任务栈的概念,可能会导致 Activity 启动的行为与预期不符。因此,在使用 FLAG_ACTIVITY_NEW_TASK 时,我们需要确保充分理解了它的行为和可能的副作用。

1.3 代码例子

下面是两个使用这些标志的代码例子:

代码语言:javascript
复制
Intent intent = new Intent(this, TargetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

在这个例子中,我们创建了一个指向 TargetActivityIntent,并为它添加了 FLAG_ACTIVITY_CLEAR_TOP 标志。当我们启动这个 Intent 时,系统会检查当前任务栈中是否已经存在 TargetActivity 的实例。如果存在,那么这个实例之上的所有 Activity 都会被销毁,使得 TargetActivity 实例成为栈顶。

代码语言:javascript
复制
Intent intent = new Intent(this, TargetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

在这个例子中,我们创建了一个指向 TargetActivityIntent,并为它添加了 FLAG_ACTIVITY_NEW_TASK 标志。当我们启动这个 Intent 时,TargetActivity 会成为新任务的根,也就是新任务的第一个 Activity

二、深入探究:小米手机离线推送跳转问题实例分析

本节将阐述在小米手机上点击离线推送,跳转到消息页面时,无法弹出手势密码页面的问题定位过程。

不符合预期的表现描述如下:

设置了手势密码,kill掉app,收到消息离线推送弹窗,点击弹窗拉起app,没有弹出手势密码页面,而是直接进入消息页面。

2.1 问题流程分析

  1. 点击小米离线推送弹窗,会自动调起小米推送sdk的页面NotificationClickedActivity,同时弹窗也会调起消息页面(业务逻辑)。
  2. NotificationClickedActivityonResume 方法触发ActivityLifecycle弹出手势密码页面。
  3. 手势密码页面的弹出逻辑会延迟300ms执行,目的是为了先让前一个业务页面弹出,再盖上手势密码页面。
  4. 但紧接着 NotificationClickedActivity 就自动 finish 了(特定小米机型必现),手势密码页面被连带着finish
  5. 此时只剩一个消息界面。

onActivityResumed 方法中,我们调用了 upAppLock 方法。upAppLock的作用是重新把不在最顶部的手势密码页面弹出到任务栈的最上面:

代码语言:javascript
复制
@Override
public void onActivityResumed(Activity activity) {
 // ... 省略部分代码 ...
    if (ActivityLifecycleState.INSTANCE.isAndroid10Background()) {
  // 设置前台状态
        ActivityLifecycleState.INSTANCE.setAndroid10Background(false);
        // 处理进入前台任务
        handleGoForeground(activity);
 }
     
 upAppLock(activity);
 // ... 省略部分代码 ...
}

private void handleGoForeground(Activity activity) {
 // ... 省略部分代码 ...
 ThreadUtils.runOnMainThread(new Runnable() {
  @Override
        public void run() {
         // 弹出手势密码页面
            IAccount.get().onActivityStarted(activity);
        }
 }, 300);
 // ... 省略部分代码 ...
}

private void upAppLock(Activity activity) {
    // ... 省略部分代码 ...

    if (appLockActivity != null && (!appLockActivity.isFinishing() || !appLockActivity.isDestroyed())) {
        // 防止出现连续两个手势密码页面
        if (!activity.getClass().getName().equals(appLockName) &&  
         // 防止把最上面的手势密码页面 finish
         topActivity != null && !topActivity.getClass().getName().equals(appLockName)) {
         // 结束任务栈内的重复手势密码页面,这个手势密码页面当前已经被压在底下看不见
            if (appLockActivity.getTaskId() == topActivity.getTaskId()) {
                appLockActivity.finish();
            }
            // 弹出手势密码页面
            IAccount.get().onActivityStarted(activity);
        }
    }
}

2.2 解决问题过程

在这个过程中,我们将关注两个问题:FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK

2.2.1 问题一:FLAG_ACTIVITY_CLEAR_TOP

首先,我们猜测消息页面使用的 FLAG_ACTIVITY_CLEAR_TOP,会导致手势密码页面消失。所以第一个改动点是启动消息页面时,不使用FLAG_ACTIVITY_CLEAR_TOP

2.2.2 问题二:FLAG_ACTIVITY_NEW_TASK

关于 FLAG_ACTIVITY_NEW_TASK,我们可以观察以下四种情况:

  1. 消息页面有 FLAG_ACTIVITY_NEW_TASK,手势密码页面没有 FLAG_ACTIVITY_NEW_TASK:没有弹出手势密码页面,直接进入消息页面。
  2. 消息页面和手势密码页面都没有 FLAG_ACTIVITY_NEW_TASK:手势密码页面显示,但没有消息页面,Launcher 启动的是主页面。原因是消息页面也被 NotificationClickedActivity 一起 finish 了(特定机型必现)。
  3. 消息页面和手势密码页面都有 FLAG_ACTIVITY_NEW_TASK,弹出手势密码页面有300ms延迟:
  • 正常弹出手势密码页面的情况是,先弹出了手势密码页面,消息页面在 300ms 后创建,upAppLock 起作用。
  • 无法弹出手势密码页面的情况是,消息页面在 300ms 内创建,再弹出手势密码页面,upAppLock 不起作用。
  1. 消息页面和手势密码页面都有 FLAG_ACTIVITY_NEW_TASK,没有延迟弹出手势密码页面:成功弹出手势密码页面,upAppLock 起作用。

最后,我们重点分析第4种情况表现正常的原因:

  1. 因为消息页面和手势密码页面都有 FLAG_ACTIVITY_NEW_TASK,所以两个页面都和NotificationClickedActivity不在同一个任务栈内,不会被连带finish
  2. 因为手势密码页面不延迟弹出,所以页面的弹出时序变成了:先弹出手势密码页面,再弹出消息页面,此时任务栈中,手势密码页面在消息页面的下面。
  3. 消息页面的onActivityResumed触发了upAppLock,重新把手势密码页面弹出到任务栈的最上面。此时的任务栈符合产品预期逻辑。

通过以上分析,我们可以得出结论:为了正确弹出手势密码页面,我们需要注意 FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK 的使用,以及如何正确处理任务和任务栈。

三、总结

总的来说,FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK 可以用来控制 Activity 的启动模式和任务栈的行为。然而,使用它们时需要谨慎,确保理解了它们的行为和可能的副作用。在实际开发中,我们可能会遇到一些复杂的场景,如小米手机上的离线推送问题。这时,我们需要深入理解和分析问题,找出问题的根源,才能找到解决问题的方法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陆业聪 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 FLAG_ACTIVITY_CLEAR_TOP
  • 1.2 FLAG_ACTIVITY_NEW_TASK
    • 1.2.1 任务和任务栈
      • 1.2.2 FLAG_ACTIVITY_NEW_TASK 的使用和注意事项
      • 1.3 代码例子
      • 二、深入探究:小米手机离线推送跳转问题实例分析
        • 2.1 问题流程分析
          • 2.2 解决问题过程
            • 2.2.1 问题一:FLAG_ACTIVITY_CLEAR_TOP
            • 2.2.2 问题二:FLAG_ACTIVITY_NEW_TASK
        • 三、总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档