2010-12-02 7 views
7

사용자 지정 CursorAdapter를 사용하여 SQLite 데이터베이스에서 데이터를 가져 와서 listview에 표시하고 있습니다. 데이터베이스에는 약 8.000 개의 행이있는 2 개의 열이 있습니다. 그래서 나는 가능한 한 빨리 쿼리하고 모든 데이터를 보여주는 방법을 찾고있다. 여기 AsyncTask를 함께이 작업을 수행 한 코드는 다음과 같습니다많은 양의 데이터에 대해 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(); 
} 

}

내가 어댑터에 결과를 설정해야 할 때이 부분을 제외하고 잘 작동합니다. 나는 약간의 시간 테스트를했으며, startManagingCursor를 지나치게 만들기 위해 aprox 700ms가 걸린다. 문제는 setAdapter (결과)를 지나치는 데 약 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가 포함 된 행을 추가하거나 더 많은 행이 있음을 나타낼 수 있습니다.

두 번째 스레드가 완료되면 어댑터를 교체하십시오.

또는 배열 어댑터 나 비슷한 것을 가질 수 있으며 두 번째 스레드에서 각각 100 개의 행을 사용하여 쿼리를 묶어 배열 어댑터에 추가합니다. 그러면 또한 추가하기가 더 쉽습니다. 바닥에있는 말단 줄은 사용자에게 말을 들으라고 알려줍니다.

한 노트 - 동시에 두 개의 스레드에서 데이터베이스를 읽는 것이 안전하다고 생각하지만 글을 쓸 때는주의해야합니다. 내 응용 프로그램은 적절한 잠금 (데이터베이스에서 활성화 할 수 있음)을 추가 할 때까지 별도의 스레드에서 데이터베이스 작업을 수행 할 때 데이터베이스를 휴지통으로 버리는 멋진 버그가있었습니다.

편집 : 물론 AsyncQueryHandler와 같은 것을 알고 있었지만 이름을 기억할 수 없었습니다. 물론 별도의 스레드보다 더 우아합니다.

Btw - 데이터베이스를 어떻게 저장하고 있습니까? 내부 저장소의 일반 데이터베이스 파일일까요?

+0

그렇다. 나는 응용 프로그램간에 데이터를 공유 할 필요가 없으므로 콘텐츠 공급자를 사용하지 않습니다. 먼저 비어있는 어댑터를 설정하려고합니다. 그리고 어댑터의 커서를 변경하려고합니다. 그러나 지금까지는 비어있는 listView 만 제공합니다. 저를 괴롭히는 것은 쿼리가 완료하는 데 약 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 노력을 제공합니다. 내 코드는 u가 8000 개의 레코드 만 가지고 있기 때문에 더 나은 결과를 줄 수 있다고 생각합니다.

1) sqlite 대신 콘텐츠 공급자를 사용하십시오. 2) loadermanager 콜백을 사용하여 커서를 비동기 적으로로드합니다. 3) 검색을 위해 FTS 테이블을 사용합니다. 4) 색인 생성을 통해 ur db를 최적화하십시오.

날 믿어도 sectionIndexer 및 빠른 스크롤 너무 8000 행을 아주 잘 작동합니다.

나는 의심이 있는지 알려 주시기 바랍니다.

P.s 만약 내가 50000 행을 처리 할 수있는 더 나은 해결책을 찾으면 알려 주시기 바랍니다.

관련 문제