【问题标题】:Concurrent Instances of Activity in one application?一个应用程序中的并发活动实例?
【发布时间】:2015-05-06 15:22:45
【问题描述】:

我有一个带有单个 Activity (MainActivity) 的 Android 应用程序。我还有一个静态变量 (foo),需要使用 MainActivity 启动和停止。 foo 的生命周期必须匹配 MainActivity 的整个生命周期,而不是它的可见生命周期,也不是它的前台生命周期。以下是基本要点:

static Foo foo;

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    foo.start();
}

protected void onPause()     
{
    super.onPause();
    if (isFinishing())
        disposeFoo();
}

protected void onDestroy()
{
    super.onDestroy();
    disposeFoo();
}

private void disposeFoo()
{
    if (foo.isRunning())
        foo.stop();
}

我会时不时地收到一个崩溃报告,上面写着:Foo 已经在运行时启动。

我可以整天从应用启动器启动和停止 MainActivity,并且不会发生这种崩溃。据我所知,也没有人在 MainActivity 上调用 startActivity。

在同一应用程序中的旧实例上运行 onDestroy 之前创建 MainActivity 的新实例并调用 onCreate 是否是预期行为?在什么情况下会出现这种情况?我应该使用不同的模式来初始化库、数据库和其他单例对象吗?

【问题讨论】:

  • Foo的确切定义在哪里?它是否从 Android API 扩展了一些东西?您可以发布该异常的完整堆栈跟踪吗?
  • 不幸的是,我的代码库中有几个不同的 'Foo',而我的 MainActivity 远没有上面那么简单。我的 Foos 中最令人沮丧的是 SQLiteOpenHelper 的子类,而我的 'Foo has been started while already running' 是 IllegalStateException: Attempt to reboot an already closed database。
  • 我明白了,但仍然不足以为您提供适当的帮助。发布完整的堆栈跟踪
  • 我理解你的沮丧,但是这个没有真正的堆栈跟踪,因为没有真正的代码。在这个玩具示例中,堆栈跟踪是 foo.start(),由 MainActivity.onCreate() 调用。我希望通过找到一种不同的方式来构造第三方库的初始化以及与数据存储的实例化/交互,从而避免未来的维护。也许我需要以某种方式将我的问题分开......我有兴趣了解为什么我的活动会在同一个应用程序中重新实例化,并且我有兴趣在一个更好的地方放置我的启动代码。
  • IllegalStateException 是一个异常,应该有一个堆栈跟踪。沮丧?不你错了。你是寻求帮助的人,而不是我。我只是想帮忙,但你不会让我。做我的客人:)

标签: android android-activity activity-lifecycle


【解决方案1】:

如果其他具有更高优先级的应用程序(通常,如果它在前台,它的优先级更高)需要资源,则您的应用程序经常杀死。这是由于具有相对有限资源的移动设备的性质。你会发现它的静态变量一旦返回就可能为null,所以在Android中长时间使用静态变量是个坏主意。

您应该将数据保存在更持久的地方。您可能会发现这篇关于一般Data Storage 的文章很有用。这个问题也应该是相关的:Saving Android Activity state using Save Instance State

【讨论】:

  • 我从许多不同的来源听说过这个,但我从未亲身体验过,我也不完全理解“静态变量一旦返回它可能为空”的含义。我相信您是说托管 Activity 的整个进程及其整个内存空间可能随时被擦除。当您返回活动时,您不能依赖任何其他活动已经运行或存在于活动下方的任务堆栈中。如果这就是你的意思,那么我完全同意,但我认为它不能回答我的问题。
  • 我的“Foo”之一是一个数据库,每次用户启动 MainActivity 时都应该擦除它,但当他“返回”它时不会。也就是说,如果他隐藏了应用程序或导航离开,然后返回到它,他的数据仍然存在。但如果他使用返回按钮退出应用程序,他的数据就会保留。我正在寻找一种适当的模式来以通用的方式做到这一点。
【解决方案2】:

因为onDestroy 不会被调用,例如,如果由于屏幕旋转而重新创建了活动。

来自活动文档:

在您的活动被销毁之前收到的最后一个电话。这可能是因为 Activity 正在完成(有人在其上调用了 finish(),或者因为系统正在临时销毁该 Activity 实例以节省空间。您可以使用 isFinishing() 方法区分这两种情况。

所以你应该在 onStart/onStop 或 onResume/onPause 中调用 foo.start/foo.stop。

--

更新:

如果我理解正确,问题是你被绑定到一个单例/单态对象 Foo 应该对所有对象都是唯一的,并且必须在所有活动被销毁时被销毁。

问题是没有什么能保证只有一个activity实例有一个正在运行的Foo,因为onDestroy可以在一个新的实例被创建之后被调用。

所以解决方案是使用实例计数器:

public class Foo {
private int instCounter = 0;

public synchronized void start() {
    ...
    ++instCounter;
}

public synchronized void dispose() {
    --instCounter;
    if (instCounter == 0) {
         // dispose
    }
}

这应该可以解决问题。

【讨论】:

  • 这个过去曾让我好几次,所以我投了赞成票。但我正在处理配置更改以防止重新实例化 Activity。如果我没记错的话,onPause isFinishing() 检查也会捕获这些。
  • 另外,我需要将静态变量的生命周期与活动的整个生命周期相关联,而不是与它何时可见或在前台相关。
  • 我不确定是否正确理解您的需求,您可以从派生自 Application 类的类中实例化变量:developer.android.com/reference/android/app/Application.html
  • 关于您的第一条评论:在 Activity 类文档中描述了 OnStart/onStop 和 onResume/onPause 之间的区别。弹出窗口(如警报对话框)将触发 onPause 但不会触发 onStop。选择取决于 Foo 做什么。
  • 好的,对不起,我误解了你的第二个表扬。帖子详情
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-30
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多