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


171

每当执行广播时,我都希望对前景活动显示警报。


从您要获取活动上下文的位置。这是您的应用活动还是其他应用。
AAnkit

这是一个应用程序活动。我已经在broadcastreceiver onreceive()函数上完成了警报对话框编码。
Deepali

应用程式活动!这是您的应用吗?以及为什么您出于任何原因想要这样做
AAnkit

我想对前景活动显示警报,这是他们在没有上下文的情况下对前景活动显示警报的任何其他方式。
Deepali

1
在onreceive中,只有您将COntext作为参数,您可以说context.getApplicationContext()
AAnkit 2012年

Answers:


38

知道ActivityManager可以管理Activity,因此我们可以从ActivityManager获取信息。我们通过以下方式获得当前正在运行的前景

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

UPDATE 2018/10/03
getRunningTasks()已弃用。请参阅下面的解决方案。

从API级别21开始不推荐使用此方法。从Build.VERSION_CODES.LOLLIPOP开始,此方法不再对第三方应用程序可用:以文档为中心的最新消息的引入意味着它可以将个人信息泄露给调用者。为了向后兼容,它仍将返回其数据的一小部分:至少调用者自己的任务,以及可能已知的一些其他不敏感的任务,例如home。


16
别这么认为,马丁,来自getRunningTasks的SDK帮助:“注意:此方法仅用于调试和呈现任务管理用户界面。永远不要将其用于应用程序中的核心逻辑”
ruhalde 2013年

3
显然,这仅支持Android 5 / Lollipop中运行任务的有限子集。
2015年

7
ActivityManager.getRunningTasks()的文档说:“此方法在API级别21中已弃用”。
markhep

210

注意: API 14中添加了一个官方API:请参阅此答案 https://stackoverflow.com/a/29786451/119733

不要使用以前的(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 currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();

9
您可以只使用WeakReference并用更少的代码获得相同的结果。
纳乔·科洛玛

5
@Nacho我永远不会建议WeakReferences在Android中使用GC来收集它们的速度比您想象的要快。

4
@MaximKorobov是的,如果您使用活动仅启动另一个活动并停止该活动,则可以从onCreate()调用finish()。在这种情况下,它将跳过onPause()和onStoo()。见底部的注意事项:developer.android.com/training/basics/activity-lifecycle/...
罗德里戈·雷涛

2
@rekire @NachoColoma WeakReference不建议将Cache 用于缓存,这不是缓存,也就是说,mCurrentActivity仅当它处于活动状态时,才会对其进行引用,因此,WeakReference永远不会在Activity处于顶部时收集它们。但是@NachoColoma的建议是错误的,因为WeakReference如果未清除该变量,它仍可能引用未恢复的活动(不在活动状态/不在顶部)!
TWiStErRob 2014年

14
从Android API级别14开始,应该可以使用Application .ActivityLifecycleCallbacks,它将更加集中,并且您不必在所有活动中添加任何管理代码。另请参阅developer.android.com/reference/android/app/...
享用Filou

68

我在@gezdy的答案上方展开。

在每个活动中,我们无需Application使用手动编码“注册”自己,而可以使用自第14级以来的以下API,以帮助我们以较少的手动编码实现类似的目的。

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月)。
制造商史蒂夫(Steve)2015年

4
找到了一个显示如何使用此方法的答案:stackoverflow.com/a/11082332/199364 好处是无需对活动本身进行任何操作;该代码全部在您的自定义回调类中。您只需创建一个class implements Application.ActivityLifecycleCallbacks,然后添加实现它的方法。然后在该类的构造函数中(或onCreate或init或在实例变为活动/就绪状态时运行的其他方法)中,将其getApplication().registerActivityLifecycleCallbacks(this);作为最后一行。
制造商史蒂夫(Steve)

我认为您的答案是最好的
-burulangtu

2
好答案。唯一的缺点是,如果您需要在班级中查询当前活动,则仍然需要将活动保存一些位置。因此,您仍然需要避免内存泄漏并使引用为空。
拉斐尔C

56

更新2:为此添加了一个官方api,请改用ActivityLifecycleCallbacks

更新:

正如@gezdy所指出的,我对此表示感谢。也将引用设置为null当前活动的,而不是仅在每个onResume上进行更新,而是在每个Activity的onDestroy上将其设置为null以避免内存泄漏问题。

前一阵子我需要相同的功能,这是我实现这一目标的方法。在您的每项活动中都应覆盖这些生命周期方法。

