2016-02-22 9 views
16

Mam dziwny problem w jednym z moich działań. Po powrocie z robienia zdjęcia/wideo w moim onActivityResult wyświetlam okno dialogowe, które pozwala użytkownikowi nazwać kamerę. Po naciśnięciu przycisku OK, wysyłam onNext() do tematu z żądaną nazwą pliku, który kopiuje plik (i pokazuje okno dialogowe postępu).Obserwowalne działa na głównym wątku, mimo że subscribeOn() jest wywoływana w innym wątku

Z jakiegoś powodu funkcja map(), która wykonuje kopię, jest zawsze wywoływana w głównym wątku, mimo że nazywam ją subscribeOn(Schedulers.io()).

@Override 
protected void onActivityResult(final int requestCode, int resultCode, Intent intent) { 
    ... 

    final PublishSubject<String> subject = PublishSubject.create();` 

    mSubscription = subject 
      .subscribeOn(Schedulers.io()) 
      .map(new Func1<String, String>() { 
       @Override 
       public String call(String fileName) { 
        Log.I.d(TAG,"map"); 
        return doSomeIOHeavyFuncition(); 
       } 
      }) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(new Action1<String>() { 
       @Override 
       public void call(final String fullPath) { 
        Log.d(TAG,"onNext"); 
        doSomethingOnUI(fullPath); 

        subject.onCompleted(); 
       } 
      }, new Action1<Throwable>() { 
       @Override 
       public void call(Throwable throwable) { 
        ... 
       } 
      }, new Action0() { 
       @Override 
       public void call() { 
        ... 
       } 
      }); 

    final AlertDialog dialog = new AlertDialog.Builder 
    .... 
    .create() 
      .show(); 

    dialog.getButton(DialogInterface.BUTTON_POSITIVE) 
      .setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View view) { 
        String someString = getStringFromDialog(dialog); 

        dialog.dismiss(); 
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 
        imm.hideSoftInputFromWindow(input.getWindowToken(), 0); 

        showProgressDialog(); 
        subject.onNext(someString); 
       } 
      }); 
} 

Zmiana subscribeOn(Schedulers.io()) wezwanie do observeOn(Schedulers.io()) rozwiązać ten problem. Nadal chciałbym wiedzieć, dlaczego to nie działa ...

Odpowiedz

33

subscribeOn i observeOn jest głównie mylić operatorów istnieją. Ten pierwszy zapewnia, że ​​efekty subskrypcji występują w określonym harmonogramie (wątku), ale to nie znaczy, że wartości pojawią się również w tym wątku.

Na przykład, jeśli Twój obserwator otwiera połączenie sieciowe, gdy się go subskrybuje, nie chcesz, aby działał w głównym wątku, w związku z tym potrzebujesz subskrybowania, aby określić miejsce, w którym subskrypcja, a tym samym połączenie sieciowe, będzie stworzony.

Po otrzymaniu danych wątek emitujący może być dowolnym elementem, jednym z planistów lub zwykłym starym wątkiem tła. Ponieważ nie znamy lub nie lubimy tego wątku, chcemy przenieść obserwację danych do innego wątku. Oto co zaobserwuje: upewnia się, że operatorzy po nim wykonają swoją logikę onNext na określonym harmonogramie. Twórcy Androida używają go już do przeniesienia obserwacji wartości z powrotem do głównego wątku.

Co rzadko wyjaśnił jednak, co się dzieje, gdy chcesz jakieś dodatkowe obliczenia poza głównym wątku przed ostatecznym ziem Wynik na głównym wątku jeszcze: Korzystanie z wielu observeOn operatorzy:

source 
.observeOn(Schedulers.computation()) 
.map(v -> heavyCalculation(v)) 
.observeOn(Schedulers.io()) 
.doOnNext(v -> { saveToDB(v); }) 
.observeOn(AndroidSchedulers.mainThread()) 
... 
+1

To coś nowego dla mnie. Używając RxJava przez jakiś czas byłem pewien, że wątek 'subscribeOn' dotyczy wszystkich operatorów niższego rzędu do pierwszego' observeOn' lub operatora z określonym harmonogramem. Czy to nie prawda? W takim przypadku jaka jest reguła wyboru wątku po 'subscribeOn'? – AndroidEx

+0

Efekt "subscribeOn" idzie w górę i w dół do źródła zdarzeń. Pomyśl o tym w ten sposób. masz źródło, ale chcesz zasubskrybować go w innym wątku. Napisałeś 'new Thread (() -> source.subscribe (s)). Start();'. Coś takiego dzieje się w 'subscribeOn'. Wybrałeś harmonogram na podstawie tego, co się stanie, gdy zasubskrybujesz to źródło. Jeśli natychmiast otworzy połączenie sieciowe lub rozpocznie czytanie długiego pliku, przejdziesz do 'Schedulers.io()'. Jeśli coś zaczyna się mocno kompresować, robisz 'computation()'. – akarnokd

+3

Dziękuję. Chociaż bardziej interesowało mnie to, co dzieje się po zasubskrybowaniu, w jakim wątku jest stosowany następny operator. Od twoich słów mam wrażenie, że nie ma gwarancji, że będzie to wątek określony w subskrybowaniu. – AndroidEx

Powiązane problemy