为什么没有用于获取当前活动的Android API?

时间:2017-01-09 21:04:07

标签: android android-intent android-activity android-service

问题,我如何才能获得当前活动?已经在Stackoverflow和其他网站上被问了几十次,并且有很多提议的方法。但是,所有这些都有某种形式的缺点。

在这篇文章中,我假设在Android的API中没有为此提供解决方案,例如:Application.getTask().getRootActivity()

如果有以下情况会不会很好: - )?

所以,要明确的是,我没有要求的答案如何才能获得当前的活动?

相反,我要问的是没有提供这种能力的原因。假设每个正在运行的应用程序都有一个任务(假设任务没有被清空)并且每个这样的任务都有一个root活动,那么提供对该根Activity的访问似乎很容易。

在没有提供此类访问权限的情况下,如果需要这样的话,这意味着对我来说,我不了解Android体系结构的基本内容。

我错过了什么?为什么Android API不提供此信息?

对于背景,这里是一个概述已经提出的一些方法的部分。我发现以下两个链接特别有用(下面的每个方法都在一个或两个链接中提供)。

链接

途径

  • 静态挂钩
  • 反射
  • ActivityManager
  • 其他(Instrumentation,AccessibilityService,UsageStatsManager)`

ActivityManager

ActivityManager方法仅提供Activity类的名称,而不是当前的Activity实例。例如,对于上下文实例c:

c.getSystemService().getActivityManager() .getAppTasks().get(0).getTaskInfo() .topActivity().getClassName()

反射

我最喜欢的是_AZ提出的反思,但这种方法很脆弱,因为它依赖于内部。我想从Android看到的是这种方法是通过开发人员可以安全依赖的标准API提供的。

静态挂钩

最常见的方法是使用静态挂钩来保存对当前正在运行的Activity的引用。钩子可以是按活动或按应用程序。通过保存/破坏钩子的值可以避免内存泄漏(例如,在onCreate()/ onDestroy(),onStart()/ onStop(),onPause()/ onResume())。但是,当涉及多个活动时(例如,由于生命周期重叠 - 见下文),可能会出现问题。

我实现了一个静态钩子方法,它执行以下操作(完全透明,我还没有实现#1 - 我目前正在使用per-Activity静态钩子,这是一个bug)。

  1. 提供一个扩展Application以提供钩子的类。钩子包含一个堆栈;堆栈中的每个节点都是一个简单的ActivityInfo类,它包含对Activity实例的引用以及该实例的状态(CREATED,STARTED,RESUMED)。
  2. 提供一个名为ActivityTracker的类,用于扩展Activity。然后,我使用ActivityTracker扩展我的每个活动。 ActivityTracker使用其生命周期回调来向/从堆栈推送/弹出自身并更新其状态 - 我的其他活动不必做任何事情。
  3. 理论上,这将使我能够始终了解任务的后备堆栈的完整状态 - 完整的活动集,包括根活动,以及它们的当前状态。然而,在实践中,有一个转折 - 当一个活动开始另一个活动时,它们的生命周期重叠。在此期间,在堆栈停止时偷看可能会产生意外的Activity实例。

    来自:https://developer.android.com/guide/components/activities/activity-lifecycle.html#soafa,"协调活动":

      

    这是活动A启动时发生的操作顺序   活动B:

         
        
    1. 活动A的onPause()方法执行。
    2.   
    3. 活动B的onCreate(),onStart()和onResume()方法按顺序执行。 (活动B现在具有用户关注点。)
    4.   
    5. 然后,如果活动A在屏幕上不再可见,则其onStop()方法将执行
    6.   

    当然,这也可以管理。最重要的是我们确实有一个可用于存储信息的全局上下文(应用程序),我们确实拥有有关活动生命周期转换的完整信息,因此我付出了足够的努力,我相信这种基于静态堆栈的方法可能会成为相当的子弹 - 证明。

    但最终

    但最后感觉我只是简单地重写代码,这些代码可能已经存在于内部,用于管理一个Activity堆栈,这就是为什么我要问(如果你已经忘记了):

    为什么没有用于获取当前活动的Android API?

    更新

    在本次更新中,我将总结一下我从这个主题和我自己的实验和研究中学到的东西。希望这个摘要对其他人有用。

    解释

    我将根据https://developer.android.com/guide/components/activities/activity-lifecycle.html上的活动状态定义,为"活动可见性状态"使用以下定义。

    -----------------------------------
    Visibility State   Definition
    -----------------------------------
    Not Visible        Created+Stopped
    Partially Visible  Started+Paused 
    Fully Visible      Resumed
    -----------------------------------
    

    问题

    "当前活动的定义"是黑暗的。当我使用它时,我指的是处于完全可见状态的单个活动。在任何给定的时刻,可能有也可能没有这样的活动。特别是,当活动A启动活动B时,会调用A的onPause(),然后调用onCreate(),onStart()和onResume(),然后是A onStop()。在A的onPause()和B的onResume()之间存在一段延伸,其中两者都不处于完全可见状态,因此没有当前活动(因为我定义它)。当然,在某些情况下,后台线程可能想要访问当前活动,并且根本不存在活动,更不用说当前活动了。

    我也意识到我可能并不总是需要一个Current("完全可见")活动。在许多情况下,我可能只需要对现有活动的引用,无论它是否当前可见。此外,该引用可能只适用于任何Activity(对于需要将通用Activity引用传递给某些API方法的情况),或者它可能是特定的Activity子类实例(这样我就可以触发一些特定于该Activity的代码)子类)。

    最后,需要了解主UI循环器何时调用Activity生命周期回调以及如何处理配置更改等事件。例如,如果我使用当前位于" Not Visible"中的Activity intance创建DialogFragment。它会不会被显示出来,如果是的话,什么时候?类似地,事实证明由配置更改引起的onDestroy()和onCreate()方法包含在UI的消息队列中的相同消息中(请参阅Android UI Thread Message Queue dispatch order),因此没有其他消息将在这两个回调之间处理(在配置更改期间)。理解这种处理水平似乎是至关重要的,但如果不完全遗漏,那么它的文档就非常缺乏。

    途径

    以下是一系列可用于解决上述大多数情况的方法。

    背景

    • 讨论时,假设活动A和活动B,其中A创建B。
    • 一般而言,"全球"变量可以通过制作来创建 " public static"几乎任何课程。从概念上讲,扩展 Application类并将其添加到扩展类中 好的,但是如果那太多的工作可以包括在里面 实例)在其中一个Activity类中。

    通用活动参考

    • 每当需要通用活动时都有用。
    • 创建一个全局变量。在A和B中,让onCreate()将其设置为"这个"和onDestroy()将其设置为null。

    最顶层的活动参考

    • 每当您想要访问当前可见的活动时都很有用。
    • 创建一个全局变量。在A和B中,让onResume()将其设置为"这个"。除非所有活动都退出,否则此方法可以正常工作,在这种情况下,您可能需要创建一个单独的标志来指示该情况。 (该标志可以是上面提到的通用活动参考实现。)

    特定活动参考

    • 每当需要特定Activity子类实例的句柄时都有用。
    • 在A和B中:在Activity子类本身中创建一个全局变量。让onCreate()将其设置为" this和onDestroy()将其设置为null。

    应用程序上下文

    • 只要需要跨越整个应用程序生命周期的上下文,或者您不关心使用特定的活动上下文(例如,从后台线程创建Toast),就会很有用。
    • 您可以从Activity的getApplication()获取此内容并将其存储在静态挂钩上。

    处理配置更改

    有时候你想要在一个Activity" session"中停止/启动一个后台线程,在那里我定义" session"包括可能由于配置更改而创建和销毁的一系列Activity实例。在我的特定情况下,我有一个蓝牙聊天活动和一个相关的后台线程来处理网络连接。我不希望每次用户旋转设备时都会破坏和创建连接,因此我只有在不存在连接时才需要创建它,并且只有在配置更改不是时才销毁它进行。这里的关键是理解当由于配置更改而调用onDestroy()时。这可以使用或不使用片段来完成。通常情况下,我更喜欢非片段方法,因为片段方法对我来说似乎不值得额外的复杂性。

    方法1:没有碎片

    在onCreate()中,如果尚未存在,则创建后台线程。在onDestroy()中,仅当isFinally()返回false时才销毁后台线程。

    方法2:使用碎片

    这很有效,因为如果使用setRetainInstance(true),FragmentManager将在配置更改中存储片段实例。有关此示例,请参阅http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html。示例适用于AsyncTasks,但也可以应用于管理后台线程(只需在片段的onCreate()中创建线程而不是AsyncTask,然后销毁片段中的线程onDestroy() )。

    完全理解这些问题需要深入了解UI looper如何处理其消息队列 - 调用Activity回调时,其他消息如何与它们交错,何时发生显示更新等。例如,如果DialogFragment是使用不可见活动的实例创建,它是否会显示,如果是,何时? 也许有一天Android会为Tasks及其相关的backstack提供更深入的API,以及更详细地描述UI的消息处理和相关机制的文档。在此之前,更多的源代码和/或......经验分析" :-)。

    谢谢,

    百里

2 个答案:

答案 0 :(得分:0)

我猜测的简短答案是,在给定的应用程序中,一次只能激活一个活动,并且该活动显然知道它是谁(它本身) - 所以任何活动都可以获得的唯一答案"当前活动的活动"永远只会是#34;你是,愚蠢"。

对于在不同活动类之间进行明确划分的简单应用程序,这样可以正常工作,因此在游戏商店中大部分应用程序占很大比例。当你通过封装和多态性获得真正的聪明时,它并没有那么热,因为我确定你已经发现了,但我不认为谷歌真正瞄准那些开发人员类型。

只要我0.02美元,我就不会认为你会得到一个"官员"回答这里。

答案 1 :(得分:0)

如果你想要知道的是哪个Activity最重要并接受用户交互,只需创建一个扩展Activity并覆盖onResume()的BaseActivity,并保存对&#34的引用;这个"在一个静态变量中。您的所有其他活动都应扩展BaseActivity。你已经完成了。