2010-04-19 14 views
7

Estoy tratando de obtener un AutoCompleteTextView (ACTV) para mostrar los resultados que obtengo de un recurso de red. Establecí el umbral de finalización en 2 y puedo ver que la solicitud se dispara cuando ingreso caracteres.AutoCompleteTextView no muestra el resultado incluso cuando se actualiza el ArrayAdapter

El resultado que obtengo es el correcto. Digamos que escribo "ca", y obtengo el resultado "auto" como autocompletado. Tengo una función de devolución de llamada que recibe el resultado de una AsyncTask y coloca el resultado en ArrayAdapter. Luego llamo a .showDropDown() en el ACTV y se muestra un menú desplegable vacío (la mitad del tamaño de un elemento normal). Luego, si ingreso la última letra "r" y el ACTV muestra "automóvil", se muestra el menú desplegable y el resultado aparece de repente en la lista.

Lo mismo ocurre si he ingresado dos caracteres (que devuelve un resultado válido) y elimino la última letra. Cuando se elimina la letra, se muestra "automóvil" como un valor de autocompletado.

¿Alguien ha tenido este problema? Parece que el adaptador está lleno con el resultado, pero el resultado no se muestra hasta la siguiente acción que realizo. También he intentado ejecutar .notifyDataSetChanged() después de haber agregado el resultado al adaptador, pero eso no debería ser necesario, o?

+0

Poder ayuda si agrega su código. – Joe

Respuesta

17

Sin ver su código, es difícil saber qué podría estar pasando. Pero lo primero que viene a la mente es que su solicitud de red está sucediendo en un hilo diferente y, por lo tanto, su performFiltering() puede estar devolviendo un conjunto de resultados vacío prematuramente. En ese punto, publishResults() está devolviendo el resultado vacío, y su lista desplegable está vacía. Más tarde, su AsyncTask obtendrá su resultado y usted agregará los resultados a la lista del adaptador, pero por una razón u otra, aún no se muestra.

Creo que puede estar confundido acerca de la necesidad de AsyncTask. El objeto Filter ya está haciendo algo similar a AsyncTask: performFiltering() se realiza en un subproceso en segundo plano y se llama al publishResults() desde el subproceso de la interfaz de usuario, después de que se complete performFiltering(). Así que puede hacer su solicitud de red directamente en performFiltering() y establecer los resultados en el objeto FilterResults, y no tendrá que preocuparse porque la solicitud de red sea demasiado lenta y cause problemas en su UI.

Una solución alternativa, algo más complicada, pero es lo que estoy haciendo en mi objeto Filter (debido a la arquitectura existente que hace llamadas API en segundo plano, utilizando una devolución de llamada asincrónica en lugar del paso de bloqueo/síncrono requerido para performFiltering()), es usar un objeto sincronizado con wait()/notify() para hacer un monitoreo de hilos cruzados, por lo que el efecto es el mismo que hacer la solicitud de red directamente en performFiltering(), pero en realidad está sucediendo en varios subprocesos:

// in Filter class.. 
protected FilterResults performFiltering(CharSequence constraint) { 

    APIResult response = synchronizer.waitForAPI(constraint); 
    // ... 
} 

// callback invoked after the API call finishes: 
public void onAPIComplete(APIResult results) { 
    synchronizer.notifyAPIDone(results); 
} 

private class Synchronizer { 
    APIResult result; 

    synchronized APIResult waitForAPI(CharSequence constraint) { 
     someAPIObject.startAsyncNetworkRequest(constraint); 
     // At this point, control returns here, and the network request is in-progress in a different thread. 
     try { 
      // wait() is a Java IPC technique that will block execution until another 
      // thread calls the same object's notify() method. 
      wait(); 
      // When we get here, we know that someone else has just called notify() 
      // on this object, and therefore this.result should be set. 
     } catch(InterruptedException e) { } 
     return this.result; 
    } 

    synchronized void notifyAPIDone(APIResult result) { 
     this.result = result; 
     // API result is received on a different thread, via the API callback. 
     // notify() will wake up the other calling thread, allowing it to continue 
     // execution in the performFiltering() method, as usual. 
     notify(); 
    } 
} 

Sin embargo, creo que es posible que la solución más fácil es que acaba de hacer su solicitud a la red de forma sincrónica, situado en el método performFiltering(). El ejemplo de código anterior es solo una posibilidad, si ya tiene la arquitectura establecida para llamadas API asíncronas/de devolución de llamada, y no desea cambiar ese comportamiento para obtener resultados sincrónicos en performFiltering().

+0

buen trabajo Joe! ¡Creo que tienes razón en que la tarea asíncrona devuelve datos DESPUÉS de que se realice el filtrado de peform así que hay una condición de carrera! – reidisaki

+1

gracias por señalar que performFiltering() se ejecuta en una cadena de fondo. –

+0

genial pero complicado –

1

Creo que la respuesta de Joe es el camino a seguir. Sin embargo, creo que debería usar CountDownLatch en lugar de esperar/notificar.

La razón es que con wait/notify arriesga una condición de carrera si su API realmente vuelve súper rápido antes de comenzar a "esperar()" ... en este caso, notificar no tendrá efecto y esperar () esperará indefinidamente. con el cierre, el código se verá así (copiado de Joe y modificado):

// in Filter class.. 
protected FilterResults performFiltering(CharSequence constraint) { 
    APIResult response = synchronizer.waitForAPI(constraint); 
    // ... 
} 

// callback invoked after the API call finishes: 
public void onAPIComplete(APIResult results) { 
    synchronizer.notifyAPIDone(results); 
} 

private class Synchronizer { 
    APIResult result; 
    CountDownLatch latch; 

    synchronized APIResult waitForAPI(CharSequence constraint) { 
     latch = new CountDownLatch(1); 
     someAPIObject.startAsyncNetworkRequest(constraint); 
     // At this point, control returns here, and the network request is in-progress in a different thread. 
     try { 
     // Will wait till the count is 0... 
     // If the count is already 0, it'll return immediately. 
     latch.await(); 
     // When we get here, we know that someone else has just called notify() 
     // on this object, and therefore this.result should be set. 
    } catch(InterruptedException e) { } 
    return this.result; 
    } 

    synchronized void notifyAPIDone(APIResult result) { 
    this.result = result; 
    // API result is received on a different thread, via the API callback. 
    // countDown() will wake up the other calling thread, allowing it to continue 
    // execution in the performFiltering() method, as usual. 
    latch.countDown(); 
    } 
} 

Por último, no tengo crédito suficiente para publicar un comentario, de lo contrario tendría ...

Cuestiones relacionadas