如何在安卓中获取当前前台活动的上下文?

215

每当我的广播被执行时,我希望向前台活动显示警报。


你想获取Activity的上下文是从哪里获取?这将是你的应用程序活动还是其他应用程序。 - AAnkit
1
在onreceive中,您只能将Context作为参数获取,可以使用context.getApplicationContext()来获取上下文。 - AAnkit
可能是重复的问题:Android:如何从服务中获取当前前台活动? - Heath Borders
@AAnkit:不,要显示任何UI,这里是一个警告对话框,需要一个上下文,即活动;getApplicationContext不会返回一个活动,因此在此用途中无法工作。 - ToolmakerSteve
所有的答案似乎随着时间的推移都是建立在彼此之上的。有没有人能够在2016年为今天写出一个清晰的解决方案? - Nathan H
显示剩余4条评论
18个回答

233
注意:API14中添加了官方API:请参见此答案https://dev59.com/nGgu5IYBdhLWcg3wUljt#29786451

不要使用之前的回答(waqas716)。

您可能会遇到内存泄漏问题,这是由于对活动的静态引用引起的。有关更多详细信息,请参见以下链接http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html

为了避免这种情况,您应该管理活动引用。 将应用程序名称添加到清单文件中:

<application
    android:name=".MyApp"
    ....
 </application>

你的应用程序类:

  public class MyApp extends Application {
        public void onCreate() {
              super.onCreate();
        }

        private Activity mCurrentActivity = null;
        public Activity getCurrentActivity(){
              return mCurrentActivity;
        }
        public void setCurrentActivity(Activity mCurrentActivity){
              this.mCurrentActivity = mCurrentActivity;
        }
  }

创建新的活动:

public class MyBaseActivity extends Activity {
    protected MyApp mMyApp;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMyApp = (MyApp)this.getApplicationContext();
    }
    protected void onResume() {
        super.onResume();
        mMyApp.setCurrentActivity(this);
    }
    protected void onPause() {
        clearReferences();
        super.onPause();
    }
    protected void onDestroy() {        
        clearReferences();
        super.onDestroy();
    }

    private void clearReferences(){
        Activity currActivity = mMyApp.getCurrentActivity();
        if (this.equals(currActivity))
            mMyApp.setCurrentActivity(null);
    }
}

现在,您不需要再扩展Activity类来创建您的活动,只需要扩展MyBaseActivity即可。现在,您可以像这样从应用程序或Activity上下文中获取当前活动:

Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();

15
你可以使用WeakReference,用更少的代码实现相同的结果。 - Nacho Coloma
5
@Nacho,我绝不会建议在Android中使用“WeakReferences”,因为垃圾回收器会比你想象的更快地将它们回收掉。 - rekire
4
如果您在onCreate()中调用finish(),并且您只是使用Activity来启动另一个Activity并停止此Activity,则可以实现这一点。在这种情况下,它会跳过onPause()和onStop()。请参见以下链接底部的注释:http://developer.android.com/training/basics/activity-lifecycle/starting.html。 - Rodrigo Leitão
3
不建议使用WeakReference进行缓存,这不是缓存,因为只有当mCurrentActivity还活着时,它才会有一个对它的引用,所以只要Activity在顶部,WeakReference就不会被垃圾回收。但是,@NachoColoma提出的方法是错误的,因为如果变量没有被清除,WeakReference仍然可能引用一个非恢复(不活动/不在顶部)的Activity! - TWiStErRob
14
从Android API 14开始,应该可以使用Application.ActivityLifecycleCallbacks,这将更加集中化,您不需要在所有活动中添加任何管理代码。另请参见http://developer.android.com/reference/android/app/Application.html#registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks)。 - Filou
显示剩余17条评论

80

我在@gezdy的答案基础上进行了拓展。

在每个活动中,我们可以利用以下API(从14级开始)来帮助我们实现类似的目的,而无需手动编码将其自身“注册”到Application中。

public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)

http://developer.android.com/reference/android/app/Application.html#registerActivityLifecycleCallbacks%28android.app.Application.ActivityLifecycleCallbacks%29

Application.ActivityLifecycleCallbacks 中,你可以获取哪个 Activity 被 "附加" 或 "解除附加" 到这个 Application 上。

然而,此技术仅适用于 API 级别 14 及以上。


