2015-04-15 19 views
18

Tôi nhận được danh sách các ứng dụng đã cài đặt trên thiết bị. Đó là một hoạt động tốn kém, vì vậy tôi đang sử dụng Rx cho điều đó:Làm thế nào để xử lý đúng onError bên trong RxJava (Android)?

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 -> L.e(TAG, "Throwable " + throwable.getMessage())) 
      .subscribe(s -> createListView(s, view)); 

Tuy nhiên, vấn đề của tôi là xử lý lỗi. Thông thường, người dùng khởi chạy màn hình này, đợi tải ứng dụng, chọn những gì tốt nhất và chuyển đến trang tiếp theo. Tuy nhiên, khi người dùng nhanh chóng thay đổi giao diện người dùng - ứng dụng gặp sự cố với NullPointer.

OK, vì vậy, tôi đã triển khai onError này. Tuy nhiên nó vẫn không hoạt động, và với usecase ở trên nó ném tôi đây:

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) 

Làm thế nào tôi nên xử lý đúng đắn vấn đề này?

+0

Bạn đã cố định được vấn đề? – User9527

+0

là 'Ứng dụng' một mô hình tùy chỉnh? – Lobato

Trả lời

12

mất của tôi là: bạn có thể sử Action1 trong

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

Bạn sẽ cần phải thay thế nó với thuê bao hoặc Observer trong đó có phương pháp trừu tượng onError. Phương thức này sẽ được gọi từ subscriber.onError(new Throwable());

EDIT: Đây là cách tôi sẽ làm. Khi nhìn kỹ hơn, tôi nghĩ rằng vấn đề chính trong mã của bạn là phần đầu, nơi bạn gọi subscriber.onError ngay cả khi không có lỗi. Có thể bạn không cần map vì bạn đang chuyển dữ liệu một cách kỹ thuật như không có thao tác. Nhưng tôi để nó trong trường hợp nó là cần thiết sau này.

 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); 
     } 
    }); 
+1

Bạn có thể đăng một số loại ví dụ không? Tôi là loại mới trong lập trình phản ứng, do đó, hầu hết các triển khai của tôi giống như hit và bỏ lỡ;) –

+0

Tôi đã chỉnh sửa câu trả lời của mình để bao gồm một ví dụ. – inmyth

+0

Có thể thực hiện được với các biểu thức lambda không? –

4

Đây là phản ứng người mới (vì tôi mới trong javarx và cuối cùng là khắc phục vấn đề này):

Đây là thực hiện của bạ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(); 

Thực hiện trước đây, khi tôi đăng ký Tôi kích hoạt luồng lỗi ... và tôi gặp sự cố ứng dụng.

Vấn đề là bạn phải quản lý lỗi từ cuộc gọi đăng ký(). "DoOnError (...)" chỉ là một loại trợ giúp sao chép lỗi và cung cấp cho bạn một vị trí mới để thực hiện một số hành động sau khi có lỗi. Nhưng nó không xử lý lỗi.

Vì vậy, bạn cần phải thay đổi mã của bạn với điều đó:

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

Không chắc chắn về lời giải thích thật, nhưng đây là cách tôi sửa chữa nó. Hy vọng nó sẽ giúp.

6

.doOnError() là nhà điều hành và không phải là một phần của Subscriber.

Do đó, có một số .doOnError() không được tính là triển khai onError().

Về câu hỏi trong một trong các nhận xét, tất nhiên có thể sử dụng lambdas.

Trong trường hợp này chỉ cần thay thế

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

với

.subscribe(s -> createListView(s, view), 
    throwable -> L.e(TAG, "Throwable " + throwable.getMessage())) 
Các vấn đề liên quan