【问题标题】:Android - creating ListView content in AsyncTask causes random crashAndroid - 在 AsyncTask 中创建 ListView 内容会导致随机崩溃
【发布时间】:2014-12-13 12:21:09
【问题描述】:

我在后台创建 ListView 的内容,并在添加每个项目时更新 ListView 适配器。通常它可以正常工作,但有时我会收到此错误。奇怪的是,它在我的 Galaxy s4 mini 中发生的频率是我的 HTC Sensation 的 10 倍。我不明白为什么会发生这种情况,因为我通过 UI 线程清楚地通知了适配器。有什么想法吗?

java.lang.IllegalStateException: The content of the adapter has changed but ListView 
did not receive a notification. Make sure the content of your adapter is not modified 
from a background thread, but only from the UI thread. Make sure your adapter calls 
notifyDataSetChanged() when its content changes. [in ListView(2131100779, class 
util.TouchInterceptor) with Adapter(class com.bill.deuterh.ListActivity$ListAdapter)]

异步任务:

private class AsyncCaller extends AsyncTask<Void,Void,Void>{

    @Override
    protected void onPostExecute(Void result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        ListActivity.this.runOnUiThread(new Runnable(){
            @Override
            public void run() {
                findViewById(R.id.progress_bar).setVisibility(View.GONE);
            }
        });
    }

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub

        for (int i=0;i<table.length;i++){
            if(isCancelled()){break;}
            myList.add(createObject(table[i])));
            ListActivity.this.runOnUiThread(new Runnable(){
                @Override
                public void run() {
                    ListAdapter.notifyDataSetChanged();
                }
            });
        }

        return null;
    }
}

(如果重要,我将 Listview 转换为 TouchInterceptor,这是一个修改后的 google 类,用于支持通过拖放重新排列 listview 项。)

【问题讨论】:

  • 为什么不将Listadapter.notifyDataSetChanged() 放入onProgressUpdate() 并通过publishProgress 调用更新。各位,asynctask 自带 UI 运行方法!使用它们。 AsyncTask 很强大,但不是你使用它的方式
  • 在 OnPostExecute 中设置 ListView 适配器。

标签: android android-listview android-asynctask android-adapter


【解决方案1】:

试试这个:如果其他所有设置都正确。然后此代码将立即在列表视图中显示来自您的 doInBackground 的每个更新 = 良好的用户体验!

private class AsyncCaller extends AsyncTask<Void,Void,Void>{

@Override
protected void onPostExecute(Void result) {
    // TODO Auto-generated method stub
    super.onPostExecute(result);
                    findViewById(R.id.progress_bar).setVisibility(View.GONE);
}

@Override
protected Void doInBackground(Void... params) {
    // TODO Auto-generated method stub

    for (int i=0;i<table.length;i++){
        if(isCancelled()){break;}
        myList.add(createObject(table[i])));
        publishProgress();
    }

    return null;
}

protected void onProgressUpdate(Void.. values) {
ListAdapter.notifyDataSetChanged();
}

}

OnPreExecute, onPostExecute and onProgressUpdate 已经在 UIThread 上运行。也许您没有以正确的方式执行异步任务。这就是为什么没有更新数据?

【讨论】:

  • 这似乎可以解决问题。我打开此活动 100 次并没有崩溃,所以我想现在没问题。我不知道 OnPreExecute、onPostExecute 和 onProgressUpdate 在 UI 线程上执行。谢谢。
  • @Anonymous 欢迎您!如果您的 ListView 即使进行了该更改也没有再次膨胀,那么您将数据放入适配器的方式一定有问题。再一次,UIMethodsAsyncTask 附带的强大功能之一!很高兴能帮助你。快乐编码
  • @Anonymous 关于 Chris 的评论,最好不要每次 Loop 运行都调用 publisProgress()。这将导致您的屏幕冻结。如果您不希望用户等待整个内容,请以中等方式使用它。在整个doInBackground() 上调用publishProgress() 大约3-4 次。我认为这是完美的!
【解决方案2】:

这应该只在 onpostexecute 结束时运行一次,而不需要 runonuithread。 ListAdapter.notifyDataSetChanged();

删除这个:

   ListActivity.this.runOnUiThread(new Runnable(){
        @Override
        public void run() {
            ListAdapter.notifyDataSetChanged();
        }
    });

然后把这个:

    @Override
    protected void onPostExecute(Void result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        findViewById(R.id.progress_bar).setVisibility(View.GONE);
ListAdapter.notifyDataSetChanged();

    }

【讨论】:

  • 这是一个糟糕的解决方案。我认为他想立即在listview 中显示新数据。当一切都完成时,还没有结束 == 糟糕的用户体验!
  • notifyDataSetChanged 永远不应在循环中运行,因为这是非常耗时的操作。这是android开发者创建这个函数的唯一原因。
  • Mike 看看那个循环,你认为它需要多长时间才能完成?当由于所有 notfydatasetchanged 函数而冻结屏幕时,低处理器上的用户体验如何?
  • 来源?我都试过了,每次循环运行都使用progressUpdate(),并且只在每 5 次循环运行时使用它。时间消耗大约是几个(2-5?)毫秒。如果我不忘记某事。很久以前了。
  • 但无论如何你是对的。过度使用它会冻结屏幕。中等用法是完美的方法。不要等到所有数据都已加载,也不要在每次循环运行时触发onProgressUpdate()!你是对的!
【解决方案3】:

不要在doInBackground(Void... params) 中编写任何与 UI 相关或更新 UI 的代码。对于 UI Async 的更新,使用了 onPostExecute(Void result) 方法。 所以从doInbackGround(Void... params){}中删除adatper的通知

【讨论】:

    猜你喜欢
    • 2013-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-22
    • 1970-01-01
    相关资源
    最近更新 更多