2010-12-02 11 views
7

大量のためのListViewアダプタでカーソルを使用します。データベースには約8.000行の2列が含まれています。だから、私はクエリとすべてのデータをできるだけ早く表示する方法を探しています。私はここでasyncTaskでこれを行っているコードです:私はSQLiteデータベースからデータを取得し、リストビューでそれを表示するには、カスタムCursorAdapterを使用していたデータ

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(); 
} 

}

これは私がアダプタに結果を設定する必要が一部を除いて良い作品。私はいくつかの時間のテストを行っており、それはstartManagingCursorを通過するためにaproxを700ミリ秒かかる。問題は、setAdapter(result)を通過させるのに約7秒かかることです。これは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

+0

私は索引付けが実装されたファストスクロールを持っています。フィルタリングするために後で検索オプションを追加します。 – DArkO

+0

いずれの場合でも、別のスレッドでクエリを実行してからアダプタに割り当てることはできますが、それでも7秒間あなたのアプリケーションは黒くなります。あなたは7秒間ユーザーを楽しませる方法がない限り? – EboMike

答えて

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を含む行を追加するか、そうでない場合は別の行を追加することができます。

2回目のスレッドが完了したら、アダプタを交換します。

また、配列アダプタなどを使用し、2行目に100行ずつのクエリをまとめて配列アダプタに追加することもできます。これにより、追加が容易になります一番下のダミー行は、ユーザーに馬を保持するよう指示します。

ワンノート - 私は同時に2つのスレッドからのデータベースから読み取ることは安全だと仮定し、それが書き込みに来るとき注意が必要でしょう。私のアプリケーションには、(データベースでも有効にできる)適切なロックを追加するまで、別のスレッドでデータベース操作を行ったときにデータベースを破棄した素晴らしいバグがありました。

EDIT:もちろん、AsyncQueryHandlerは、私はそのようなことがあった知っていたが、私は名前を覚えていませんでした。もちろん、別のスレッドよりもエレガントです。

Btw - データベースをどのように保存していますか?内部ストレージの通常のデータベースファイルですか?

+0

はいただ定期的な内部データベース。私はアプリケーション間でデータを共有する必要がないので、私はコンテンツプロバイダを使用していません。私は最初にアダプタを空にしてからアダプタのカーソルを変更しようとしていますが、これまでは空リストビューしか得られていません。私に迷惑をかけているのは、クエリが完了するのに約600ミリ秒かかりますが、リストの割り当てが7秒なので、遅い応答がクエリから正しく来ないか、何か不足していると思いますか? – DArkO

+0

素晴らしいよ、私はそれを誤解した。どんな種類のアダプターを使用していますか?私はCustomCursorAdapterを見ています - カスタム部分は何ですか? – EboMike

+0

SimpleCursorAdapterを継承しています。ここに私が作ったものがあります。 http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/ – DArkO

3

なぜsetAdapterが長くかかるかわかりません。私はそれよりもはるかに高速に約5000行を行った。しかし、私は通常、カーソルのないアダプタを作成し、setAdapterを空のアダプタで呼び出し、AsyncQueryHandlerサブクラスを使用して非同期クエリを開始します。次に、onQueryCompleteで、アダプタのchangeCursorを呼び出します。

1

こんにちはイムが50000rowsより多くのために、このacheiveしようとしています。私は私のコードはあなたには8000レコードしかないので、あなたのために良い結果を与えることができると思います。

1)sqliteの代わりにコンテンツプロバイダを使用します。 2)loadermanagerコールバックを使用してカーソルを非同期にロードする 3)検索用にFTSテーブルを使用する。 4)索引付けでur DBを最適化します。

私はそれがsectionIndexerと高速スクロールでも8000行で非常にうまく動作します。

ご不明な点がありましたら教えてください。

P.s私はあなたに50000行を処理できると思うより良い解決策を見つけたら教えてください。

関連する問題