【问题标题】:Managing android activity stacks: How can a specific instance of an activity bring itself to the front?管理 android 活动堆栈:活动的特定实例如何将自己带到最前面?
【发布时间】:2011-11-04 16:10:30
【问题描述】:

我有一组要在其中导航的活动,并且由于这些活动的初始化成本很高,因此我希望尽可能保留现有状态。问题是我可能不止一次打开相同的活动,每个活动的状态都不同,而且我没有看到标准 Android 标志可以帮助我解决这种情况。

这是一个视觉示例:

假设我有三项活动:

A、B、C

这些活动中的每一个都可以打开多次,每个活动都具有不同的状态。不仅这些活动的初始化成本很高,而且我还想保持用户的当前状态,例如滚动位置、所选项目等......

假设 A(1) 是状态 1 中活动 A 的实例,而 A(2) 是状态 2 中活动 A 的实例。A(1) 和 B(1) 不相关。

我想实现一个循环导航栈如下:

... --> A(1) --> A(2) --> B(1) --> B(2) --> B(3) --> A(3) -- > C(1) --> A(1) --> ...

由于活动很昂贵,我真的很想重用现有的实例。但是,如您在上面看到的,我可能需要保留大约 2 或 3 个相同活动的实例。

谢谢!

【问题讨论】:

  • 到目前为止您尝试了哪些操作,是否有任何不起作用的代码?
  • 目前我每次只创建一个新活动,并使用广播接收器来确保重复的活动/状态自行关闭。

标签: android user-interface android-activity navigation stack


【解决方案1】:

好的,这实际上是可能的,但您需要使用反射和 Intent.FLAG_ACTIVITY_MULTIPLE_TASK 标志。

我是这样做的:

控制器:以 Intent.FLAG_ACTIVITY_NEW_TASK | 开始活动Intent.FLAG_ACTIVITY_MULTIPLE_TASK

Activity:向控制器注册自己,通过传入一个唯一的 id 来识别它们的状态,并使用 getTaskId() 来识别它们的任务 id

Controller:如果任务已经终止,则创建一个活动(它使用 ActivityManager.getRunningTasks() 检查)。如果任务还活着,那么它使用以下反射代码将任务带到前面:

Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");

Method getDefault = ActivityManagerNative.getMethod("getDefault", null);
Object activityManagerNative = getDefault.invoke(ActivityManagerNative, null);

Method[] methods = activityManagerNative.getClass().getMethods();

Method moveTaskToFront;

for (final Method method : methods) { if (method.getName().equals("moveTaskToFront")) { moveTaskToFront = method;}}

...

moveTaskToFront.invoke(activityManagerNative, registeredTaskId);

可以通过从请求更改任务的 Activity 调用 overridePendingTransition 来控制动画。

希望这可以帮助其他人。

【讨论】:

    【解决方案2】:

    moveTaskToFront.invoke 仅在 API 11 之后可用。如何在 API 8 级应用程序中实现相同的功能。

    【讨论】:

      【解决方案3】:

      在 AndroidManifest.xml 中,您可以为应用程序的每个活动定义运行时系统应如何启动特定活动。在您的情况下,您应该使用值“singleTask”定义活动的 launchMode 属性。然后运行时系统不会创建特定活动的多个实例。每次应该启动特定活动时,运行时系统都会调用 onNewIntent() 活动。在这种方法中,您可以处理一个特定活动的不同状态。

      【讨论】:

      • 这意味着活动必须重新加载状态。这涉及大量昂贵的数据库查询和网络查询以及重置 UI 状态,这就是为什么我希望只切换到已经具有正确状态的特定实例。
      • 您在另一个答案中提到您希望使用单例类保留对数据、视图和适配器的引用。带有 launchMode singleTask 的 Activity 的作用完全相同。它就像一个单例。在 onCreate 中尝试获取您需要的所有数据(数据库、网络、膨胀视图、配置适配器)。在 onNewIntent 中,您可以访问此引用。您可以将数据分配给已经膨胀的视图或执行其他与 ui 相关的逻辑。
      • 你看过这个问题吗...和你的类似...stackoverflow.com/questions/6905774/…
      • 感谢各位更新。似乎我必须编写包装器代码来保存和恢复状态,比如列表适配器的内容、当前滚动位置以及检查的内容/等等......这似乎比简单地调用更多的工程工作像显示(A(1))。缺点也是因为我们正试图在多个应用程序和团队之间进行协调。也许真正的问题是我们的活动很繁重,所以如果我们提高启动速度,那么保留检查状态之类的东西可能会更容易。
      【解决方案4】:

      可行的方法是:不允许超过 1 个特定活动的实例。当您通过设置launchMode 在清单中声明每个活动时,您会执行此操作。然后,通过创建一个包含每个活动 Bundle 对象的应用程序范围的对象来跟踪实例状态。打开 Activity 时,将其传递给适当的 Bundle。

      【讨论】:

      • 嗯...也许我可以使用包含适配器列表和所有其他类型数据的应用程序范围的单例?我希望只重用同一个实例,这样我就不必重新应用所有状态数据。应用程序范围的对象可能比从数据库重新加载所有内容要快,但我不知道要快多少。
      • 按照我的建议进行操作的好处是,每个活动的 onCreate 中的繁重工作只需执行一次。为每个活动应用实例状态肯定比从数据库中加载所有内容更快、更有效。
      • 我真的希望我可以做一个 showActivity(A(1)) 回到我以前的位置。 ;) 这也可以,这只是编写代码以保存滚动状态、适配器内容等的工程缺点……然后稍后重新应用,这让我特别担心,因为它涉及多个团队和应用程序。谢谢你让我知道。
      猜你喜欢
      • 2017-10-02
      • 1970-01-01
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      相关资源
      最近更新 更多