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

Dee*_*ali 156 android android-activity

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

小智 203

(注意: API 14中添加了官方API:请参阅此答案/sf/answers/2085051601/)

不要使用上一个(waqas716)的答案.

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

为避免这种情况,您应该管理活动参考.在清单文件中添加应用程序的名称:

<application
    android:name=".MyApp"
    ....
 </application>
Run Code Online (Sandbox Code Playgroud)

你的申请类:

  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;
        }
  }
Run Code Online (Sandbox Code Playgroud)

创建一个新的活动:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,现在不要为您的活动扩展Activity类,只需扩展MyBaseActivity.现在,您可以从应用程序或活动上下文中获取当前活动:

Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
Run Code Online (Sandbox Code Playgroud)

  • 从Android API级别14开始,应该可以使用`Application .ActivityLifecycleCallbacks`,这将更加重要,您不必在所有活动中添加任何管理代码.另请参阅http://developer.android.com/reference/android/app/Application.html#registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks) (12认同)
  • 您可以使用WeakReference并使用更少的代码实现相同的结果. (7认同)
  • @Nacho我永远不会建议在Android中使用`WeakReferences`,GC会比你想象的更快地收集它们. (5认同)
  • @MaximKorobov是的,如果你从onCreate()调用finish(),如果你只是用你的活动来启动另一个活动并停止这个活动.在这种情况下,它会跳过onPause()和onStoo().请参阅底部注释:http://developer.android.com/training/basics/activity-lifecycle/starting.html (4认同)
  • @rekire @NachoColoma不建议使用`WeakReference`进行缓存,这不是缓存,也就是说`mCurrentActivity`只有在它活着的时候才会引用它,所以在`Activity`时永远不会收集`WeakReference`.在顶部.然而@NachoColoma建议的是错误的,因为如果没有清除变量,`WeakReference`仍然可以引用非恢复(非活动/不在顶部)活动! (2认同)
  • 反对使用一个可怕的全局变量,该变量必须在您的应用程序中的任何地方手动更新。*永远*不要写这样的代码! (2认同)

Che*_*eng 65

我扩展了@ gezdy的答案.

在每个活动中,Application我们不需要使用手动编码"注册"自己,而是从14级开始使用以下API,以帮助我们实现类似的目的,减少手动编码.

public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
Run Code Online (Sandbox Code Playgroud)

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

Application.ActivityLifecycleCallbacks,你可以得到哪个Activity"附加"或"分离"到这个Application.

但是,此技术仅在API级别14之后可用.

  • 找到一个答案,展示如何使用这种方法:http://stackoverflow.com/a/11082332/199364好处是**不需要对活动本身做任何事情**; 代码全部在您的自定义回调类中.您只需创建一个"实现Application.ActivityLifecycleCallbacks"的类,并添加实现它的方法.然后在该类的构造函数(或onCreate或init或其他在实例变为活动/准备就绪时运行的方法)中,将`getApplication().registerActivityLifecycleCallbacks(this);`作为最后一行. (4认同)
  • @MichaelBushe--在2012年,当其他答案被写入时,取决于API级别14并不是每台设备都可以依赖的,因为API最近才发布(2011年10月). (2认同)
  • 很好的答案.唯一的缺点是,如果您需要查询当前活动的课程,您仍需要将活动保存到某个地方.所以你仍然需要避免内存泄漏并使引用为空. (2认同)

Waq*_*qas 51

更新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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • @ waqas716我建议将`clearReferences()`中的条件简化为`(this.equals(currActivity))`. (4认同)
  • 这个答案应该真正获得更多的选票,简单的解决方案,但是当你有需要操纵活动但不是活动本身的类时,它会很强大. (3认同)

AZ_*_*AZ_ 48

@lockwobr感谢您的更新

这在api版本16中没有100%的时间工作,如果你在github上读取代码,函数"currentActivityThread"在Kitkat中发生了变化,所以我想说版本19ish,有点难以匹配api版本到github中的版本.

能够访问当前Activity非常方便.使用静态getActivity方法返回当前活动并没有不必要的问题,这不是很好吗?

