启动模式为singleInstance时,startActivityForResult无法正常工作

18
我希望我的应用程序的Activity堆栈上的活动只有一个实例。 我有几个屏幕是ListActivities,我不想在更改ListActivity的另一个实例时(添加,编辑,删除等)更新以前ListActivity中的列表(或者是否有一种简单的方法可以做到这一点?)。
注意:我已经阅读过singleTop将实现此目的(尽管如果按下返回按钮,它会销毁Activity),但它不起作用。 如果我转到收件箱屏幕,然后转到我的快速列表屏幕,然后再次转到我的收件箱屏幕,它会创建一个新的收件箱Activity。
现在,在我的ListActivities中,我将launchMode设置为singleInstance。 问题是:如果我使用startActivityForResult启动另一个Activity,则onActivityResult处理程序会立即触发(在创建新Activity之前)。 当我执行下一个屏幕上必要的操作以返回结果时,onActivityResult处理程序不会触发。
发生了什么?
以下是我如何启动新的Activity:
Intent intentLaunchQuickList = new Intent(ActivityMyList.this, ActivityQuickList.class);
startActivityForResult(intentLaunchQuickList, REQUEST_QUICKLIST);

这是我的返回结果方式:

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    QuickListItem qlItem = m_Adapter.getItem(position);
    if (qlItem != null && qlItem.getQLId() != -1) {
        Intent data = new Intent();
        data.putExtra("ql_id", qlItem.getQLId());
        if (getParent() == null) {
            setResult(Activity.RESULT_OK, data);
        }
        else {
            getParent().setResult(Activity.RESULT_OK, data);
        }
    }
    finish();
}

这是我的onActivityResult处理程序:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_QUICKLIST) {
        if (resultCode == Activity.RESULT_OK) {
            Bundle extras = data.getExtras();
            if (extras != null) {
                int id = extras.getInt("ql_id");
                if (id > 0) {
                    launchQLItemsThread(id);
                }
            }
        }
    }
}
2个回答

23
根据 startActivityForResult 的文档:“例如,如果您要启动的活动使用 singleTask 启动模式,则它不会在您的任务中运行,因此您将立即收到取消结果。”singleInstance 活动也是如此。
换句话说,如果您想使用 sAFR,您需要处理多个活动实例。我的建议是,在 onPause 中将您的 ListActivity 实例的列表状态存储到某个应用程序全局位置(单例或其他位置),然后在 onResume 中从该位置加载。那样,即使创建了多个 ListActivity 实例,顶部实例也总是在旧实例恢复之前更新数据,列表始终对用户显示为当前状态。
请注意,如果您的数据应该是持久性的,您无论如何都应该这样做,因为在 onPause 调用后,系统可以随时杀死整个进程,如果您还没有在此之前将任何更改保存在某个地方,则可能会在某些情况下发生静默丢失,这些情况通常非常罕见且不可预测。在这种情况下,您需要使用本地文件或 SQLite 数据库,而不是持久化到网络。由于用户无法在运行期间与系统交互,因此 onPause 需要快速返回,因此请保存到本地存储并在其他时间通过由 onPause 启动的服务进行同步。

谢谢您的回复。我明白您想让我做什么,但是这不会引起问题吗?显然,在onSaveInstanceState和onRestoreInstanceState处理程序中,我需要做同样的事情。如果在onResume和onPause中从数据库读取和保存信息,我的应用程序将不必要地从数据库中读取,并且只有在它命中onRestoreInstanceState处理程序时才从SavedInstanceState中读取。而且,这是否真的是Android开发人员普遍的做法?将ListActivity数据保存到单例以供多个ListActivity实例使用吗?再次感谢您的帮助。 - Andrew
另外,你能解释一下为什么singleTop对我不起作用吗?还是我没有理解它的含义? - Andrew
SavedInstanceState仅适用于恢复活动的特定实例的状态;使用它来保存滚动位置等,而不是列表内容。您想要做的是让所有活动实例共享某些状态,因此如果状态是持久的,则应将其放入数据库中,如果只是来自网络的数据缓存,则应将其放入静态成员中。根据您所说的,我建议使用后者;只需使用DB来保存您仍然需要在未来发送到网络的数据,以便您不会丢失它。 - Walter Mundt
我认为我将把所有东西都存储到数据库中。我需要缓存所有这些数据,以便离线使用。目前我使用单例模式处理某些事情。例如,用户登录后,我会设置一个单例,其中包含用户名、密码和用户 ID。但是,在许多屏幕中的数据需要在离线时可用。根据经验,您是否建议我在担心保存数据(和同步多个活动实例)之前使我的活动功能化?或者应该直接在构建其余屏幕的同时构建数据库。谢谢 - Andrew
我建议首先将你的数据模型定义为Java类,然后在其上紧密构建Activity UI实现以推动设计。数据模型类背后的实际实现可以很简单(不是持久的和其他东西),稍后再展开。重要的第一步是确保数据模型正确地驱动UI。我发现最好的方法通常是具有数据模型类的静态单例实例,每个Activity在onCreate()中检索它。然后,让该数据在所有不同的Activity实例之间传播就很容易了。 - hackbod
你可能还会发现,你的数据模型需要变得更加活跃--拥有自己的线程来完成一些工作,例如拉取网络数据,当数据变化时执行回调以便更新UI。我通常通过在数据模型上添加resume()和pause()方法来解决这个问题,这些方法在活动的onResume()和onPause()中调用。每次只会有一个客户活动存在,它可以提供回调接口给resume()方法,在那个时候刷新它的UI以匹配当前的数据。 - hackbod

2
我有几个屏幕是 ListActivities,我不想在另一个实例的 ListActivity 更改时更新以前的 ListActivity 中的列表(或者是否有一种简单的方法可以做到这一点?)。应使用一致的模型。例如,您的数据希望在数据库中。每个 ListActivity 都有一个游标,指向它所需的数据库部分。通过 startManagingCursor() 使该游标成为“受管理的游标”,并且您的 ListViews 将在 onResume() 中自动更新。然后,通过数据库对模型进行更改。
如果我进入收件箱屏幕,然后进入 QuickList 屏幕,然后再次进入收件箱屏幕,它会创建一个新的 Inbox Activity。那就是它应该做的。引用文档:
“标准”模式和“singleTop”模式之间仅有一个区别:每当有新的意图要求“标准”活动时,就会创建该类的新实例以响应该意图。每个实例处理单个意图。同样,也可能创建一个新的“singleTop”活动实例来处理新的意图。但是,如果目标任务已经存在该活动类的现有实例位于其堆栈顶部,则该实例将接收新的意图(在onNewIntent()调用中);不会创建新实例。
在其他情况下 - 例如,如果“singleTop”活动的现有实例位于目标任务中,但不在堆栈顶部,或者如果它在堆栈顶部,但不在目标任务中 - 将创建一个新实例并推入堆栈。
(加粗部分为强调)
“现在,在我的ListActivities中,我将launchMode设置为singleInstance。”
请不要这样做。

谢谢您的回复。您能否提供一些关于管理游标和数据库的有用信息链接给我?我还没有在Android应用程序中实现过数据库。 - Andrew
目前(我上周开始编写这个应用程序,这是我的第一个应用程序),我只是从我的 Web 服务中提取数据并填充列表。显然,创建 Activity 的新实例将需要再次从 Web 服务中提取数据。这就是为什么我开始使用 singleInstance 的原因。我理解你所说的逻辑。我还没有实现类似的东西,所以一些资源会很有帮助。再次感谢。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接