繁体   English   中英

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

[英]How to get current foreground activity context in android?

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

注: 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();

我扩展了@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 开始可用。

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

@lockwobr感谢您的更新

这在 api 版本 16 中 100% 都不起作用,如果您阅读 github 上的代码,Kitkat 中的“currentActivityThread”函数发生了变化,所以我想说的是 19 版本,很难将 api 版本与 github 中的版本相匹配.

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

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

  • 使用覆盖的生命周期方法跟踪应用程序的状态。 您必须将当前活动存储在一个静态变量中,并且您需要访问所有活动的代码。
  • 使用 Instrumentation 跟踪应用程序的状态。 在清单中声明 Instrumentation,实现它并使用它的方法来跟踪 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;
}

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

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

博客文章

知道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。

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

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

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

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

为了向后兼容:

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

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

我个人按照“卓仁诚”所说的做了,但我用一个“列表”来对我所有的活动进行“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”,如您所见。

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

就是这样,希望这会有所帮助。 再见 :)

通过使用这部分代码,您可以检测应用程序何时进入后台/前台并访问当前活动名称和上下文。

我的回答基于这篇文章: Android:如何检测 App 何时进入后台/前台

首先,创建一个扩展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。 在“App”类中声明 2 个变量,如下所示。

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

activityReferences 将保持处于已启动状态的活动数量的计数。 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
    }
}

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

迄今为止最好的解决方案在您的应用程序中创建一个类名 ActivityManager (java)

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

您可以使用此类进行灵活的生命周期处理

用法:

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

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

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

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

使用is运算符或其否定形式!is执行运行时检查,以识别对象是否符合给定类型:

if (this !is OneActivity) {
// do something
} else if (this !is TwoActivity) {
// do something 2
}

我不喜欢任何其他答案。 ActivityManager 并不意味着用于获取当前活动。 超级分类和依赖于 onDestroy 也是脆弱的,不是最好的设计。

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

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

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

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

我迟到了 3 年,但无论如何我都会回答,以防有人像我一样发现这个问题。

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

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

请注意,“getIntent().toString()”包括一堆其他文本,例如您的包名称和您的活动的任何意图过滤器。 从技术上讲,我们正在检查当前的意图,而不是活动,但结果是相同的。 只需使用例如 Log.d("test", getIntent().toString()); 如果您想查看所有文本。 这个解决方案有点老套,但它在你的代码中更干净,功能也是一样的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM