关于多线程下动态加载ListView数据源的注意事项

    这两天在编写使用ListVIew分页加载数据库中的信息时,碰到了一个若隐若现的bug,有时有有时没有,着实让人头大。

    其具体实现过程是在子线程中获取需要加载的下一页数据,然后在runOnUiThread(Runnable action)方法中调用adpter.notifyDataSetChanged()来通知listView进行展示更新。部分代码如下
new Thread(new Runnable() {
    @Override
        public void run() {
        //从数据库中获取下一页的数据信息
            final List<BlackBean> list = dao.findPart(pageSize,
                    totalItemCount);
        //判断是否加载到了最后
            if (list.size() < pageSize) {
                isLoadAll = true;
            }
        //将获取到的数据添加到ListView的数据源中
            blacks.addAll(list);       //放在这里会出错,为什么。。。。
            Log.d(TAG, "这里添加了数据");

        //模拟数据加载的延迟
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        //在主线程中更新UI
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    //隐藏加载进度条
                    llLoading.setVisibility(View.GONE);
                    Log.d(TAG, "这里更新UI");
                    //通知适配器更新数据
                    adapter.notifyDataSetChanged();
                    //将正在加载中的状态置为false
                    isLoading = false;
                }
            });
        }

}).start();
    经过思索和几次排查将导致异常出现的代码锁定在了blacks.addAll(list);的位置上,试着将这一句移动到runOnUiThread(new Runnable() ) 方法中,与通知adpter更新的方法放在一起。果然错误就再也没有出现了。修改后如下
new Thread(new Runnable() {
    @Override
        public void run() {
            final List<BlackBean> list = dao.findPart(pageSize,
                    totalItemCount);
            if (list.size() < pageSize) {
                isLoadAll = true;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    llLoading.setVisibility(View.GONE);
                    Log.d(TAG, "这里添加了数据");
                    blacks.addAll(list);
                    Log.d(TAG, "这里更新UI");
                    adapter.notifyDataSetChanged();
                    isLoading = false;
                }
            });
        }

}).start();
    虽然解决了bug,但还是没弄懂为什么会有这种问题。于是进一步思考,根据异常时有时无的现象,猜想可能与线程有关,毕竟时间片的调度不受我们控制,可能就在某中情况下的线程调度中执行代码出现问题。顺着这个想法,立刻去百度寻找相关资料。功夫不负有心人,在几番查询中终于在一个博客中发现了类似信息,并且那个博主对此进行了简要的解释,现整理如下。

在子线程中更新ListView的数据:多线程环境下,在listView快速滑动的时候, 这个线程更新了adapter 里面的数据,但是notifyDataSetChange还没来得及通知,下一个线程又再次更新数据,就会导致了java.lang.IllegalStateException 异常。解决办法就是:必须在Handler里面整理将要设置的给adapter的数据,设置adapter数据和notifyDataSetChange,必须在一起完成!必须在一起完成!必须在一起完成!

而我正是在listview滑动监听中开启子线程来更新数据,所以滑动过快就可能会出现异常,虽然具体导致异常产生的原理没说,但也算是能解决我关于代码方面的疑惑了。特地在此记录一下。以便回顾。

这里是参考的文章地址,http://blog.csdn.net/zhou_shaowen5208/article/details/8963740,也一并贴出来。顺带表示感谢 (●’◡’●)

你可能感兴趣的:(android学习)