2010-12-02 24 views
7

我正在使用自定義的CursorAdapter從SQLite數據庫獲取數據並將其顯示在列表視圖中。數據庫包含大約8000行的2列。所以我正在尋找一種方法來儘可能快地查詢和顯示所有數據。我有做的AsyncTask這這裏是代碼:使用Cursor和ListView適配器處理大量數據

private class PrepareAdapter extends AsyncTask<Void,Void,CustomCursorAdapter > { 

@Override 
protected void onPreExecute() { 
    dialog.setMessage("Wait"); 
    dialog.setIndeterminate(true); 
    dialog.setCancelable(false); 
    dialog.show(); 


    Log.e("TAG","Posle nov mAdapter"); 
} 

@Override 
protected CustomCursorAdapter doInBackground(Void... unused) { 

    Cursor cursor = myDbNamesHelper.getCursorQueryWithAllTheData(); 
    mAdapter.changeCursor(cursor); 
    startManagingCursor(cursor); 
    Log.e("TIME","posle start managing Cursor" + String.valueOf(SystemClock.elapsedRealtime()-testTime)+ " ms"); 
    testTime=SystemClock.elapsedRealtime(); 

    mAdapter.initIndexer(cursor); 
    return mAdapter; 
} 

protected void onPostExecute(CustomCursorAdapter result) { 

    TabFirstView.this.getListView().setAdapter(result); 
    Log.e("TIME","posle adapterSet" + String.valueOf(SystemClock.elapsedRealtime()-testTime)+ " ms"); 
    testTime=SystemClock.elapsedRealtime(); 
    dialog.dismiss(); 
} 

}

,當我需要的結果集到一個適配器這部作品除了一部分好。我做了一些時間測試,它需要約700毫秒才能通過startManagingCursor。問題是大約需要7秒才能使其通過setAdapter(結果),並且這是在UI線程中運行的,所以它使我的應用程序無響應(進度對話框凍結,有時會執行應用程序)。我該如何減少這一次?我可以讓這也運行在後臺或以任何方式來提高響應能力?

tnx。

public class CustomCursorAdapter extends SimpleCursorAdapter implements OnClickListener,SectionIndexer,Filterable, 
            android.widget.AdapterView.OnItemClickListener{ 

    private Context context; 
    private int layout; 
    private AlphabetIndexer alphaIndexer; 

    public CustomCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to) { 
     super(context, layout, c, from, to); 
     this.context = context; 
     this.layout = layout; 

    } 
    public void initIndexer(Cursor c){ 
     alphaIndexer=new AlphabetIndexer(c, c.getColumnIndex(DataBaseNamesHelper.COLUMN_NAME), " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
    } 

    @Override 
    public View newView(Context context, Cursor cursor, ViewGroup parent) { 

     Cursor c = getCursor(); 

     final LayoutInflater inflater = LayoutInflater.from(context); 
     View v = inflater.inflate(layout, parent, false); 

     int nameCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_NAME); 

     String name = c.getString(nameCol); 

     /** 
     * Next set the name of the entry. 
     */ 
     TextView name_text = (TextView) v.findViewById(R.id.name_entry); 
     if (name_text != null) { 
      name_text.setText(name); 
     } 

     int favCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_FAVOURITED); 
     int fav = c.getInt(favCol); 

     int idCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_ID); 

     Button button = (Button) v.findViewById(R.id.Button01); 
     button.setOnClickListener(this); 
     button.setTag(c.getInt(idCol)); 
     if(fav==1){ 
      button.setVisibility(View.INVISIBLE); 
     } 
     else button.setVisibility(View.VISIBLE); 

     return v; 
    } 

    @Override 
    public void bindView(View v, Context context, Cursor c) { 

     int nameCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_NAME); 

     String name = c.getString(nameCol); 

     /** 
     * Next set the name of the entry. 
     */ 
     TextView name_text = (TextView) v.findViewById(R.id.name_entry); 
     if (name_text != null) { 
      name_text.setText(name); 
     } 
     int favCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_FAVOURITED); 
     int fav = c.getInt(favCol); 

     Button button = (Button) v.findViewById(R.id.Button01); 
     button.setOnClickListener(this); 
     int idCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_ID); 
     button.setTag(c.getInt(idCol)); 
     // Log.e("fav",String.valueOf(fav)); 
     if(fav==1){ 
      button.setVisibility(View.INVISIBLE); 
     } else button.setVisibility(View.VISIBLE); 
    } 



    @Override 
    public int getPositionForSection(int section) { 
     return alphaIndexer.getPositionForSection(section); 
    } 

    @Override 
    public int getSectionForPosition(int position) { 
      return alphaIndexer.getSectionForPosition(position); 
    } 

    @Override 
    public Object[] getSections() { 
      return alphaIndexer.getSections(); 
    } 
    @Override 
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { 
     Log.e("item Click", arg1.toString()+ " position> " +arg2); 
    } 

    @Override 
    public void onClick(View v) { 
      if(v.getId()==R.id.Button01){ 
       //Log.e("Button Click", v.toString()+ " position> " +v.getTag().toString()); 
       v.setVisibility(View.INVISIBLE); 
       DataBaseNamesHelper dbNames = new DataBaseNamesHelper(context); 
       dbNames.setFavouritesFlag(v.getTag().toString()); 
      } 

     } 



} 
+1

