1. 程式人生 > >關於多執行緒下動態載入ListView資料來源的注意事項

關於多執行緒下動態載入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滑動監聽中開啟子執行緒來更新資料,所以滑動過快就可能會出現異常,雖然具體導致異常產生的原理沒說,但也算是能解決我關於程式碼方面的疑惑了。特地在此記錄一下。以便回顧。