@Override
protected void onResume() {
    super.onResume();
    appConstantsObj.setCurrentActivity(this);

}

@Override
protected void onPause() {
   clearReferences();
   super.onPause();
}

@Override
protected void onDestroy() {        
   clearReferences();
   super.onDestroy();
}

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

现在,在您的广播课程中,您可以访问当前活动以在其上显示警报。


3
这个答案实际上应该获得更多的投票权,简单的解决方案,但是当您有需要操纵活动的类而不是活动本身时,此答案将很有效。
ryvianstyron

它只是活动对象的静态引用。您可以在任意位置创建它:)。没关系
Waqas 2012年

顺便说一句,它等同于您先前的答案。Application仅创建一次,并且永远不会像静态变量一样完全进行垃圾回收。
zapl

1
分层活动会出现问题。当您从子项活动返回到父项活动时:(1)称为子项的onPause;(2)父母的onResume;(3)孩子的onDestroy ==>当前活动将为null。您应该在他的示例中的clearReferences方法中执行@gezdy之类的检查。
艺术

4
@ waqas716我建议简化的条件clearReferences()(this.equals(currActivity))
2014年

51

@lockwobr感谢您的更新

在api版本16中,这不能100%地起作用,如果您在github上阅读代码,则Kitkat中的函数“ currentActivityThread”已更改,因此我想说的是19ish版本,很难将api版本与github中的发行版进行匹配。

获得最新消息Activity非常方便。有一个静态的不是很好吗getActivity方法返回当前Activity而没有不必要的问题,吗?

Activity班是非常有用的。它提供对应用程序的UI线程,视图,资源等的访问。许多方法都需要一个Context,但是如何获取指针呢?以下是一些方法:

  • 使用覆盖的生命周期方法跟踪应用程序的状态。您必须将当前的活动存储在静态变量中,并且需要访问所有活动的代码。
  • 使用Instrumentation跟踪应用程序的状态。在清单中声明Instrumentation,实施它并使用其方法来跟踪Activity的变化。将Activity指针传递给Activity中使用的方法和类。使用代码注入库之一注入指针。所有这些方法都很不方便 ; 幸运的是,有一种更容易的方法来获取当前的活动。
  • 似乎系统需要访问所有活动,而没有上述问题。因此,很可能有一种方法可以仅使用静态调用来获取活动。我花了很多时间在grepcode.com上浏览Android来源,然后找到了想要的东西。有一个叫的课程ActivityThread。此类可以访问所有Activity,更妙的是,它具有用于获取current的静态方法ActivityThread。仅有一个小问题–活动列表具有程序包访问权限。

使用反射很容易解决:

public static Activity getActivity() {
    Class activityThreadClass = Class.forName("android.app.ActivityThread");
    Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
    Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
    activitiesField.setAccessible(true);

    Map<Object, Object> activities = (Map<Object, Object>) activitiesField.get(activityThread);
    if (activities == null)
        return null;

    for (Object activityRecord : activities.values()) {
        Class activityRecordClass = activityRecord.getClass();
        Field pausedField = activityRecordClass.getDeclaredField("paused");
        pausedField.setAccessible(true);
        if (!pausedField.getBoolean(activityRecord)) {
            Field activityField = activityRecordClass.getDeclaredField("activity");
            activityField.setAccessible(true);
            Activity activity = (Activity) activityField.get(activityRecord);
            return activity;
        }
    }

    return null;
}

这种方法可以在应用程序中的任何位置使用,并且比所有提到的方法都方便得多。而且,看起来它并不像看上去那样不安全。它不会引入任何新的潜在泄漏或空指针。

上面的代码片段缺少异常处理,并且天真地假定第一个正在运行的Activity是我们要寻找的Activity。您可能需要添加一些其他检查。

博客文章


2
在Kitkat及更高版本中,mActivities不是HashMap,而是ArrayMap,因此您需要更改此行:HashMap活动=(HashMap)ActivitiesField.get(activityThread); 看起来像这样:ArrayMap Activities =(ArrayMap)ActivitiesField.get(activityThread);
2015年

7
@Palejandro支持两种api级别(高于18和更低),它应该使用Mapinterface HashMapArrayMap。我已经编辑了@AZ_答案。
Yuriy Kolbasinskiy 2015年

2
在api 版本16中,这不能100%地起作用,如果您在github上阅读代码,则Kitkat中的函数“ currentActivityThread”已更改,因此我想说19ish版本,很难将api版本与github中的发行版进行匹配。
lockwobr

