2016-11-28 3 views
2

のAndroidに精通し、および/またはFirebase開発されていないもののための簡単な紹介:Androidの開発では特定のスレッドから常にコールバックを呼び出すリスナを、subscribeOnで定義されたスケジューラに準拠するObservableにラップする方法はありますか?

、あなたは常にメインスレッド(とも呼ばれるUIスレッド)からアプリケーションのビューを操作する必要がありますが、あなたのアプリケーションがする必要がある場合いくつかの重い処理を行うには、バックグラウンドスレッドotherwise the app would seem unresponsiveを使用する必要があります。

Firebaseは、クラウド内のNoSQLデータベースにデータを保存して同期する方法を提供するサービスです。また、このデータベースを管理するためのAndroid SDKも提供しています。このSDKを使用してクエリなどの操作を行うたびに、Firebaseはその内部のバックグラウンドスレッドで大量の処理を行い、常にコールバックを呼び出すことで、これらのスレッドの落とし穴を回避します。on the main thread

例:

Query postsQuery = FirebaseDatabase.getInstance().getReference("posts"); 

ValueEventListener postListener = new ValueEventListener() { 
    @Override 
    public void onDataChange(DataSnapshot dataSnapshot) { 
    // This is always called on the main thread 
    // Get Post object and use the values to update the UI 
    Post post = dataSnapshot.getValue(Post.class); 
    // ... 
    } 

    @Override 
    public void onCancelled(DatabaseError databaseError) { 
    // Getting Post failed, log a message 
    printError(databaseError.toException()); 
    // ... 
    } 
}; 

postsQuery.addValueEventListener(postListener); 

私が直面しています実際の問題:

を私はこのような方法を使用してRxJavaでFirebaseのクエリリスナーをラップしようとしている:

private static Observable<DataSnapshot> queryObservable(final Query query) { 
    return Observable.fromEmitter(emitter -> { 
    // This is called on the Scheduler's thread defined with .subscribeOn() 
    printThread("emitter"); 
    final ValueEventListener listener = new ValueEventListener() { 
     @Override public void onDataChange(final DataSnapshot dataSnapshot) { 
     // This is always called on the main thread 
     printThread("onDataChange"); 
     emitter.onNext(dataSnapshot); 
     } 

     @Override public void onCancelled(final DatabaseError databaseError) { 
     // This is called on the main thread too 
     emitter.onError(databaseError.toException()); 
     } 
    }; 

    query.addValueEventListener(listener); 

    emitter.setCancellation(() -> query.removeEventListener(listener)); 
    }, Emitter.BackpressureMode.BUFFER); 
} 

ObservableがFirebaseのコールバック(メインスレッドで呼び出された)の中からアイテムを放出しているので、さらに.subscribeOn()演算子は無視されます。

Query postsQuery = FirebaseDatabase.getInstance().getReference("posts"); 

queryObservable(postsQuery).doOnSubscribe(() -> printThread("onSubscribe")) 
    .subscribeOn(Schedulers.io()) 
    .subscribe(dataSnapshot -> printThread("onNext")); 

次を印刷します::

onSubscribe Thread: RxIoScheduler-2 
emitter Thread: RxIoScheduler-2 
onDataChange Thread: main 
onNext Thread: main 

私が理解から、FirebaseのSDKは、独自のonDataChange()コールバックやスイッチを呼び出したときに、このような上記の方法は、呼び出し例えば

、メインスレッドへの内部バックグラウンドスレッドは、Observableがメインスレッド上で新しいアイテムを発行するため、.subscribeOn()オペレータはストリームをダウンさせることになります。

実際の質問:

私は何をするだけでなく、正しく観察可能にこのようなリスナーを包むだけでなく、彼らは.subscribeOn()によって定義されたスケジューラに適合させるために何ができますか?

ありがとうございました!

更新:

私は.observeOn()は私に別のスレッドにFirebaseによって返されたデータを処理することができます知っています。それは私がすでにやっていることですが、この質問のポイントではありません。ポイントは:私は.subscribeOn()を介してスケジューラを渡すとき、私はそのスケジューラのスレッドに準拠することを期待していますが、Observableが別のスレッドのコールバックからトリガされている内部リスナを持っている場合は起こりません。それが起こると、私は.subscribeOn()保証を失う。

この問題の重大度は、最初は明らかに見えないかもしれませんが、Observableがライブラリの一部だった場合はどうなりますか?そこでのベストプラクティスは何ですか?ライブラリは、そのメソッドへの呼び出しの後に常に.observeOn()を呼び出すようにクライアントを強制すべきですか?図書館は.observeOn()と呼ばれ、それを「デフォルトスケジューラー」と呼ぶべきでしょうか?これらのケースのいずれかで、.subscribeOn()はちょうど役に立たず、それは私にとって正しいようには見えません。

+1

クエリの後に 'observeOn'を呼び出して下流のスレッドを強制的に変更するオプションしかないようです。 –

+0

@maxostこれは実際に私が使用している現在の回避策です。問題は、 '.subscribeOn()'で定義されたスケジューラをストリームをさらに下にして無視するということです。これは 'queryObservable()'メソッドを悪いAPIの上に置くことになります。私はそれに適切な解決を望んでいます。 –

+0

@RenanFerrariあなたはこれに対する解決策を見つけましたか? Thnx –

答えて

0

メインスレッドでobserveOnをIOに、subscribeOnをメインスレッドで使用すると、MainThreadで受け取ったデータを管理し、firebaseワークを別のThreadに移動できます。

はあなたのGradle(RxjavaまたはRxJava 2)にrxAndroidをインポートすることを忘れないでください:

RxJava:

compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 

はまた、次のライブラリの1あなたが参照として確認します(または単にそれを使用)することをお勧め: https://github.com/nmoskalenko/RxFirebase

RxJava 2.0:https://github.com/FrangSierra/Rx2Firebaseそれらの

一つはRxJavaとRxJavaの新しいRCと他の1で動作します2.0。興味があれば、hereとの違いを見ることができます。