用戶是否真的想滾動ListView中的8000行?有沒有辦法讓他們先過濾列表? – EboMike 2010-12-02 00:33:34

+0

我有一個fastscroll與索引實施。我稍後會添加一個搜索選項來過濾。 – DArkO 2010-12-02 00:54:19

回答

25

加載適配器時間較慢的原因是CursorAdapter對Cursor.getCount()所做的內部調用。

Android中的遊標延遲加載。直到需要時纔會加載結果。當CursorAdapter調用getCount()時,這會強制查詢完全執行並計算結果。

下面是討論這個問題的幾個鏈接。

http://groups.google.com/group/android-developers/browse_thread/thread/c1346ec6e2310c0c

http://www.androidsoftwaredeveloper.com/2010/02/25/sqlite-performance/

我的建議是分裂您的查詢。在屏幕上僅加載可見列表項的數量。當用戶滾動加載下一組。非常像GMail和Market應用程序。不幸的是我沒有得心應手的例子:(

這不回答你的問題,但希望它提供了一些見解:)

2

好吧,我可以爲您提供一個愚蠢的建議,在這一點上 - 開始在一個單獨的線程將經歷整個數據庫,並創建8000行光標查詢。

在您的UI線程中,創建一個光標,將限制設置爲100左右,然後將其用於適配器。如果用戶滾動到列表的底部,則可以使用ProgressBar添加一行或指示除此之外還有更多內容。

一旦你的第二個線程完成,請更換適配器。

另外,你可以有一個數組適配器或類似的東西,在第二個線程中進行一堆查詢,每個查詢有100行,並在它們進來時將它們添加到你的數組適配器。這也會使它更容易添加在底部的一個虛擬行,告訴用戶拿着他們的馬。

一注 - 我會認爲從兩個線程同時讀取數據庫是安全的,但在寫入時要小心。我的應用程序有一個很好的bug,當我在一個單獨的線程中進行數據庫操作時,它會拋出數據庫,直到我添加了適當的鎖定(您也可以在數據庫中啓用它)。

編輯:當然,AsyncQueryHandler,我知道有這樣的事情,但我不記得名字。當然,比單獨的線程更優雅。

順便說一句 - 你如何存儲數據庫?它只是內部存儲中的常規數據庫文件嗎?

3

不知道爲什麼setAdapter應該這麼長時間。我已經做了大約5000行比那更快。但是,我通常先創建一個不帶遊標的適配器,然後用「空」適配器調用setAdapter,然後使用AsyncQueryHandler子類啓動異步查詢。然後,在onQueryComplete中,我調用適配器上的changeCursor。

1

喜我嘗試達致這與比50000rows更多。我認爲我的代碼可以爲你提供更好的結果,因爲你只有8000條記錄。

1)使用內容提供者而不是sqlite。 2)使用loadermanager回調異步加載遊標 3)搜索使用FTS表。 4)使用索引優化你的分貝。

相信我,即使使用sectionIndexer和快速滾動,它也能很好地處理8000行。

讓我知道你是否有任何疑問。

P.s如果你找到了更好的解決方案,你認爲可以處理50000行,請讓我知道。