@lockwobr谢谢,您的解决方案已更新评论
:)

2
不支持通过反射访问内部API,并且可能无法在所有设备上或将来使用。

9

我在科特林做了以下活动

  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
        }
    }
    
     }
  3. 创建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
    }
    
    }
  4. 您现在可以通过调用以下命令在任何类中使用它: FTApplication.currentActivity()


5

getCurrentActivity()也位于ReactContextBaseJavaModule中。
(由于最初提出了这个问题,因此许多Android应用也具有ReactNative组件-混合应用。)

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

注意:我希望getCurrentActivity()在Android Application类中实现。


在某些情况下,来自ReactContextBaseJavaModule的上下文为null,您知道为什么吗?
Moxor

4

我找不到能使我们的团队满意的解决方案,所以我们自己动手了。我们ActivityLifecycleCallbacks用来跟踪当前活动,然后通过服务公开它。此处有更多详细信息:https : //stackoverflow.com/a/38650587/10793


2

为了向后兼容:

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;
}

4
除非有办法从ComponentName到Activity的当前实例,否则无法回答IMO问题。
nasch

@nasch可以保留并WeakReferenceApplication类中获取一个句柄-同时ComponentName需要确定该类是否Activity在运行任务列表的顶部。如果这不能完全回答问题,那么接受的答案也不会。
马丁·泽特勒

我同意,接受的答案也不能完全回答问题。
nasch

1
topActivity仅可从Android Q
Eugen Martynov

1

我个人按照“ Cheok Yan Cheng”的说法进行操作,但是我使用“ List”对所有活动进行“ Backstack”操作。

如果要检查哪个是当前活动,则只需要获取列表中的最后一个活动类。

创建一个扩展“应用程序”的应用程序,然后执行以下操作:

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks,
EndSyncReceiver.IEndSyncCallback {

private List<Class> mActivitiesBackStack;
private EndSyncReceiver mReceiver;
    private Merlin mMerlin;
    private boolean isMerlinBound;
    private boolean isReceiverRegistered;

@Override
    public void onCreate() {
        super.onCreate();
        [....]
RealmHelper.initInstance();
        initMyMerlin();
        bindMerlin();
        initEndSyncReceiver();
        mActivitiesBackStack = new ArrayList<>();
    }

/* START Override ActivityLifecycleCallbacks Methods */
    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
        mActivitiesBackStack.add(activity.getClass());
    }

    @Override
    public void onActivityStarted(Activity activity) {
        if(!isMerlinBound){
            bindMerlin();
        }
        if(!isReceiverRegistered){
            registerEndSyncReceiver();
        }
    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {
        if(!AppUtils.isAppOnForeground(this)){
            if(isMerlinBound) {
                unbindMerlin();
            }
            if(isReceiverRegistered){
                unregisterReceiver(mReceiver);
            }
            if(RealmHelper.getInstance() != null){
                RealmHelper.getInstance().close();
                RealmHelper.getInstance().logRealmInstanceCount("AppInBackground");
                RealmHelper.setMyInstance(null);
            }
        }
    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        if(mActivitiesBackStack.contains(activity.getClass())){
            mActivitiesBackStack.remove(activity.getClass());
        }
    }
    /* END Override ActivityLifecycleCallbacks Methods */

/* START Override IEndSyncCallback Methods */
    @Override
    public void onEndSync(Intent intent) {
        Constants.SyncType syncType = null;
        if(intent.hasExtra(Constants.INTENT_DATA_SYNC_TYPE)){
            syncType = (Constants.SyncType) intent.getSerializableExtra(Constants.INTENT_DATA_SYNC_TYPE);
        }
        if(syncType != null){
            checkSyncType(syncType);
        }
    }
    /* END IEndSyncCallback Methods */

private void checkSyncType(Constants.SyncType){
    [...]
    if( mActivitiesBackStack.contains(ActivityClass.class) ){
         doOperation()     }
}

}

