Search code examples
androidrx-javaandroid-dialogfragment

RXJava and DialogFragment => Dialog waits for observable - why?


I show a DialogFragment and load data in the background. I want the dialog to show up, the data keeps on loading and incrementally updates the already visible dialog.

Problem

The dialog is not shown before the observable completes. Why?

Code

private final static Handler mHandler = new Handler();

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    Observable<List<Data>> observable =  Observable.create(new Observable.OnSubscribe<Data>()
    {
        @Override
        public void call(Subscriber<? super Data> subscriber)
        {

            // iterate and create data
            for (int i = resolveInfo.size() - 1; i >= 0; i--)
            {
                 Data d = ...;
                 subscriber.onNext(d);
            }

            subscriber.onCompleted();
        }
    })
            .onBackpressureBuffer()
            .buffer(150, TimeUnit.MILLISECONDS)
            .filter(new Func1<List<Data>, Boolean>()
            {
                @Override
                public Boolean call(List<Data> rxSingleDatas)
                {
                    return rxSingleDatas.size() > 0; // only deliver not emtpy lists!
                }
            })
            .onBackpressureBuffer()
            .doOnNext(new Action1<List<Data>>()
            {
                @Override
                public void call(List<Data> shareDatas)
                {
                    // add result to list + notify adapter
                    for (int i = 0; i < shareDatas.size(); i++)
                        mData.add(shareData);
                    if (mAdapter != null)
                        mAdapter.notifyDataSetChanged();
                }
            })
            .doOnCompleted(new Action0()
            {
                @Override
                public void call()
                {
                    // flag to indicate all data is loaded
                    mReady = true;
                }
            });

    // subscribe on handler thread for async execution
    // observe on main thread for updating UI
    mSubscription = observable
            .subscribeOn(HandlerScheduler.from(mHandler))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe();
}

@Override
public void onDestroy()
{
    if (mSubscription != null)
        mSubscription.unsubscribe();
    super.onDestroy();
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
    MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity())
            .title(R.string.share_with)
        .cancelable(true)
        .autoDismiss(false);

    mAdapter = ...;
    builder
            .adapter(mAdapter, new MaterialDialog.ListCallback() {
                @Override
                public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence charSequence) {

                    if (!mReady)
                        return;

                    // handle click
                }
            });

    MaterialDialog dlg = builder.build();
    return dlg;
}

EDIT

I replaced the handler with one that's running on the background thread, but this leads to following execption: CalledFromWrongThreadException when trying to update the adapter in the doOnNext call... I would say the observeOn should have the effect that the doOnNext is called on the main thread, but it seems, that this does not work...


Solution

  • There was a little confusion about how subscribeOn and observeOn operators work.

    subscribeOn() applies the thread to the generating part (Observable.create() here) and whatever goes after it until the first observeOn() or an operator that operates on a specific thread by design. Thus, observeOn switches the thread for operations downstream of it in the chain. RxJava doesn't automatically switch threads until explicitly told to.