Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Cómo manejar correctamente onError dentro de RxJava (Android)?

Estoy recibiendo una lista de aplicaciones instaladas en el dispositivo. Es una operación costosa, así que estoy usando Rx para eso:

Observable<List> observable = Observable.create(subscriber -> { List result = getUserApps(); subscriber.onNext(result); subscriber.onError(new Throwable()); subscriber.onCompleted(); }); observable .map(s -> { ArrayList<String> list = new ArrayList<>(); ArrayList<Application> applist = new ArrayList<>(); for (Application p : (ArrayList<Application>) s) { list.add(p.getAppName()); applist.add(p); } return applist; }) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .doOnError(throwable -> Le(TAG, "Throwable " + throwable.getMessage())) .subscribe(s -> createListView(s, view)); 

Sin embargo, mi problema es con el manejo de errores. Normalmente, el usuario inicia esta pantalla, espera que las aplicaciones se carguen, selecciona lo que es mejor y pasa a la siguiente página. Sin embargo, cuando el usuario cambia rápidamente la interfaz de usuario se bloquea con NullPointer.

Bueno, así que implementé esto onError . Sin embargo, todavía no funciona, y por encima usecase me lanza esto:

  04-15 18:12:42.530 22388-22388/pl.digitalvirgo.safemob E/AndroidRuntime﹕ FATAL EXCEPTION: main java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling. at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:52) at android.os.Handler.handleCallback(Handler.java:730) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:5419) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862) at dalvik.system.NativeStart.main(Native Method) Caused by: rx.exceptions.OnErrorNotImplementedException at rx.Observable$31.onError(Observable.java:7134) at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:154) at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111) at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) at rx.internal.operators.NotificationLite.accept(NotificationLite.java:147) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:177) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.access$000(OperatorObserveOn.java:65) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:153) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47)            at android.os.Handler.handleCallback(Handler.java:730)            at android.os.Handler.dispatchMessage(Handler.java:92)            at android.os.Looper.loop(Looper.java:176)            at android.app.ActivityThread.main(ActivityThread.java:5419)            at java.lang.reflect.Method.invokeNative(Native Method)            at java.lang.reflect.Method.invoke(Method.java:525)            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)            at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.Throwable at pl.digitalvirgo.safemob.fragments.wizard.ApplicationsFragment.lambda$getAppList$25(ApplicationsFragment.java:267) at pl.digitalvirgo.safemob.fragments.wizard.ApplicationsFragment.access$lambda$2(ApplicationsFragment.java) at pl.digitalvirgo.safemob.fragments.wizard.ApplicationsFragment$$Lambda$3.call(Unknown Source) at rx.Observable$1.call(Observable.java:145) at rx.Observable$1.call(Observable.java:137) at rx.Observable.unsafeSubscribe(Observable.java:7304) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390) at java.util.concurrent.FutureTask.run(FutureTask.java:234) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:153) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:267) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573) at java.lang.Thread.run(Thread.java:841) 

¿Cómo debo manejar correctamente este problema?

  • Robolectric + OkHttp + retrofit + prueba de unidad rxJava
  • ¿Cómo se muestra spinner si RxJava observable toma mucho tiempo?
  • Patrón de repositorio con SqlBrite / SqlDelight (base de datos sin conexión) y Retrofit (solicitud Http)
  • Encadenamiento de RxJava observables con callbacks / listeners
  • Rx java retrofit 2 manejo de errores
  • ¿Existe una regla de proguard válida para RxJava y FasterXML?
  • Solicitudes HTTP periódicas con RxJava y Retrofit
  • RxAndroid y Retrofit: No se puede crear el adaptador de llamada para io.reactivex.Observable <retrofit2.Response <okhttp3.ResponseBody >>
  • 3 Solutions collect form web for “Cómo manejar correctamente onError dentro de RxJava (Android)?”

    Mi opinión es: Probablemente utilice Action1 en

     .subscribe(s -> createListView(s, view)); 

    Deberá reemplazarlo por Suscriptor u Observador que tenga método abstracto onError . Este método será llamado desde subscriber.onError(new Throwable());

    EDIT: Así es como lo haría. Al mirar más de cerca, creo que el principal problema en su código es la primera parte en la que llama a subscriber.onError incluso cuando no hay error. Es probable que no necesite el map ya sea porque está técnicamente pasando los datos tal como está sin manipulación. Pero lo dejé en caso de que sea necesario más tarde.

      Observable.create(new Observable.OnSubscribe<Application>() { @Override public void call(Subscriber<? super Application> subscriber) { List result = getUserApps(); if (result != null){ for (Application app : result){ subscriber.onNext(app); } subscriber.onComplete(); }else{ subscriber.onError(new IOException("no permission / no internet / etc")); //or if this is a try catch event you can pass the exception } } }) .subscribeOn(Schedulers.io())//the thread *observer* runs in .observeOn(AndroidSchedulers.mainThread())//the thread *subscriber* runs in .map(new Func1<Application, String>() { // Mapping methods are where data are manipulated. // You can simply skip this and //do the same thing in Subscriber implementation @Override public String call(Application application) { return application.getName(); } }).subscribe(new Subscriber<String>() { @Override public void onCompleted() { Toast.makeText(context, "completed", Toast.LENGTH_SHORT).show(); //because subscriber runs in main UI thread it's ok to do UI stuff //raise Toast, play sound, etc } @Override public void onError(Throwable e) { Log.e("getAppsError", e.getMessage()); //raise Toast, play sound, etc } @Override public void onNext(String s) { listAdapter.add(s); } }); 

    Aquí está la respuesta del novato (porque soy nuevo en javarx y finalmente soluciono este problema):

    Aquí está su implementación:

      Observable.create(new Observable.OnSubscribe<RegionItem>() { @Override public void call(Subscriber<? super RegionItem> subscriber) { subscriber.onError(new Exception("TADA !")); } }) .doOnNext(actionNext) .doOnError(actionError) .doOnCompleted(actionCompleted) .subscribe(); 

    En esta implementación anterior, cuando me suscribo, activar el flujo de errores … y obtengo un bloqueo de aplicación.

    El problema es que Tienes que administrar el error de la llamada subscribe (). El "doOnError (…)" es sólo una especie de ayudante que clona el error y te da un nuevo lugar para hacer algo de acción después de un error. Pero no maneja el error.

    Así que tienes que cambiar tu código con eso:

      Observable.create(new Observable.OnSubscribe<RegionItem>() { @Override public void call(Subscriber<? super RegionItem> subscriber) { subscriber.onError(new Exception("TADA !")); } }) .subscribe(actionNext, actionError, actionCompleted); 

    No estoy seguro de la verdadera explicación, pero así es como lo arreglo. Espero que ayude.

    .doOnError() es un operador y no forma parte del Subscriber .

    Por lo tanto, tener un .doOnError() no cuenta como un onError() implementado.

    Acerca de la pregunta en uno de los comentarios, por supuesto, es posible utilizar lambdas.

    En este caso simplemente reemplace

     .doOnError(throwable -> Le(TAG, "Throwable " + throwable.getMessage())) .subscribe(s -> createListView(s, view)) 

    con

     .subscribe(s -> createListView(s, view), throwable -> Le(TAG, "Throwable " + throwable.getMessage())) 
    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.