2010-12-02 17 views
7

Estoy usando un CursorAdapter personalizado para obtener datos de una base de datos SQLite y mostrarlos en una vista de lista. La base de datos contiene 2 columnas con aproximadamente 8,000 filas. entonces estoy buscando una forma de consultar y mostrar todos los datos lo más rápido posible. Lo he hecho con AsyncTask aquí está el código:Uso del Cursor con el adaptador ListView para una gran cantidad de datos

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

}

esto funciona bien, excepto la parte en la que necesito para establecer el resultado en un adaptador. He hecho algunas pruebas de tiempo y se requieren aproximadamente 700 ms para pasar el startManagingCursor. el problema es que tarda unos 7 segundos en pasar el setAdapter (resultado) y esto se ejecuta en el subproceso UI, por lo que mi aplicación no responde (el diálogo de progreso se congela y, a veces, la aplicación). ¿Cómo hago que esta vez sea menos? ¿Puedo hacer que esto también se ejecute en segundo plano o de alguna manera para aumentar la capacidad de respuesta?

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

¿Sus usuarios realmente desean desplazarse a través de 8000 filas en un ListView? ¿Hay alguna manera de que filtren la lista primero? – EboMike

+0

tengo un fastscroll con indización implementada. y agregaré una opción de búsqueda más tarde para filtrar. – DArkO

+0

En cualquier caso, PODRÍA hacer la consulta en un hilo separado y luego asignarla al adaptador, pero eso dejaría su aplicación en negro durante 7 segundos. ¿A menos que tengas una manera de entretener al usuario durante 7 segundos? – EboMike

Respuesta

25

La razón de la lentitud en la carga del adaptador es la llamada interna que hace CursorAdapter a Cursor.getCount().

Los cursores en Android están cargados con holgura. Los resultados no se cargan hasta que se necesiten. Cuando CursorAdapter llama a getCount(), esto obliga a que la consulta se ejecute por completo y se cuenten los resultados.

A continuación se muestran un par de enlaces sobre este tema.

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

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

Mi sugerencia sería la de dividir su consulta. Cargue solo la cantidad de elementos de la lista visible en la pantalla. A medida que el usuario se desplaza, carga el siguiente conjunto. Muy parecido a las aplicaciones de GMail y Market. Por desgracia no tengo un ejemplo práctico :(

Esto no responde a su pregunta, pero es de esperar que ayuda a comprender mejor :)

2

Bueno, sólo puedo ofrecer una sugerencia tonta en este punto - iniciar una consulta en un hilo separado que pasará a través de toda la base de datos y crear el cursor 8000 filas.

En la secuencia de la interfaz de usuario, cree un cursor donde establezca el límite en 100 o más y utilícelo para su adaptador. Si el usuario se desplaza al final de la lista, puede agregar una fila con una ProgressBar o indicar lo contrario que hay más por venir.

Una vez que haya terminado su segundo hilo, reemplace el adaptador.

Como alternativa, podría tener un adaptador de matriz o algo similar, hacer un montón de consultas en el segundo subproceso con 100 filas cada una, y agregarlas a su adaptador de matriz cuando entren. Eso también haría que sea más fácil agregar una fila ficticia en la parte inferior que les dice a los usuarios que sostengan sus caballos.

Una nota: supongo que es seguro leer desde la base de datos desde dos hilos al mismo tiempo, pero tenga cuidado a la hora de escribir. Mi aplicación tenía un error maravilloso en el que descartaba la base de datos cuando tenía las operaciones de la base de datos en un hilo separado hasta que añadí el bloqueo adecuado (que también puedes habilitar en la base de datos).

EDITAR: Por supuesto, AsyncQueryHandler, sabía que había algo así, pero no podía recordar el nombre. Más elegante que un hilo separado, por supuesto.

Btw - ¿cómo está almacenando la base de datos? ¿Es solo un archivo de base de datos normal en su almacenamiento interno?

+0

sí solo una base de datos interna regular. No estoy usando un proveedor de contenido ya que no tengo la necesidad de compartir datos entre aplicaciones. Estoy tratando de configurar el adaptador primero para que se vacíe y luego para cambiar el cursor en el adaptador, pero hasta ahora no hay suerte, solo me da una lista en blanco. Lo que me molesta es que la consulta solo tarda unos 600 ms en completarse, pero la asignación de lista en 7 segundos, por lo que supongo que la respuesta lenta no proviene de la consulta correcta o ¿me falta algo? – DArkO

+0

Oh increíble, lo leí mal. ¿Qué tipo de adaptador estás usando? Veo CustomCursorAdapter, ¿cuál es la parte personalizada? – EboMike

+0

Se extiende SimpleCursorAdapter. aquí es lo que solía hacer. http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/ – DArkO

3

No estoy seguro de por qué setAdapter debería llevar tanto tiempo. He hecho unas 5000 filas mucho más rápido que eso. Sin embargo, normalmente creo primero un adaptador sin cursor, llamo a setAdapter con el adaptador "vacío" y luego comienzo una consulta asincrónica usando una subclase AsyncQueryHandler. Luego, en onQueryComplete, llamo changeCursor en el adaptador.

1

hi im tratando de lograr esto con más de 50000rows. Creo que mi código puede darte mejores resultados ya que solo tienes 8000 registros.

1) use el proveedor de contenido en lugar de sqlite. 2) use las devoluciones de llamada de loadermanager para cargar el cursor de forma asíncrona 3) para buscar utilizar tablas FTS. 4) optimizar tu ur db con la indexación también.

créeme, funciona muy bien con 8000 filas incluso con sectionIndexer y con desplazamiento rápido también.

hágamelo saber si tiene alguna duda.

P.s si encontraste una mejor solución, que crees que puede manejar 50000 filas, házmelo saber.

Cuestiones relacionadas