Activity门课非常有用.它可以访问应用程序的UI线程,视图,资源等等.许多方法需要一个Context,但如何获得指针?以下是一些方法:

  • 使用重写的生命周期方法跟踪应用程序的状态.您必须将当前Activity存储在静态变量中,并且需要访问所有活动的代码.
  • 使用Instrumentation跟踪应用程序的状态.在清单中声明Instrumentation,实现它并使用其方法来跟踪Activity更改.将Activity指针传递给Activity中使用的方法和类.使用其中一个代码注入库注入指针.所有这些方法都相当不方便 ; 幸运的是,有一种更容易获得当前活动的方法.
  • 似乎系统需要访问所有活动而没有上述问题.因此,很可能有一种方法只使用静态调用来获取活动.我花了很多时间在grepcode.com上挖掘Android源代码,然后找到了我想要的东西.有一个叫做的课ActivityThread.这个类可以访问所有活动,更好的是,它有一个静态方法来获取当前活动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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法可以在应用程序的任何地方使用,它比所有提到的方法更方便.而且,它看起来并不像它看起来那样不安全.它不会引入任何新的潜在泄漏或空指针.

上面的代码片段没有异常处理,天真地假设第一个正在运行的Activity是我们正在寻找的.您可能想要添加一些额外的检查.

博客文章

  • @Palejandro支持api级别(18以上)它应该使用`Map`接口而不是`HashMap`或`ArrayMap`.我编辑过@AZ_回答. (6认同)
  • 在Kitkat及更高版本中,mActivities不是HashMap,而是ArrayMap,因此您需要更改此行:HashMap活动=(HashMap)ActivitiesField.get(activityThread); 看起来像这样:ArrayMap Activities =(ArrayMap)ActivitiesField.get(activityThread); (2认同)
  • 在api [版本16]中,这不能100%地起作用(https://github.com/android/platform_frameworks_base/blob/jb-mr1.1-release/core/java/android/app/ActivityThread.java) ,如果您在github上阅读了代码,则Kitkat中的函数“ currentActivityThread”已更改,因此我想说的是[19ish](https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/ java / android / app / ActivityThread.java),很难将api版本与github中的版本进行匹配。 (2认同)
  • 不支持通过反射访问内部 API,并且可能不适用于所有设备或将来。 (2认同)

KAl*_*lO2 35

知道ActivityManager管理Activity,所以我们可以从ActivityManager获取信息.我们得到当前前台运行的Activity

ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Run Code Online (Sandbox Code Playgroud)

更新2018/10/03
getRunningTasks()已弃用.看下面的解决方案.

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

  • 不要这么认为Martin,来自getRunningTasks的SDK帮助"注意:此方法仅用于调试和呈现任务管理用户界面.这绝不应该用于应用程序中的核心逻辑" (15认同)
  • [ActivityManager.getRunningTasks()](https://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks(int))的文档说"此方法在API级别21中已弃用". (7认同)
  • 显然,这仅支持Android 5/Lollipop中有限的运行任务子集. (3认同)

Ras*_*d.Z 15

我在 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
        }
    }
    
     }
    
    Run Code Online (Sandbox Code Playgroud)
  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
    }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 您现在可以通过调用以下命令在任何类中使用它: FTApplication.currentActivity()

  • 这是我找到的唯一干净的解决方案,顺便说一句,您只需要运行 onActivityResumed ,其他操作都是无用的,因为恢复是在创建活动后启动的。只是为了确保如果 currentActivity == Activity 我会在 onActivityStopped 上设置 currentActivity = null (3认同)

Mar*_*ler 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 除非有办法从 ComponentName 获取 Activity 的当前实例,否则这并不能回答这个问题。 (5认同)
  • `topActivity` 仅适用于 Android Q (2认同)

朱梅寧*_*朱梅寧 5

getCurrentActivity()也在ReactContextBaseJavaModule中.
(由于这个问题最初被问到,许多Android应用程序也有ReactNative组件 - 混合应用程序.)

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

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