1
其他的回答怎么了?很明显这个API是为了这个目的而设计的。谢谢,Cheok Yan Cheng - Michael Bushe
2
@MichaelBushe - 在2012年,当其他答案被写下时,依赖API级别14并不是每个设备都可以依靠的东西,因为该API仅在最近发布(2011年10月)。 - ToolmakerSteve
4
找到了一个答案,展示了如何使用这种方法:https://dev59.com/Kmkv5IYBdhLWcg3wbgQx#11082332。好处是**无需对活动本身做任何事情**;所有代码都在您的自定义回调类中。只需创建一个实现了 Application.ActivityLifecycleCallbacks 接口的类,添加实现该接口所需的方法即可。然后在那个类的构造函数(或 onCreate 或 init 或其他在实例变为活动/准备就绪状态时运行的方法)中,将 getApplication().registerActivityLifecycleCallbacks(this); 作为最后一行放入即可。 - ToolmakerSteve
我认为你的答案是最好的。 - burulangtu
3
很棒的答案。唯一的缺点是,如果您需要查询当前活动的类,则仍然需要将活动保存在某个地方。因此,您仍然需要避免内存泄漏并将引用设置为null。 - Raphael C
这是一个稳健的、简单的解决方案。 - Nestor Ledon

71

3
这个回答应该得到更多的点赞,它提供了简单的解决方案,但当你需要操作活动而不是活动本身时,它会变得非常强大。 - shecodesthings
这与您之前的答案等价。Application仅创建一次,永远不会像静态变量一样被垃圾回收。 - zapl
对不起,我匆忙之中漏掉了清晰的参考部分。我会再次更新我的回答。感谢您指出这个问题 :)。 - Waqas
1
层次化活动会出现问题。当您从子活动返回到父活动时:(1)调用子的onPause;(2)调用父的onResume;(3)调用子的onDestroy ==> 当前活动将为null。您应该像@gezdy在clearReferences方法中的示例中那样进行一些检查。 - Arts
4
建议简化 clearReferences() 中的条件为 (this.equals(currActivity)) - naXa stands with Ukraine
显示剩余6条评论

46

了解到 ActivityManager 管理着 Activity,因此我们可以从 ActivityManager 获取信息。我们通过以下方式获取当前正在运行的前台 Activity:

ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;

更新于2018/10/03:
getRunningTasks() 方法已被弃用,请参考以下解决方案。

该方法在API 21级别中已被弃用。从Build.VERSION_CODES.LOLLIPOP版本开始,第三方应用程序将无法使用此方法:由于文档为中心的最近文档的引入,它可能会向调用者泄露个人信息。为了向后兼容,它仍将返回其数据的一小部分:至少是调用者自己的任务,以及可能是一些其他任务(例如Home等),它们被认为不是敏感信息。


17
马丁,我不这么认为。根据SDK中getRunningTasks的帮助文档:“注意:此方法仅用于调试和呈现任务管理用户界面。在应用程序的核心逻辑中永远不应使用此方法”。 - ruhalde
3
显然,这仅支持在Android 5 / 棒棒糖中运行的任务的有限子集。 - Sam
7
ActivityManager.getRunningTasks()的文档称:“该方法在API级别21中已被弃用”。 - markshep

23

我使用 Kotlin 进行了以下操作

  1. 创建应用程序类
  2. 按照以下方式编辑应用程序类:

    class FTApplication: MultiDexApplication() {
    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        MultiDex.install(this)
    }
    
    init {
        instance = this
    }
    
    val mFTActivityLifecycleCallbacks = FTActivityLifecycleCallbacks()
    
    override fun onCreate() {
        super.onCreate()
    
        registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
    }
    
    companion object {
        private var instance: FTApplication? = null
    
        fun currentActivity(): Activity? {
    
            return instance!!.mFTActivityLifecycleCallbacks.currentActivity
        }
    }
    
     }
    
    创建ActivityLifecycleCallbacks类。
    class FTActivityLifecycleCallbacks: Application.ActivityLifecycleCallbacks {
    
    var currentActivity: Activity? = null
    
    override fun onActivityPaused(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityResumed(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityStarted(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityDestroyed(activity: Activity?) {
    }
    
    override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
    }
    
    override fun onActivityStopped(activity: Activity?) {
    }
    
    override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
        currentActivity = activity
    }
    
    }
    
  3. 现在您可以通过调用以下代码在任何类中使用它:FTApplication.currentActivity()


3
这是我找到的唯一干净的解决方案,顺便说一句,你只需要在onActivityResumed中运行,其它操作都是无用的,因为恢复操作是在创建Activity之后启动的。 为了确保,如果currentActivity == activity,则在onActivityStopped中将currentActivity设置为null。 - Plokko
它运行良好,谢谢。 FYI:要激活回调,您必须在AndroidManifest中添加: <application android:name="your-package-code.FTApplication" - Caesar

6

getCurrentActivity() 也在 ReactContextBaseJavaModule 中。
(自从最初提出这个问题以来,许多 Android 应用程序也具有 ReactNative 组件 - 混合应用程序。)

ReactNative 中的 ReactContext 类拥有维护 mCurrentActivity 的整套逻辑,该逻辑在 getCurrentActivity() 中返回。

注意:我希望 getCurrentActivity() 在 Android 应用程序类中实现。


在某些情况下,ReactContextBaseJavaModule 中的上下文为 null,你知道为什么吗? - Moxor
ReactContext.getCurrentActivity() 不够可靠。例如,参见这个 bug - d4vidi

6

为了向后兼容:

ComponentName cn;
ActivityManager am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    cn = am.getAppTasks().get(0).getTaskInfo().topActivity;
} else {
    //noinspection deprecation
    cn = am.getRunningTasks(1).get(0).topActivity;
}

