35

Tengo una aplicación que necesita extraer datos de un servidor e insertarlos en una base de datos SQLite en respuesta a la entrada del usuario. Pensé que esto sería bastante simple: el código que extrae los datos del servidor es una subclase bastante sencilla de AsyncTask, y funciona exactamente como lo espero sin colgar el hilo de la interfaz de usuario. He implementado la funcionalidad de devolución de llamada para ello con una interfaz sencilla, lo envolvió en una clase estática, por lo que mi código aparece así:¿La inserción de ContentProvider() siempre se ejecuta en el subproceso de interfaz de usuario?

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() { 
    @Override 
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) { 
     // do something with contents 
    } 
} 

Todo sigue siendo buena. Incluso si el servidor tarda una hora en recuperar los datos, la IU aún se ejecuta sin problemas, porque el código en getFolderContents se ejecuta en el método doInBackground de una AsyncTask (que está en un hilo separado de la IU). Al final del método getFolderContents, se llama a onFolderContentsResponse y pasa la lista de FilesystemEntry que se recibió del servidor. Solo digo todo esto para que quede claro que mi problema no está en el método getFolderContents o en ninguno de mis códigos de red, porque nunca ocurre allí.

El problema surge cuando trato de insertar en una base de datos a través de mi subclase de ContentProvider dentro del método onFolderContentsResponse; la IU siempre se cuelga mientras el código se está ejecutando, lo que me lleva a pensar que, a pesar de ser llamado desde el método doInBackground de una AsyncTask, las inserciones de alguna manera todavía se están ejecutando en el subproceso de la interfaz de usuario. Esto es lo que el código problemática se ve así:

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() { 
    @Override 
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) { 
     insertContentsIntoDB(contents); 
    } 
} 

Y el método insertContentsIntoDB:

void insertContentsIntoDB(final List<FilesystemEntry> contents) { 
    for (FilesystemEntry entry : contents) { 
     ContentValues values = new ContentValues(); 
     values.put(COLUMN_1, entry.attr1); 
     values.put(COLUMN_2, entry.attr2); 
     // etc. 

     mContentResolver.insert(MyContentProvider.CONTENT_URI, values); 
    } 
} 

donde mContentResolver se ha establecido previamente al resultado del método getContentResolver().

He intentado poner insertContentsIntoDB en su propio hilo, así:

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() { 
    @Override 
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       insertContentsIntoDB(contents); 
      } 
     }).run(); 
    } 
} 

También he intentado correr cada inserción individual en su propio hilo (el método de inserción en MyContentProvider está sincronizado, por lo que este shouldn 't causa ningún problema allí):

void insertContentsIntoDB(final List<FilesystemEntry> contents) { 
    for (FilesystemEntry entry : contents) { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       ContentValues values = new ContentValues(); 
       values.put(COLUMN_1, entry.attr1); 
       values.put(COLUMN_2, entry.attr2); 
       // etc. 
       mContentResolver.insert(MyContentProvider.CONTENT_URI, values); 
      } 
     }).run(); 
    } 
} 

Y sólo por si acaso, también he intentado tanto de esas soluciones con el código correspondiente en el método doInBackground de otro AsyncTask. Por último, he definido explícitamente MyContentProvider como viviendo en un proceso separado en mi AndroidManifest.xml:

<provider android:name=".MyContentProvider" android:process=":remote"/> 

Funciona muy bien, pero todavía parece correr en el hilo de interfaz de usuario. Ese es el punto donde realmente comencé a arrancarme el pelo por esto, porque eso no tiene ningún sentido para mí. No importa lo que haga, la IU siempre se cuelga durante las inserciones. ¿Hay alguna forma de hacer que no lo hagan?

+0

Imprima la ID/nombre de la secuencia actual de su ContentProvider para verificar su hipótesis. Y/o use el depurador y verifique los hilos en ejecución. –

Respuesta

52

En lugar de llamar al mContentResolver.insert(), use AsyncQueryHandler y su método startInsert().AsyncQueryHandler está diseñado para facilitar las consultas asincrónicas ContentResolver.

+0

Muchas gracias, ¡eso hace exactamente lo que estaba buscando! – jcsmnt0

+0

@ jcsmnt0 me alegro de que haya ayudado :) –

+0

Gracias! Estoy tan contento ahora mismo: D – passsy

0

Hombre. Relájese usted mismo. Y todo se vería mejor. Al principio, Iniciar un hilo es Func start no Func run, si desea iniciar el nuevo Thread no solo llama al func run.

new Thread(Runnable runnable).start(); 

Apuesto a que el uso de Handler a veces sería mejor que AsyncTask.

+0

controlador siempre se ejecuta en su subproceso principal y no se ejecuta en segundo plano. –

0

Puede ejecutar la consulta en el doInBackground(Integer int) método reemplazado del AsynTask y actualizar la interfaz de usuario principal en el método onPostExecute(Integer int).

3

Creo que su problema original puede haber sido que llama al método run en su nuevo hilo (lo que hace que la ejecución continúe en el hilo actual) en lugar de llamar al método start. Creo que esto es lo que Bright Great estaba tratando de decir en su respuesta. Ver Difference between running and starting a thread. Es un error común.

Cuestiones relacionadas