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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11411395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:19:16  来源:igfitidea点击:

How to get current foreground activity context in android?

androidandroid-activity

提问by Deepali

Whenever my broadcast is executed I want to show alert to foreground activity.

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

采纳答案by KAlO2

Knowing that ActivityManagermanages Activity, so we can gain information from ActivityManager. We get the current foreground running Activity by

知道ActivityManager管理Activity,所以我们可以从ActivityManager获取信息。我们通过以下方式获取当前前台运行的 Activity

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

UPDATE 2018/10/03
getRunningTasks() is DEPRECATED. see the solutions below.

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

This method was deprecated in API level 21. As of Build.VERSION_CODES.LOLLIPOP, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive.

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

回答by gezdy

(Note:An official API was added in API 14: See this answer https://stackoverflow.com/a/29786451/119733)

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

DO NOT USE PREVIOUS (waqas716) answer.

不要使用以前的 (waqas716) 答案。

You will have memory leak problem, because of the static reference to the activity. For more detail see the following link http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html

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

To avoid this, you should manage activities references. Add the name of the application in the manifest file:

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

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

Your application class :

您的应用程序类:

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

Create a new Activity :

创建一个新活动:

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

So, now instead of extending Activity class for your activities, just extend MyBaseActivity. Now, you can get your current activity from application or Activity context like that :

因此,现在不是为您的活动扩展 Activity 类,而是扩展 MyBaseActivity。现在,您可以像这样从应用程序或活动上下文中获取当前活动:

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

回答by Cheok Yan Cheng

I expand on the top of @gezdy's answer.

我扩展了@gezdy 的答案。

In every Activities, instead of having to "register" itself with Applicationwith manual coding, we can make use of the following API since level 14, to help us achieve similar purpose with less manual coding.

在每个活动中,Application我们可以使用以下 API来代替手动编码来“注册”自己,从级别 14 开始,以帮助我们以更少的手动编码实现类似的目的。

public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)

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

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

In Application.ActivityLifecycleCallbacks, you can get which Activityis "attached" to or "detached" to this Application.

在 中Application.ActivityLifecycleCallbacks,您可以获得Activity“附加”到或“分离”到 this 的信息Application

However, this technique is only available since API level 14.

但是,此技术仅从 API 级别 14 开始可用。

回答by Waqas

Update 2: There is an official api added for this, please use ActivityLifecycleCallbacksinstead.

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

UPDATE:

更新:

As pointed by @gezdy, and I'm grateful for that. set reference to null toofor current activity, instead of updating on just every onResume set it to null on every Activity's onDestroy to avoid memory leak problem.

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

A while ago I needed the same functionality and here is the method how I achieved this. In your every activity override these life cycle methods.

不久前我需要相同的功能,这里是我如何实现这一点的方法。在您的每个活动中覆盖这些生命周期方法。

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

Now in your broadcast class you can access current activity to show alert on it.

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

回答by AZ_

@lockwobrThanks for update

@lockwobr感谢更新

This does not work 100% of the time in api version 16, if you read the code on github the function "currentActivityThread" was change in Kitkat, so I want to say version 19ish, kind of hard to match api version to releases in github.

这在 api 版本 16 中 100% 的时间不起作用,如果您阅读 github 上的代码,Kitkat 中的函数“currentActivityThread”已更改,所以我想说版本 19ish,有点难以将 api 版本与 github 中的版本匹配.

Having access to the current Activityis very handy. Wouldn't it be nice to have a static getActivitymethod returning the current Activity with no unnecessary questions?

访问电流Activity非常方便。有一个静态getActivity方法返回当前 Activity 而没有不必要的问题不是很好吗?

The Activityclass is very useful. It gives access to the application's UI thread, views, resources, and many more. Numerous methods require a Context, but how to get the pointer? Here are some ways:

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

  • Tracking the application's state using overridden lifecycle methods. You have to store the current Activity in a static variable and you need access to the code of all Activities.
  • Tracking the application's state using Instrumentation. Declare Instrumentation in the manifest, implement it and use its methods to track Activity changes. Passing an Activity pointer to methods and classes used in your Activities. Injecting the pointer using one of the code injection libraries. All of these approaches are rather inconvenient; fortunately, there is a much easier way to get the current Activity.
  • Seems like the system needs access to all Activities without the issues mentioned above. So, most likely there is a way to get Activities using only static calls. I spent a lot of time digging through the Android sources on grepcode.com, and I found what I was looking for. There is a class called ActivityThread. This class has access to all Activities and, what's even better, has a static method for getting the current ActivityThread. There is only one little problem – the Activity list has package access.
  • 使用覆盖的生命周期方法跟踪应用程序的状态。您必须将当前活动存储在一个静态变量中,并且您需要访问所有活动的代码。
  • 使用 Instrumentation 跟踪应用程序的状态。在清单中声明 Instrumentation,实现它并使用它的方法来跟踪 Activity 的变化。将活动指针传递给活动中使用的方法和类。使用代码注入库之一注入指针。所有这些方法都相当不方便幸运的是,有一种更简单的方法来获取当前的 Activity。
  • 似乎系统需要访问所有没有上述问题的活动。因此,很可能有一种方法可以仅使用静态调用来获取活动。我花了很多时间在 grepcode.com 上挖掘 Android 源代码,然后找到了我要找的东西。有一个类叫做ActivityThread. 这个类可以访问所有活动,更棒的是,它有一个静态方法来获取当前的ActivityThread. 只有一个小问题——活动列表具有包访问权限。

