java - interfaces - interfaz grafica para netbeans
ContentProvider insert() siempre se ejecuta en el subproceso de interfaz de usuario? (4)
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. Implementé la funcionalidad de devolución de llamada con una interfaz simple y lo envolví en una clase estática, por lo que mi código se ve así:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
@Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
// do something with contents
}
}
Todo sigue siendo bueno 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. Así es como se ve el código problemático:
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 en el resultado del método getContentResolver ().
Intenté poner insertContentsIntoDB en su propio subproceso, 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 intenté ejecutar cada inserción individual en su propio hilo (el método de inserción en MyContentProvider está sincronizado, por lo que esto no debería causar ningún problema):
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 solo por si acaso, también probé ambas soluciones con el código relevante en el método doInBackground de otra AsyncTask. Finalmente, definí explícitamente MyContentProvider como viviendo en un proceso separado en mi AndroidManifest.xml:
<provider android:name=".MyContentProvider" android:process=":remote"/>
Funciona bien, pero parece funcionar en el hilo de UI . 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?
Creo que su problema original puede haber sido que está llamando al método de 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 de start
. Creo que esto es lo que Bright Great estaba tratando de decir en su respuesta. Vea Diferencia entre correr e iniciar un hilo . Es un error común.
En lugar de llamar a mContentResolver.insert()
, use AsyncQueryHandler
y su método startInsert()
. AsyncQueryHandler
está diseñado para facilitar consultas asincrónicas de ContentResolver
.
Hombre. Relájese. Y todo se vería mejor. Al principio, Start a Thread es Func start no Func run, si desea iniciar el nuevo Thread no solo llama al func run.
new Thread(Runnable runnable).start();
Entonces apuesto a que el uso de Handler a veces sería mejor que AsyncTask.
Puede ejecutar la consulta en el doInBackground(Integer int)
anulado de AsynTask y actualizar la interfaz de usuario principal en el onPostExecute(Integer int)
.