2016-02-22 17 views
16

Ho avuto un problema strano in una delle mie attività. Quando torno da una foto o un video, nel mio onActivityResult sto visualizzando una finestra di dialogo che consente all'utente di nominare la telecamera. Una volta che l'utente preme OK, invio onNext() a un oggetto con il nome del file richiesto che copia il file (e mostra la finestra di avanzamento).Osservabili corse sul thread principale anche se subscribeOn() viene chiamato su un altro thread

Per qualche motivo la funzione map() che fa la copia viene sempre chiamata sul thread principale, anche se chiamo 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); 
       } 
      }); 
} 

Modifica della subscribeOn(Schedulers.io()) chiamata observeOn(Schedulers.io()) risolto il problema. Ancora mi piacerebbe sapere perché non ha funzionato ...

risposta

33

subscribeOn e observeOn sono gli operatori per lo più confusi che ci sono. Il primo si assicura che gli effetti collaterali dell'abbonamento avvengano sullo schedulatore specificato (thread), ma ciò non significa che i valori appariranno anche su quel thread.

Ad esempio, se il tuo Observer apre una connessione di rete quando ci si iscrive ad esso, non si vuole che venga eseguito sul thread principale, quindi è necessario subscribeOn per specificare dove tale abbonamento e quindi la connessione di rete sarà creato.

Quando i dati arrivano finalmente, il thread di emissione può essere qualsiasi cosa, uno degli scheduler o uno sfondo vecchio thread normale. Dal momento che non conosciamo o non ci piace quel thread, vogliamo spostare l'osservazione dei dati su un altro thread. Questo è ciò che osservaOn fa: assicura che gli operatori dopo di esso eseguano la loro logica onNext sullo scheduler specificato. Gli sviluppatori di Android lo usano già per spostare l'osservazione dei valori sul thread principale.

Ciò che raramente ha spiegato se è ciò che accade quando si vuole un po 'di calcolo in più fuori il thread principale prima che le terre risultato finale sul thread principale di nuovo: uso multiplo observeOn operatori:

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

Questo è qualcosa di nuovo per me. Usando RxJava da un po 'di tempo, sono sicuro che il thread 'subscribeOn' si applica a tutti gli operatori downstream fino al primo' observOn' o ad un operatore con uno schedulatore specificato. Non è vero? In questo caso qual è la regola per la selezione del thread dopo 'subscribeOn'? – AndroidEx

+0

Gli effetti di 'subscribeOn' vanno a monte e più vicini all'origine degli eventi. Pensalo come questo. hai una fonte ma vuoi iscriverti su una discussione diversa. Scriveresti 'new Thread (() -> source.subscribe (s)). Start();'. Qualcosa di simile accade in "subscribeOn". Hai scelto lo scheduler in base a ciò che accade quando ti iscrivi a quella fonte. Se apre immediatamente una connessione di rete o inizia a leggere un file lungo, vai per 'Schedulers.io()'. Se inizia a calcolare pesantemente qualcosa, fai 'computation()'. – akarnokd

+3

Grazie. Anche se ero più interessato a ciò che succede dopo subscribeOn, a quale thread viene applicato il prossimo operatore. Dalle tue parole ho avuto l'impressione che non è garantito che sia il thread specificato in subscribeOn. – AndroidEx

Problemi correlati