Easy to solve using reflection:

使用反射很容易解决:

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

Such a method can be used anywhere in the app and it's much more convenient than all of the mentioned approaches. Moreover, it seems like it's not as unsafe as it looks. It doesn't introduce any new potential leaks or null pointers.

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

The above code snippet lacks exception handling and naively assumes that the first running Activity is the one we're looking for. You might want to add some additional checks.

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

Blog Post

博客文章

回答by Rashad.Z

I did the Following in Kotlin

我在 Kotlin 中做了以下操作

  1. Create Application Class
  2. Edit the Application Class as Follows

    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. Create the ActivityLifecycleCallbacks class

    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. you can now use it in any class by calling the following: FTApplication.currentActivity()

  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()

回答by u9028356

getCurrentActivity() is also in ReactContextBaseJavaModule.
(Since the this question was initially asked, many Android app also has ReactNative component - hybrid app.)

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

class ReactContext in ReactNative has the whole set of logic to maintain mCurrentActivity which is returned in getCurrentActivity().

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

Note: I wish getCurrentActivity() is implemented in Android Application class.

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

回答by Muxa

I could not find a solution that our team would be happy with so we rolled our own. We use ActivityLifecycleCallbacksto keep track of current activity and then expose it through a service. More details here: https://stackoverflow.com/a/38650587/10793

我找不到让我们的团队满意的解决方案,所以我们推出了自己的解决方案。我们ActivityLifecycleCallbacks用来跟踪当前活动,然后通过服务公开它。更多细节在这里:https: //stackoverflow.com/a/38650587/10793

回答by Martin Zeitler

For backwards compatibility:

为了向后兼容:

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

回答by Z3R0

Personally I did as "Cheok Yan Cheng" said, but I used a "List" to have a "Backstack" of all my activities.

我个人按照“Cheok Yan Cheng”说的做了,但是我用了一个“列表”来拥有我所有活动的“后台”。

If you want to check Which is the Current Activity you just need to get the last activity class in the list.

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

Create an application which extends "Application" and do this:

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

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()     }
}

}

In my case I used "Application.ActivityLifecycleCallbacks" to:

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

  • Bind/Unbind Merlin Instance (used to get event when the app lose or get connection, for example when you close mobile data or when you open it). It is useful after the "OnConnectivityChanged" intent action was disabled. For more info about MERLIN see: MERLIN INFO LINK

  • Close my last Realm Instance when the application is closed; I will init it inside a BaseActivity wich is extended from all others activities and which has a private RealmHelper Instance. For more info about REALM see: REALM INFO LINKFor instance I have a static "RealmHelper" instance inside my "RealmHelper" class which is instantiated inside my application "onCreate". I have a synchronization service in which I create I new "RealmHelper" because Realm is "Thread-Linked" and a Realm Instance can't work inside a different Thread. So in order to follow Realm Documentation "You Need To Close All Opened Realm Instances to avoid System Resources Leaks", to accomplish this thing I used the "Application.ActivityLifecycleCallbacks" as you can see up.

  • Finally I have a receiver wich is triggered when I finish to synchronize my application, then when the sync end it will call the "IEndSyncCallback" "onEndSync" method in which I look if I have a specific Activity Class inside my ActivitiesBackStack List because I need to do some operation if I already have passed this point inside my application.

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

  • 当应用程序关闭时关闭我的最后一个领域实例;我将在一个 BaseActivity 中初始化它,它是从所有其他活动扩展而来的,它有一个私有的 RealmHelper 实例。有关 REALM 的更多信息,请参阅:REALM INFO LINK例如,我的“RealmHelper”类中有一个静态的“RealmHelper”实例,它在我的应用程序“onCreate”中实例化。我有一个同步服务,我在其中创建了新的“RealmHelper”,因为 Realm 是“线程链接的”,并且 Realm 实例无法在不同的线程中工作。因此,为了遵循领域文档“您需要关闭所有打开的领域实例以避免系统资源泄漏”,为了完成此操作,我使用了“Application.ActivityLifecycleCallbacks”,如您所见。

  • 最后,当我完成同步我的应用程序时,我有一个接收器被触发,然后当同步结束时,它会调用“IEndSyncCallback”“onEndSync”方法,在该方法中我查看我的 ActivitiesBackStack 列表中是否有特定的活动类,因为我需要如果我已经在我的应用程序中通过了这一点,则执行一些操作。

That's all, hope this is helpful. See u :)

就这些,希望对大家有帮助。再见 :)