如何在android中获取当前的前台活动上下文?
Posted
技术标签:
【中文标题】如何在android中获取当前的前台活动上下文?【英文标题】:How to get current foreground activity context in android? 【发布时间】:2012-07-09 19:35:22 【问题描述】:每当我的广播执行时,我都想对前台活动显示警报。
【问题讨论】:
你想从哪里获得活动的上下文。这将是您的应用程序活动还是其他应用程序。 这是一个应用活动。我已经在广播接收器 onreceive() 函数上完成了警报对话框编码。 一个应用活动!这是你的应用程序??以及你为什么想要这个,任何原因,可能有相同的替代方案 我想在我的前台活动中显示警报。他们是否还有其他方式可以在没有上下文的情况下对前台活动显示警报。 只有在 onreceive 中你会得到 COntext 作为参数,你可以说 context.getApplicationContext() 【参考方案1】:(注意: API 14 中添加了官方 API:请参阅此答案https://***.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();
【讨论】:
你可以只使用 WeakReference 并用更少的代码实现相同的结果。 @Nacho 我永远不会建议在 Android 中使用WeakReferences
,GC 收集它们的速度比你想象的要快。
@MaximKorobov 是的,如果你从 onCreate() 调用 finish(),如果你使用你的活动只是为了启动另一个活动并停止这个活动,这是可能的。在这种情况下,它会跳过 onPause() 和 onStoo()。见底注:developer.android.com/training/basics/activity-lifecycle/…
@rekire @NachoColoma 不建议使用WeakReference
进行缓存,这不是缓存,即mCurrentActivity
只有在它存在时才会引用它,所以WeakReference
将当Activity
位于顶部时,永远不会被收集。但是,@NachoColoma 的建议是错误的,因为如果未清除变量,WeakReference
可能仍会引用未恢复(未激活/不在顶部)的活动!
从 Android API 级别 14 开始,应该可以使用 Application .ActivityLifecycleCallbacks
,这将更加集中,您不必在所有活动中添加任何管理代码。另见developer.android.com/reference/android/app/…【参考方案2】:
我在@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 开始可用。
【讨论】:
所有其他答案是怎么回事?显然,这是为此目的而设计的 API。谢谢你,卓仁诚 @MichaelBushe - 在 2012 年编写其他答案时,取决于 API 级别 14 并不是每个设备都依赖的东西,因为 API 只是最近才发布(2011 年 10 月)。 找到了一个说明如何使用这种方法的答案:***.com/a/11082332/199364 好处是不需要对活动本身做任何事情;代码都在您的自定义回调类中。您只需创建一个implements Application.ActivityLifecycleCallbacks
的类,然后添加实现它的方法。然后在该类的构造函数(或 onCreate 或 init 或在实例变为活动/就绪时运行的其他方法)中,将 getApplication().registerActivityLifecycleCallbacks(this);
作为最后一行。
我认为你的答案是最好的
很好的答案。唯一的缺点是,如果您需要查询班级的当前活动,您仍然需要将活动保存在某个地方。所以你仍然需要避免内存泄漏并将引用设为空。【参考方案3】:
更新3:官方为此添加了一个api,请使用ActivityLifecycleCallbacks代替。
【讨论】:
这个答案真的应该得到更多的支持,简单的解决方案,但是当你有需要操纵活动但不是活动本身的类时功能强大。 这只是您的活动对象的静态引用。您可以在任何地方创建它:)。没关系。 这顺便说一句等同于您之前的答案。Application
只被创建一次,并且永远不会像静态变量一样被垃圾收集。
分层活动会出现问题。当您从子活动返回到父活动时:(1)称为子活动的onPause; (2) 父母的onResume; (3)child的onDestroy ==>当前activity将为null。您应该在他的示例中使用 clearReferences 方法进行一些检查,例如 @gezdy。
@waqas716 我建议将clearReferences()
中的条件简化为(this.equals(currActivity))
。【参考方案4】:
@lockwobr感谢更新
如果您阅读 github上的代码函数“currentActivityThread”发生了变化 Kitkat,所以我想说版本 19ish,有点难以匹配 api 在 github 上发布版本。
访问当前的Activity
非常方便。如果有一个静态的getActivity
方法返回当前的 Activity 而没有不必要的问题不是很好吗?
Activity
类非常有用。它允许访问应用程序的 UI 线程、视图、资源等等。很多方法都需要Context
,但是如何获取指针呢?以下是一些方法:
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;
这种方法可以在应用程序的任何地方使用,它比所有提到的方法都方便得多。此外,它似乎并不像看起来那么不安全。它不会引入任何新的潜在泄漏或空指针。
上面的代码 sn-p 缺少异常处理,并且天真地假设第一个运行的 Activity 就是我们正在寻找的那个。您可能需要添加一些额外的检查。
Blog Post
【讨论】:
在Kitkat及以上mActivities中不是HashMap,而是ArrayMap,所以需要修改这一行:HashMapactivity = (HashMap)activityField.get(activityThread);看起来像这样: ArrayMap 活动 = (ArrayMap) activitiesField.get(activityThread); @Palejandro 支持两个 api 级别(18 以上和以下)它应该使用Map
接口而不是HashMap
或ArrayMap
。我已经编辑了@AZ_ 答案。
这在 api version 16 中 100% 都不起作用,如果您阅读 github 上的代码,Kitkat 中的“currentActivityThread”函数发生了变化,所以我想说版本 19ish,很难将 api 版本与 github 中的版本相匹配。
@lockwobr 谢谢,解决方案更新了你的评论:)
不支持通过反射访问内部 API,并且可能不适用于所有设备或将来。【参考方案5】:
知道 ActivityManager 管理 Activity,因此我们可以从 ActivityManager 获取信息。我们通过
获取当前前台运行的ActivityActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
2018 年 10 月 3 日更新getRunningTasks() 已弃用。请参阅下面的解决方案。
此方法在 API 级别 21 中已弃用。 从 Build.VERSION_CODES.LOLLIPOP 开始,此方法不再可用于第三方应用程序:引入以文档为中心的最新消息意味着它可以将人员信息泄露给调用者。为了向后兼容,它仍然会返回一小部分数据:至少是调用者自己的任务,可能还有其他一些已知不敏感的任务,例如 home。
【讨论】:
不这么认为 Martin,来自 getRunningTasks 的 SDK 帮助“注意:此方法仅用于调试和呈现任务管理用户界面。这不应该用于应用程序中的核心逻辑” 显然这仅支持 Android 5 / Lollipop 中有限的运行任务子集。 ActivityManager.getRunningTasks() 的文档说“此方法在 API 级别 21 中已弃用”。【参考方案6】:我在 Kotlin 中做了以下操作
-
创建应用程序类
如下编辑应用程序类
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
您现在可以通过调用以下命令在任何类中使用它:FTApplication.currentActivity()
【讨论】:
这是我找到的唯一干净的解决方案,顺便说一句,您只需要运行 onActivityResumed,其他操作无用,因为在创建 Activity 后启动恢复。只是为了确保我会在 onActivityStopped 上设置 currentActivity = null if currentActivity == activity 效果很好,谢谢。仅供参考:要激活回调,您必须添加 AndroidManifest:【参考方案7】:getCurrentActivity() 也在 ReactContextBaseJavaModule 中。 (自从最初提出这个问题以来,许多Android应用也有ReactNative组件-混合应用。)
ReactNative 中的 ReactContext 类拥有一整套逻辑来维护在 getCurrentActivity() 中返回的 mCurrentActivity。
注意:我希望 getCurrentActivity() 在 Android Application 类中实现。
【讨论】:
在某些情况下,来自 ReactContextBaseJavaModule 的上下文为空,你知道为什么吗?【参考方案8】:为了向后兼容:
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;
【讨论】:
除非有办法从 ComponentName 获取到 Activity 的当前实例,否则这不能回答 IMO 的问题。 @nasch 可以保留并从Application
类中获取 WeakReference
句柄 - 而需要 ComponentName
来确定所需的 Activity
是否位于正在运行的任务列表的顶部.如果这不能完全回答问题,那么接受的答案也不会。
我同意,接受的答案也不能完全回答问题。
topActivity
仅适用于 Android Q【参考方案9】:
我找不到我们的团队会满意的解决方案,所以我们推出了自己的解决方案。我们使用ActivityLifecycleCallbacks
来跟踪当前活动,然后通过服务公开它。更多细节在这里:https://***.com/a/38650587/10793
【讨论】:
【参考方案10】:我个人按照“Cheok Yan Cheng”说的做了,但是我用了一个“列表”来有一个我所有活动的“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 实例;我将在 BaseActivity 中对其进行初始化,该 BaseActivity 是从所有其他活动扩展而来的,并且具有私有 RealmHelper 实例。 有关 REALM 的更多信息,请参阅:REALM INFO LINK 例如,我的“RealmHelper”类中有一个静态“RealmHelper”实例,该实例在我的应用程序“onCreate”中实例化。我有一个同步服务,我在其中创建了新的“RealmHelper”,因为 Realm 是“线程链接的”,并且 Realm 实例不能在不同的线程中工作。 因此,为了遵循领域文档“您需要关闭所有打开的领域实例以避免系统资源泄漏”,为了完成这件事,我使用了“Application.ActivityLifecycleCallbacks”,如您所见。
1234563我的 ActivitiesBackStack 列表,因为如果同步更新了视图上的数据,我需要更新它们,并且我可能需要在应用同步后执行其他操作。就是这样,希望对您有所帮助。见你:)
【讨论】:
【参考方案11】:您可以使用此Class 进行灵活的生命周期处理
用法:
//Initialization
val lifeCycleHandler = ActivityLifeCycleHandler<Activity>()
//Detect only a specific type of activities
val lifeCycleHandler = ActivityLifeCycleHandler<MainActivity>()
//Get current activity
val instance = lifeCycleHandler.currentReference
//Get current activity state
val state = lifeCycleHandler.currentState
//Use listeners
lifeCycleHandler.addStateChangeListener newState ->
//TODO: handle new state
lifeCycleHandler.addSpecificStateChangeListener(ActivityLifeCycleHandler.ActivityState.STARTED)
//TODO: handle new state
//Removable listeners
val listener = newState: Int ->
lifeCycleHandler.addStateChangeListener(listener)
lifeCycleHandler.removeStateChageListener(listener)
//Start listening
App.app.registerActivityLifecycleCallbacks(lifeCycleHandler)
//Stop listening
lifeCycleHandler.releaseListeners()
App.app.unregisterActivityLifecycleCallbacks(lifeCycleHandler)
【讨论】:
【参考方案12】:通过使用这部分代码,您可以检测应用何时进入后台/前台并访问当前活动名称和上下文。
我的回答基于这篇文章: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);
如下在Manifest中注册“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);
因此,当您的任何 Activity(您创建或包含在您的库中的 Activity)通过上述任何生命周期方法时,将调用这些回调。 应用在前台时,至少有一个处于已启动状态的Activity,而在应用处于后台时,将没有处于已启动状态的Activity。 在“App”类中声明如下2个变量。
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences 将保持活动数量的计数处于启动状态。 isActivityChangingConfigurations 是一个标志,用于指示当前 Activity 是否正在经历配置更改,例如方向切换。 使用以下代码可以检测 App 是否进入前台。
@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
现在您可以访问当前前台活动名称和上下文。
【讨论】:
【参考方案13】:waqas716 的回答很好。我为需要较少代码和维护的特定案例创建了一种解决方法。
我通过使用静态方法从我怀疑处于前台的活动中获取视图找到了一个特定的解决方法。 您可以遍历所有活动并检查您是否愿意或从 martin's 答案中获取活动名称
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
然后我检查视图是否不为空,并通过 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"));
【讨论】:
我在这里寻找类似的问题***.com/questions/22788289/… 我们如何获得“SuspectedActivity”?这是原生 API 吗? 但来自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, ..."
in developer.android.com/reference/android/app/…
ActivityManager.getRunningTasks() 的文档现在显示“此方法在 API 级别 21 中已弃用”。【参考方案14】:
我不喜欢任何其他答案。 ActivityManager 并不意味着用于获取当前活动。超级类和依赖于 onDestroy 也是脆弱的,不是最好的设计。
老实说,到目前为止我想出的最好的方法就是在我的应用程序中维护一个枚举,它会在创建活动时设置。
另一个建议可能是尽可能避免使用多项活动。这可以通过使用片段或在我偏好的自定义视图中完成。
【讨论】:
一个枚举?这如何帮助定位当前的前台活动实例? "超类和依赖于 onDestroy 也很脆弱" 怎么这么脆弱?【参考方案15】:一个相当简单的解决方案是创建一个单例管理器类,您可以在其中存储对一个或多个活动的引用,或者您希望在整个应用程序中访问的任何其他内容。
在主Activity的onCreate中调用UberManager.getInstance().setMainActivity( activity );
。
在您的应用程序中的任何位置调用 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;
【讨论】:
这是侵入性的,需要对所有活动进行更改。 answer by AZ_ 更好,因为它完全本地化和独立,无需对代码库进行其他更改。【参考方案16】:我迟到了 3 年,但无论如何我都会回答,以防有人像我一样发现这个问题。
我通过简单地使用这个解决了这个问题:
if (getIntent().toString().contains("MainActivity"))
// Do stuff if the current activity is MainActivity
请注意,“getIntent().toString()”包含一堆其他文本,例如您的包名称和您的活动的任何意图过滤器。从技术上讲,我们正在检查当前的意图,而不是活动,但结果是相同的。只需使用例如Log.d("test", getIntent().toString());如果您想查看所有文本。这个解决方案有点老套,但在你的代码中更简洁,功能也是一样的。
【讨论】:
以上是关于如何在android中获取当前的前台活动上下文?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 android 的 JUnit 测试用例中获取 MainActivity 的上下文和活动?
如何获取当前在android中活动的wifiNetwork的端口号