Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Architecture SingleLiveEvent and EventObserver Practicle Example in Java

I try to make sample login page with two fields (username, password) and save button with android architecture component, using android data binding, validating the data in viewmodel and from view model I make call to repository for remote server call as mentioned in official doc, remote server return me userid with success so how can I start new fragment from view model using this success? I learn something about singleLiveEvent and EventObserver, but I'm not able to find there clear usage example:

LoginViewModel

private MutableLiveData<String> snackbarStringSingleLiveEvent= new MutableLiveData<>();

@Inject
public LoginViewModel(@NonNull AppDatabase appDatabase, 
                      @NonNull JobPortalApplication application,
                      @NonNull MyApiEndpointInterface myApiEndpointInterface) {
    super(application);
    loginRepository = new LoginRepository(application, appDatabase, myApiEndpointInterface);
    snackbarStringSingleLiveEvent = loginRepository.getLogin(username.get(), password.get(), type.get());
}

public MutableLiveData<String> getSnackbarStringSingleLiveEvent() {
    return snackbarStringSingleLiveEvent;
}

Repository

public SingleLiveEvent<String> getLogin(String name, String password, String type) {
    SingleLiveEvent<String> mutableLiveData = new SingleLiveEvent<>();
    
    apiEndpointInterface.getlogin(name, password, type).enqueue(new Callback<GenericResponse>() {
        @Override
        public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
            mutableLiveData.setValue(response.body().getMessage());
        }

        @Override
        public void onFailure(Call<GenericResponse> responseCall, Throwable t) {
            mutableLiveData.setValue(Constant.FAILED);
        }
    });

    return mutableLiveData;
}

Login Fragment

private void observeViewModel(final LoginViewModel viewModel) {
    // Observe project data
    viewModel.getSnackbarStringSingleLiveEvent().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
        }
    });
}

How can I use EventObserver in above case? Any practical example?

like image 638
K Guru Avatar asked May 10 '19 06:05

K Guru


People also ask

What is Android SingleLiveEvent?

SingleLiveEvent is a subclass of MutableLiveData with a single Observer Observing it at a time, hence it is aware of view's lifecycle.

Why use flow instead of LiveData?

StateFlow and LiveData have similarities. Both are observable data holder classes, and both follow a similar pattern when used in your app architecture. The StateFlow and LiveData do behave differently: StateFlow requires an initial state to be passed into the constructor, while LiveData does not.

What is SingleLiveData?

SingleLiveData. It is a lifecycle-aware observable that sends only new updates after subscription, used for events like navigation and Snackbar messages. livedata-ktx has different implementation comparing to SingleLiveEvent from google samples android-architecture.


2 Answers

Check out below example about how you can create single LiveEvent to observe only one time as LiveData :

Create a class called Event as below that will provide our data once and acts as child of LiveData wrapper :

public class Event<T> {
    private boolean hasBeenHandled = false;
    private T content;

    public Event(T content) {
        this.content = content;
    }

    public T getContentIfNotHandled() {
        if (hasBeenHandled) {
            return null;
        } else {
            hasBeenHandled = true;
            return content;
        }
    }

    public boolean isHandled() {
        return hasBeenHandled;
    }
}

Then declare this EventObserver class like below so that we don't end up placing condition for checking about Event handled every time, everywhere :

public class EventObserver<T> implements Observer<Event<T>> {
    private OnEventChanged onEventChanged;

    public EventObserver(OnEventChanged onEventChanged) {
        this.onEventChanged = onEventChanged;
    }

    @Override
    public void onChanged(@Nullable Event<T> tEvent) {
        if (tEvent != null && tEvent.getContentIfNotHandled() != null && onEventChanged != null)
            onEventChanged.onUnhandledContent(tEvent.getContentIfNotHandled());
    }

    interface OnEventChanged<T> {
        void onUnhandledContent(T data);
    }
}

And How you can implement it :

MutableLiveData<Event<String>> data = new MutableLiveData<>();

// And observe like below
data.observe(lifecycleOwner, new EventObserver<String>(data -> {
        // your unhandled data would be here for one time.
    }));

// And this is how you add data as event to LiveData
data.setValue(new Event(""));

Refer here for details.


Edit for O.P.:

Yes, data.setValue(new Event("")); is meant for repository when you've got response from API (Remember to return same LiveData type you've taken in VM instead of SingleLiveEvent class though).

So, let's say you've created LiveData in ViewModel like below :

private MutableLiveData<Event<String>> snackbarStringSingleLiveEvent= new MutableLiveData<>();

You provide value to this livedata as Single Event from repository like below :

@Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
    mutableLiveData.setValue(new Event(response.body().getMessage())); // we set it as Event wrapper class.
}

And observe it on UI (Fragment) like below :

viewModel.getSnackbarStringSingleLiveEvent().observe(this, new EventObserver<String>(data -> {
        // your unhandled data would be here for one time.
    }));
like image 124
Jeel Vankhede Avatar answered Nov 15 '22 18:11

Jeel Vankhede


Event.java

public class Event<T> {

  private T content;

  private boolean hasBeenHandled = false;

  public Event(T content) {
    this.content = content;
  }

  /**
   * Returns the content and prevents its use again.
   */
  public T getContentIfNotHandled() {
      if (hasBeenHandled) {
          return null;
      } else {
          hasBeenHandled = true;
          return content;
      }
  }

  /**
   * Returns the content, even if it's already been handled.
   */
  public T peekContent() {
      return content;
  }
}

EventObserver.java

public class EventObserver<T> implements Observer<Event<? extends T>> {

   public interface EventUnhandledContent<T> {
       void onEventUnhandledContent(T t);
   }

   private EventUnhandledContent<T> content;

   public EventObserver(EventUnhandledContent<T> content) {
       this.content = content;
   }

   @Override
   public void onChanged(Event<? extends T> event) {
       if (event != null) {
           T result = event.getContentIfNotHandled();
           if (result != null && content != null) {
               content.onEventUnhandledContent(result);
           }
       }
   }
}

Example, In ViewModel Class

public class LoginViewModel extends BaseViewModel {
   private MutableLiveData<Event<Boolean>> _isProgressEnabled = new MutableLiveData<>();
   LiveData<Event<Boolean>> isProgressEnabled = _isProgressEnabled;

   private AppService appService;

   private SchedulerProvider schedulerProvider;

   private SharedPreferences preferences;

  @Inject
  LoginViewModel(
          AppService appService,
          SchedulerProvider schedulerProvider,
          SharedPreferences preferences
  ) {
      this.appService = appService;
      this.schedulerProvider = schedulerProvider;
      this.preferences = preferences;
    }


   public void login(){
   appService.login("username", "password")
                          .subscribeOn(schedulerProvider.executorIo())
                          .observeOn(schedulerProvider.ui())
                          .subscribe(_userLoginDetails::setValue,
                                     _userLoginDetailsError::setValue,
                                     () -> _isProgressEnabled.setValue(new Event<>(false)),
                                     d -> _isProgressEnabled.setValue(new Event<>(true))
                          )
   }
}

In Login Fragment,

viewModel.isProgressEnabled.observe(this, new EventObserver<>(hasEnabled -> {
        if (hasEnabled) {
            // showProgress
        } else {
            // hideProgress
        }
    }));

Using Event and EventObserver class we can achieve the same like SingleLiveEvent class but if you are thinking a lot of boilerplate code just avoid this method. I hope it would help you and give some idea about why we are using SingleEvent in LiveData.

like image 44
Santhosh Avatar answered Nov 15 '22 16:11

Santhosh