studio start_sticky servicios onstartcommand example ejemplo java android asynchronous intentservice

java - start_sticky - Esperando la devolución de llamada asincrónica en el servicio de intención de Android



servicios android (6)

Tengo un IntentService que inicia una tarea asincrónica en otra clase y debería estar esperando el resultado.

El problema es que IntentService terminará tan pronto como el onHandleIntent(...) haya terminado de ejecutarse, ¿no?

Eso significa que, normalmente, IntentService se cerrará inmediatamente después de iniciar la tarea asíncrona y ya no estará allí para recibir los resultados.

public class MyIntentService extends IntentService implements MyCallback { public MyIntentService() { super("MyIntentService"); } @Override protected final void onHandleIntent(Intent intent) { MyOtherClass.runAsynchronousTask(this); } } public interface MyCallback { public void onReceiveResults(Object object); } public class MyOtherClass { public void runAsynchronousTask(MyCallback callback) { new Thread() { public void run() { // do some long-running work callback.onReceiveResults(...); } }.start(); } }

¿Cómo puedo hacer que el fragmento de arriba funcione? Ya he intentado poner Thread.sleep(15000) (duración arbitraria) en onHandleIntent(...) después de comenzar la tarea. Parece funcionar.

Pero definitivamente no parece ser una solución limpia. Tal vez incluso hay algunos problemas serios con eso.

¿Alguna mejor solución?


Acepto, probablemente tenga más sentido usar Service directamente en lugar de IntentService , pero si usa Guava , puede implementar AbstractFuture como su manejador de devolución de llamada, lo que le permite ignorar convenientemente los detalles de la sincronización:

public class CallbackFuture extends AbstractFuture<Object> implements MyCallback { @Override public void onReceiveResults(Object object) { set(object); } // AbstractFuture also defines `setException` which you can use in your error // handler if your callback interface supports it @Override public void onError(Throwable e) { setException(e); } }

AbstractFuture define get() qué bloques hasta que se llaman los métodos set() o setException() , y devuelve un valor o genera una excepción, respectivamente.

Entonces su onHandleIntent convierte en:

@Override protected final void onHandleIntent(Intent intent) { CallbackFuture future = new CallbackFuture(); MyOtherClass.runAsynchronousTask(future); try { Object result = future.get(); // handle result } catch (Throwable t) { // handle error } }


Estás condenado sin cambiar MyOtherClass .

Al cambiar esa clase, tienes dos opciones:

  1. Haga una llamada sincrónica. IntentService ya está generando un Thread fondo para ti.
  2. Devuelve el Thread recién creado en runAsynchronousTask() y llama a join() en él.

Estoy de acuerdo con corsair992 en que normalmente no debería tener que realizar llamadas asincrónicas desde un servicio de intención porque IntentService ya hace su trabajo en un hilo de trabajo. Sin embargo, si debe hacerlo, puede usar CountDownLatch .

public class MyIntentService extends IntentService implements MyCallback { private CountDownLatch doneSignal = new CountDownLatch(1); public MyIntentService() { super("MyIntentService"); } @Override protected final void onHandleIntent(Intent intent) { MyOtherClass.runAsynchronousTask(this); doneSignal.await(); } } @Override public void onReceiveResults(Object object) { doneSignal.countDown(); } public interface MyCallback { public void onReceiveResults(Object object); } public class MyOtherClass { public void runAsynchronousTask(MyCallback callback) { new Thread() { public void run() { // do some long-running work callback.onReceiveResults(...); } }.start(); } }


Mi opción favorita es exponer dos métodos similares, por ejemplo:

public List<Dog> getDogsSync(); public void getDogsAsync(DogCallback dogCallback);

Entonces la implementación podría ser la siguiente:

public List<Dog> getDogsSync() { return database.getDogs(); } public void getDogsAsync(DogCallback dogCallback) { new AsyncTask<Void, Void, List<Dog>>() { @Override protected List<Dog> doInBackground(Void... params) { return getDogsSync(); } @Override protected void onPostExecute(List<Dog> dogs) { dogCallback.success(dogs); } }.execute(); }

Luego, en su IntentService puede llamar a getDogsSync() porque ya está en una getDogsSync() fondo.


Si aún está buscando maneras de utilizar el Servicio de Intento para la devolución de llamada asincrónica, puede esperar y notificar sobre el hilo de la siguiente manera:

private Object object = new Object(); @Override protected void onHandleIntent(Intent intent) { // Make API which return async calback. // Acquire wait so that the intent service thread will wait for some one to release lock. synchronized (object) { try { object.wait(30000); // If you want a timed wait or else you can just use object.wait() } catch (InterruptedException e) { Log.e("Message", "Interrupted Exception while getting lock" + e.getMessage()); } } } // Let say this is the callback being invoked private class Callback { public void complete() { // Do whatever operation you want // Releases the lock so that intent service thread is unblocked. synchronized (object) { object.notifyAll(); } } }


Utilice la clase de Service estándar Service lugar de IntentService , inicie la tarea asincrónica desde la devolución de llamada onStartCommand() y destruya el Service cuando reciba la devolución de llamada de finalización.

El problema con eso sería manejar correctamente la destrucción del Service en el caso de ejecutar simultáneamente tareas como resultado de que el Service se inicie de nuevo mientras ya se estaba ejecutando. Si necesita manejar este caso, puede necesitar configurar un contador en ejecución o un conjunto de devoluciones de llamadas, y destruir el Service solo cuando estén completos.