5
在我看来,除非有一种方法可以从ComponentName获取到当前Activity实例,否则这个答案并没有回答问题。 - nasch
@nasch 可以从 Application 类中保留并获取 WeakReference 句柄,而 ComponentName 则需要确定所需的 Activity 是否在正在运行的任务列表的顶部。如果这不能完全回答问题,那么被接受的答案也不是。 - Martin Zeitler
我同意,被接受的答案也没有完全回答这个问题。 - nasch
2
topActivity 仅在 Android Q 及以上版本可用。 - Eugen Martynov

4

我们没有找到一种让团队满意的解决方案,所以我们自己开发了一个。我们使用 ActivityLifecycleCallbacks 来跟踪当前活动,并通过服务公开它。更多详细信息请参见此处:https://dev59.com/tm865IYBdhLWcg3wUs7z#38650587


4
到目前为止最好的解决方案是在您的应用程序(Java)中创建名为ActivityManager的类。
public class ActivityManager implements Application.ActivityLifecycleCallbacks {

    private Activity activity;


    public ActivityManager(App myApplication) {
        myApplication.registerActivityLifecycleCallbacks(this);
    }

    public Activity getActivity(){
        return activity;
    }
    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
        this. activity = activity;

    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {
       this. activity = activity;
    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {
        this. activity = activity;

    }

    @Override
    public void onActivityPaused(@NonNull Activity activity) {

    }

    @Override
    public void onActivityStopped(@NonNull Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {

    }
}

然后在你的应用程序中初始化它(kotlin)

class App : Application() {

    override fun onCreate() {
     
        appOpenManager =  AppOpenManager(this);
    }
  companion object {
        lateinit var appOpenManager: AppOpenManager
    }
}

然后像这样使用:
App.activityManager.getActivity()

2
通过使用这段代码,您可以检测应用程序何时进入后台/前台,并访问当前活动名称和上下文。
我的答案基于这篇文章:Android: How to detect when App goes background/foreground 首先,创建一个扩展android.app.Application并实现ActivityLifecycleCallbacks接口的类。在Application.onCreate()中,注册回调函数。
public class App extends Application implements ActivityLifecycleCallbacks
@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
}

在清单文件中注册“App”类,如下所示:
<application
    android:name=".App"

这是ActivityLifecycleCallbacks接口的外观,
public interface ActivityLifecycleCallbacks {
    void onActivityCreated(Activity activity, Bundle savedInstanceState);
    void onActivityStarted(Activity activity);
    void onActivityResumed(Activity activity);
    void onActivityPaused(Activity activity);
    void onActivityStopped(Activity activity);
    void onActivitySaveInstanceState(Activity activity, Bundle outState);
    void onActivityDestroyed(Activity activity);
}

因此,当您的任何活动(您创建或通过库包含的活动)经历上述任何一个生命周期方法时,这些回调将被调用。 当应用程序在前台时,至少会有一个处于启动状态的活动,并且当应用程序在后台时,不会有任何活动处于启动状态。 在“App”类中声明以下2个变量。

private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;

activityReferences会记录处于started状态的Activity数量。isActivityChangingConfigurations是一个标志,用于指示当前Activity是否正在进行配置更改,例如方向切换。 使用以下代码,您可以检测应用程序是否进入前台。

@Override
public void onActivityStarted(Activity activity) {
    if (++activityReferences == 1 && !isActivityChangingConfigurations) {
        // App enters foreground
    }
}

您可以在此方法中像这样访问上下文:
activity.getBaseContext()

这是如何检测应用程序是否进入后台的方法。

Override
public void onActivityStopped(Activity activity) {

    isActivityChangingConfigurations = activity.isChangingConfigurations();
    if (--activityReferences == 0 && !isActivityChangingConfigurations) {
        // App enters background
    }
}

现在您可以访问当前前台活动的名称和上下文。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接