Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage a DialogFragment with RxJava?

I've been trying to determine if it's possible to create an observable DialogFragment. Essentially I want to be able to:

  • Create and show a DialogFragment
  • Get back an rx Observable which can be subscribed to for the result (ok/cancel pressed, String input, background task success/failure, etc.)
  • Properly handle configuration change

So far the closest thing I've found is ReactiveDialog, which used to be part of RxAndroid, but has been removed from RxAndroid in v1.0.0 as a part of simplifying RxAndroid.

While ReactiveDialog does appear to meet my first two criteria, it does not appear to handle configuration change. There are two issues to consider:

  1. The DialogFragment must maintain its Observable across configuration change so it can notify subscribers of its state.
  2. The subscriber(s) must be able to either hold on to their subscription or re-subscribe after a configuration change (obviously without producing a memory leak).

I'm still fairly new to RxJava, so I'm still trying to wrap my head around how you would manage something like this. It seems like it should be possible, but I feel like it would require a static or singleton Observable manager and possibly retainedInstance DialogFragments.

Anyone have any suggestions or best practices for this?

like image 781
ashughes Avatar asked Sep 05 '15 22:09

ashughes


2 Answers

There are two issues here; one is that you don't want to lose Java Objects during relayout - look into the runtime changes docs about that.

The other issue is that you want to create an Observable that has the action of the dialog, when that action is triggered. For that, have a look at the RxJava docs, the Asynchronous Observer example. You will need to create an Observable.OnSubscribe, and pass that Subscriber to your code that will call the necessary onNext/onError/onCompleted calls.

like image 131
Tassos Bassoukos Avatar answered Oct 14 '22 10:10

Tassos Bassoukos


I would use a ViewModel for the dialog which helps with configuration changes. After a configuration change re-subscribe to the dialog's ViewModel.

1. Components

  • Screen (Activity/Fragment) - This will display the dialog fragment
  • DialogFragment - The dialog. Will publish updates about User's actions.
  • DialogViewModel - holds the User's actions stream

2. Implementation

SimpleActivity

public class SimpleActivity extends AppCompatActivity {

    private SimpleDialogViewModel dialogViewModel;
    private CompositeDisposable compositeDisposable;

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

        dialogViewModel = ViewModelProviders.of(this).get(SimpleDialogViewModel.class);
        compositeDisposable = new CompositeDisposable();

        showDialog();
    }

    @Override
    protected void onResume() {
        super.onResume();

        Disposable disposable =
        dialogViewModel
                .actionStream()
                .subscribe(
                        result -> {

                            if (AlertDialog.BUTTON_POSITIVE == result) {
                                // User clicked yes
                            }

                            if (AlertDialog.BUTTON_NEGATIVE == result) {
                                // User clicked no
                            }

                        }
                );
        compositeDisposable.add(disposable);
    }

    @Override
    protected void onPause() {
        super.onPause();

        compositeDisposable.clear();
    }

    private void showDialog() {
        SimpleDialogFragment dialogFragment = new SimpleDialogFragment();
        dialogFragment.show(getSupportFragmentManager(), SimpleDialogFragment.TAG);
    }
}

SimpleDialogFragment

public class SimpleDialogFragment extends DialogFragment {

    public static final String TAG = "SimpleDialogFragment";

    private SimpleDialogViewModel dialogViewModel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        dialogViewModel = ViewModelProviders.of(getActivity()).get(SimpleDialogViewModel.class);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_simple_message, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        final View btnYes = view.findViewById(R.id.yes);
        final View btnNo = view.findViewById(R.id.no);

        btnYes.setOnClickListener(v -> dialogViewModel.onClickYes());
        btnNo.setOnClickListener(v -> dialogViewModel.onClickNo());
    }
}

SimpleDialogViewModel

public class SimpleDialogViewModel extends ViewModel {

    private Subject<Integer> actionSubject;

    SimpleDialogViewModel() {
        actionSubject = PublishSubject.create();
    }

    public void onClickYes() {
        actionSubject.onNext(AlertDialog.BUTTON_POSITIVE);
    }

    public void onClickNo() {
        actionSubject.onNext(AlertDialog.BUTTON_NEGATIVE);
    }

    public Observable<Integer> actionStream() {
        return actionSubject;
    }
}
like image 28
pumnao Avatar answered Oct 14 '22 09:10

pumnao