2010-12-02 72 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行,请让我知道。