2011-06-05 50 views

回答

27

這不是線程安全的ArrayAdapter問題。與適配器一起工作的ListView和其他此類UI小部件不允許適配器的內容在其上發生意外更改。這不僅僅是由於其他線程 - 您需要告訴ListView有關您在下次嘗試與您的適配器交互之前所做的更改。

如果您允許另一個線程修改適配器,或在主線程上修改它,但在允許其他任何事情發生之前不告訴ListView有關更改,您將隨機(由於競爭)獲得ListView拋出的異常關於適配器意外更改。

在ArrayAdapter的特定情況下,如果使用API​​修改其內容,它將負責告訴列表視圖關於更改。但是,必須在主線程上進行這樣的更改,以確保列表視圖不會嘗試在發生更改的位置和列表視圖之間訪問適配器。

如果您只是對ArrayAdapter進行簡單的更改(添加和刪除一些項目),那麼您將會很好,但是您必須在主線程上執行這些操作。

對於更重要的更改(例如,由於從服務器獲取新數據,適配器正在獲取新數據集),請考慮不使用ArrayAdapter,而是實現您自己的BaseAdapter子類。 ArrayAdapter適用於需要顯示簡單而相當靜態的小數據集的情況 - 簡單情況。對於更復雜的事情,只需實施BaseAdapter並自己進行數據管理,您可能會更高興。

適配器在這些複雜情況下更新的典型方式是後臺線程生成新數據集,一旦可用,則在主線程上通過對notifyDataSetChanged()的調用自動交換到適配器讓ListView知道數據已經改變。

假設您顯示的是一些MyItem對象的數據。保持你的數據在一齣戲陣列:

ArrayList<MyItem> 

實施BaseAdapter的子類,表明這個名單:

class MyAdapter extends BaseAdapter<MyItem> { 
    ArrayList<MyItem> mItems; 

    public int getCount() { 
     return mItems != null ? mItems.size() : 0; 
    } 

    public MyItem getItem(int position) { 
     return mItems.get(i); 
    } 

    ... 
} 

喏,這就是你可以,可以從另一個被稱爲適配器上實現的功能線程提供了新的數據集顯示:

final Handler mHandler = new Handler(); 

    void setDataFromAnyThread(final ArrayList<MyItem> newData) { 
     // Enqueue work on mHandler to change the data on 
     // the main thread. 
     mHandler.post(new Runnable() { 
       @Override 
       public void run() { 
        mItems = newData; 
        notifyDataSetChanged(); 
       } 
      }); 
    } 

當然,如果你使用的AsyncTask做你的數據生成這個已經執行一個便利設施回到主線程上。同樣,您可以使用新的Loader工具來照顧後臺的這一代。

如果你仍然想使用ArrayAdapter,在你上面的功能,你可以通過清除當前數據並添加新的數據到現在空適配器做到這一點。這只是更多的開銷,並沒有給你帶來任何好處。

4

數組適配器不是線程安全的。我看到它由於併發問題而崩潰。數組適配器只會將您的數組轉換到主(GUI)線程上的視圖。因此,如果您只是在主線程上只更改數組(添加或刪除),那麼您可以確保它只會在每個線程上運行。

+0

不錯,解決了我的問題。謝謝 – 2red13 2014-03-27 09:03:39

相關問題