【问题标题】:Populating listview in separate thread在单独的线程中填充列表视图
【发布时间】:2012-05-04 20:08:51
【问题描述】:

在一个活动中,我加载需要花费大量时间的列表视图的行,因此我将此任务放在单独的线程中以允许显示进度对话框。

我做以下事情

private void doMyStuff() {
    listItems.clear();
    progressDialog.show();

    new Thread(new Runnable() {
        @Override
        public void run() {                
            for () {
                listItems.add(something to add);
            }
        handler.sendEmptyMessage(0);
        progressDialog.dismiss();
        }
    }).start();
}

private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        if (msg.what == 0) {
            adapter.notifyDataSetChanged();
        }
    };
};

我有时会遇到一个引发 IllegalStateException 的错误。首先我很惊讶,因为像这样的线程编程是我通常在标准 Java 程序中所做的。

这个bug只是“有时”出现,在逐步调试时不会出现。

这导致我在网上搜索,我在 SO 中发现了一些与此相关的问题,我必须承认我的想法并不清楚。

当我只在线程完成时才调用 notifyDataSetChanged() 为什么它有时会引发异常。

有人可以确认我这种做法是错误的,我必须使用异步任务,或许可以解释一下原因???

我需要显示一个progressDialog,谁能给我一个AsyncTask填充列表视图并显示填充进度的progressDialog的简单示例。

谢谢

更新

jtanveer 给了我 asynctask 问题的答案。现在其他人指出解雇不在处理程序中,我更正了。

根据 jtanveer 关于“无痛线程”的文章,他们这么说

Android 提供了几种从其他线程访问 UI 线程的方法,其中一种是 HANDLER。

有人知道为什么将被解雇的处理程序没有解决我的问题吗?对我来说 listItem.add 与 UI 无关?在这一点上我错了吗?

对我来说,在我的代码中唯一的 UI 是 adapter 和 progressdialog ?欢迎任何评论或解释。

最终答案

stjom 为我的特定代码提供了有效的答案。在处理程序中运行 runOnUiThread。它正在工作,但我很惊讶,因为我认为处理程序是在 Ui 线程中运行的......

感谢大家的回答。

【问题讨论】:

  • 我相信有很多例子。尝试在网上搜索类似“android progressdialog asynctask example”的内容。
  • 您现在的两个答案都是正确的。你不需要使用AsyncTask,尽管它确实使这样的操作变得干净。您必须做的是 UI/主线程上的所有 UI 操作,这包括关闭进度对话框。您可以使用下面的 AsyncTask 示例,也可以将 progressDialog.dismiss() 添加到您的 Handler 回调中。
  • +1 感谢你们指出了这个错误。不幸的是,它没有纠正我的问题,但我不明白为什么因为处理程序在 UI 线程中!
  • 我已经更新了我的问题。为什么你的答案不起作用?

标签: android multithreading listview android-asynctask


【解决方案1】:

定义如下内部类:

private class LoadListTask extends AsyncTask<String, Void, Integer> {
    protected void onPreExecute() {
        progressDialog.show();
    }

    protected Integer doInBackground(String... params) {
        for () {
            listItems.add(something to add);
        }
        return 0;
    }

    protected void onPostExecute(Integer result) {
        progressDialog.dismiss();
        if (result == 0) {
           adapter.notifyDataSetChanged();
        }
    }
}

如果需要,可以参考这个article

【讨论】:

  • 感谢您抽出宝贵时间回答问题。它工作正常并纠正了我的错误。但是我不明白为什么其他人的答案不起作用,因为将被解雇的处理程序应该纠正它。
  • 您正在尝试从单独的线程更新 UI,这是严格禁止的。 UI 更新必须从 UI 线程完成。也许这就是你得到异常的原因。
  • 正如我所说,我已经更正了我的代码并在“处理程序”中执行了解除操作,这就是为什么现在我不明白为什么我仍然收到错误,而您的代码工作正常
  • 我不确定您是否可以将项目添加到与后台线程中的适配器链接的集合listItems。有时它会引发异常。所以我通常创建另一个列表(集合),在那里添加项目。然后在 UI 线程中,我向适配器添加一个集合 (adapter.addAll(newList); adapter.notifyDataSetChanged();)。
【解决方案2】:

每当您调用 adapter.notifyDataSetChanged(); 时,它都会识别您的 listItems 对象的任何更改。如果发现任何更改,它将相应地更新 UI,我认为这会导致您的问题。你可以打电话

runOnUiThread(new Runnable() {
    public void run() {
        adapter.notifyDataSetChanged();
    }  
});

在您的处理程序内部。

【讨论】:

  • +1 它正在工作!但是你指出了一些我无法理解的东西。 UI线程中怎么没有执行Handler???我以为处理程序是在 UI 线程中运行的。
【解决方案3】:

您不需要使用 AsyncTask,它只是一种方便。

至于为什么您当前的实现有时不起作用 - 您应该从 UI 线程中关闭您的进度对话框,因此需要进入您的处理程序,而不是您的后台线程。

【讨论】:

  • +1 用于指出错误。不幸的是,它没有纠正我的问题,但我不明白为什么,因为正如你所说,据我所知,这只是为了“方便”,并且由于处理程序位于 UI 线程中,所以我仍然不清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 2013-10-11
  • 1970-01-01
  • 2011-02-17
  • 2014-01-05
相关资源
最近更新 更多