- MainActivity
- TabLayout + ViewPager
- ViewPagerの各ページは MainFragment
- MainFragment
- RecyclerView
やりたいことは
- MainActivity
- 各ページで共通のデータ(以後 CommonData)をサーバーから取得する
- MainFragment
- ページ特有のデータ(以後 SpecificData)をサーバーから取得する
- MainActivity から CommonData をもらう
- CommonData と SpecificData 両方の読み込みが終わったら RecyclerView にデータを追加する
キモになるのが、CommonData と SpecificData 両方の読み込みが終わるのを待ち合わせたいというところです。
CommonData の読み込みが終わる前に生成された MainFragment なら両方の読み込みを待ち合わせるし、 CommonData の読み込みが終わった後に生成された MainFragment なら SpecificData の読み込みだけ待てばいいわけです。
でも、この2つの状態をわけて処理すると煩雑になってしまいます。
そこで、BehaviorSubject を使って、CommonData の値を MainFragment 側に渡せるようにします。 BehaviorSubject は onNext() が呼ばれたときにその値を通知し、さらにその値をキャッシュします。新しく subscribe されると、最新のキャッシュした値があればその時点で通知します。
- public class MainActivity extends AppCompatActivity implements MainFragment.MainFragmentListener {
- private final BehaviorSubject<CommonData> commonDataBehaviorSubject = BehaviorSubject.create();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ...
- loadCommonData();
- }
- private void loadCommonData() {
- subscription = DataRetriever.getInstance().getCommonData()
- .onErrorReturn(new Func1<Throwable, CommonData>() {
- @Override
- public CommonData call(Throwable throwable) {
- // エラーのときはデータがないものとして扱う
- return CommonData.empty();
- }
- })
- .subscribe(new Action1<CommonData>() {
- @Override
- public void call(CommonData commonData) {
- commonDataBehaviorSubject.onNext(commonData);
- }
- });
- }
- @NonNull
- @Override
- public Observable<CommonData> getCommonDataObservable() {
- return commonDataBehaviorSubject;
- }
- }
- public class MainFragment extends Fragment {
- public interface MainFragmentListener {
- @NonNull
- Observable<CommonData> getCommonDataObservable();
- }
- @Nullable
- private MainFragmentListener listener;
- ...
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (context instanceof MainFragmentListener) {
- listener = (MainFragmentListener) context;
- }
- }
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (adapter == null) {
- adapter = new DataAdapter();
- load();
- }
- recyclerView.setAdapter(adapter);
- }
- void load() {
- recyclerView.setVisibility(View.GONE);
- progressView.setVisibility(View.VISIBLE);
- // 共通データとタブ独自のデータ両方揃うまで待ち合わせ
- subscription = Observable
- .combineLatest(
- getCommonDataObservable(),
- getSpecificDataObservable(),
- new Func2<CommonData, SpecificData, Pair<CommonData, SpecificData>>() {
- @Override
- public Pair<CommonData, SpecificData> call(CommonData commonData, SpecificData specificData) {
- return new Pair<>(commonData, specificData);
- }
- })
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber<Pair<CommonData, SpecificData>>() {
- @Override
- public void onCompleted() {
- }
- @Override
- public void onError(Throwable e) {
- recyclerView.setVisibility(View.VISIBLE);
- progressView.setVisibility(View.GONE);
- }
- @Override
- public void onNext(Pair<CommonData, SpecificData> combinedData) {
- recyclerView.setVisibility(View.VISIBLE);
- progressView.setVisibility(View.GONE);
- final List<String> list = new ArrayList<>();
- final CommonData commonData = combinedData.first;
- list.add("CommonData : " + (commonData.isEmpty() ? "empty" : commonData.getData()));
- final SpecificData specificData = combinedData.second;
- list.addAll(specificData.getData());
- adapter.addAll(list);
- }
- });
- }
- /**
- * 共通のデータを取得
- */
- private Observable<CommonData> getCommonDataObservable() {
- return listener != null
- // first() を介して onComplete()が呼ばれるようにしている
- ? listener.getCommonDataObservable().first()
- : Observable.just(CommonData.empty());
- }
- /**
- * このタブ独自のデータを取得
- */
- private Observable<SpecificData> getSpecificDataObservable() {
- final int position = getArguments() == null ? -1 : getArguments().getInt(ARGS_POSITION);
- return DataRetriever.getInstance().getSpecificData(position);
- }
- ...
- }
さらに、MainActivity に SwipeRefreshLayout を追加して、PullToRefresh で共通データを取り直し、各 MainFragment にもデータを取り直させる処理を追加したサンプルが
https://github.com/yanzm/BehaviorSubjectSample
です。
0 件のコメント:
コメントを投稿