就我而言,我使用“ Application.ActivityLifecycleCallbacks”来:

  • 绑定/解除绑定Merlin实例(用于在应用程序丢失或获得连接时(例如,当您关闭移动数据或打开移动数据时)获得事件)。禁用“ OnConnectivityChanged”意图操作后,此功能很有用。有关MERLIN的更多信息,请参见:MERLIN INFO LINK

  • 当应用程序关闭时,关闭我的最后一个Realm Instance;我将在所有其他活动的BaseActivity范围内对其进行初始化,该活动具有私有RealmHelper实例。有关REALM的更多信息,请参见:REALM INFO LINK 例如,我在“ RealmHelper”类中有一个静态“ RealmHelper”实例,该实例在我的应用程序“ onCreate”中实例化。我有一个同步服务,其中创建了新的“ RealmHelper”,因为Realm是“线程链接的”,并且Realm实例不能在其他线程中工作。因此,为了遵循Realm文档“您需要关闭所有打开的Realm实例以避免系统资源泄漏”,为完成此任务,我使用了“ Application.ActivityLifecycleCallbacks”,您可以看到。

  • 最后,当我完成对应用程序的同步时,我将触发一个接收器,然后在同步结束时它将调用“ IEndSyncCallback”“ onEndSync”方法,在该方法中,我查看是否在ActivityBackStack列表中有特定的Activity类,因为我需要如果同步更新了视图中的数据,则更新视图中的数据,应用同步后,我可能需要执行其他操作。

就这样,希望对您有所帮助。再见 :)


-1

waqas716的答案很好。我为需要较少代码和维护的特定案例创建了解决方法。

通过使用静态方法从我怀疑在前台的活动中获取视图,我找到了一种解决方法。您可以遍历所有活动并检查您是否愿意或从马丁的答案中获得活动名称

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

然后,我检查视图是否不为null并通过getContext()获取上下文。

View v = SuspectedActivity.get_view();

if(v != null)
{
    // an example for using this context for something not 
    // permissible in global application context. 
    v.getContext().startActivity(new Intent("rubberduck.com.activities.SomeOtherActivity"));
}

我在这里寻找类似的问题 stackoverflow.com/questions/22788289/… 我们如何获得“ SuspectedActivity”?这是本机API吗?
斯特拉2014年

2
但是从文档为getRunningTasks"Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, ..."developer.android.com/reference/android/app/...
ToolmakerSteve

3
现在,ActivityManager.getRunningTasks()的文档显示“ API级别21中不推荐使用此方法”。
markhep

-2

我不喜欢其他答案。不打算将ActivityManager用于获取当前活动。依赖于onDestroy的超级分类也很脆弱,而不是最佳设计。

老实说,到目前为止,我想出的最好的办法就是在我的应用程序中维护一个枚举,该枚举在创建活动时设置。

另一个建议是,尽可能避免使用多个活动。这可以通过使用片段或在我的首选项自定义视图中完成。


1
一个枚举?这如何帮助定位当前的前台活动实例?
制造商史蒂夫(Steve)2015年

“超级分类并依赖onDestroy也是脆弱的”那脆弱吗?
制造商

-3

一个相当简单的解决方案是创建一个单例管理器类,您可以在其中存储对一个或多个“活动”或整个应用程序中您想访问的任何内容的引用。

呼叫 UberManager.getInstance().setMainActivity( activity );主活动的onCreate。

调用UberManager.getInstance().getMainActivity();应用中的任意位置以进行检索。(我正在使用它来从非UI线程使用Toast。)

确保UberManager.getInstance().cleanup();在销毁应用程序时添加呼叫。

import android.app.Activity;

public class UberManager
{
    private static UberManager instance = new UberManager();

    private Activity mainActivity = null;

    private UberManager()
    {

    }

    public static UberManager getInstance()
    {
        return instance;
    }

    public void setMainActivity( Activity mainActivity )
    {
        this.mainActivity = mainActivity;
    }

    public Activity getMainActivity()
    {
        return mainActivity;
    }

    public void cleanup()
    {
        mainActivity = null;
    }
}

这是侵入性的,需要在所有活动中进行更改。AZ_答案要好得多,因为它完全本地化并且是独立的,不需要在代码库中进行其他更改。
markhep

-7

我想晚3年了,但是无论如何我都会回答,以防有人像我那样找到它。

我通过简单地使用以下方法解决了这个问题:

    if (getIntent().toString().contains("MainActivity")) {
        // Do stuff if the current activity is MainActivity
    }

请注意,“ getIntent()。toString()”包括一堆其他文本,例如程序包名称和活动的所有意图过滤器。从技术上讲,我们正在检查当前意图,而不是活动,但结果是相同的。只需使用例如Log.d(“ test”,getIntent()。toString()); 如果您想查看所有文本。该解决方案有点笨拙,但是代码中的代码更加简洁,功能相同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.