Java中的Android体系结构SingleLiveEvent和EventObserver实践示例

时间:2019-05-10 06:33:59

标签: java android android-architecture-components

我尝试制作具有两个字段(username, password)的示例登录页面,并使用android体系结构组件保存按钮,使用android数据绑定,验证viewmodel中的数据,并从视图模型进行调用如官方文档所述,用于远程服务器调用的存储库,远程服务器成功返回我userid,所以如何使用此成功从视图模型启动新片段?我了解了有关singleLiveEventEventObserver的一些知识,但是找不到清晰的用法示例:

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;
}

存储库

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;
}

登录片段

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

在上述情况下如何使用EventObserver?有实际的例子吗?

3 个答案:

答案 0 :(得分:0)

请查看以下示例,了解如何创建单个LiveEvent LiveData的身份仅观察一次:

如下创建一个名为 Event 的类,该类将提供一次数据,并充当LiveData包装器的子代:

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;
    }
}

然后像下面这样声明此EventObserver类,以便我们不会遇到每次在任何地方都检查过的Event时出现的放置条件

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);
    }
}

以及如何实现它:

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(""));

有关详细信息,请参考 here


编辑 O.P。

是的,data.setValue(new Event(""));用于存储库,当您从API获得响应 (请记住,返回与您在VM中相同的LiveData类型) (SingleLiveEvent类)

因此,假设您已经在LiveData中创建了ViewModel,如下所示:

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

您可以通过以下存储库中的 Single Event (单个事件)为该实时数据提供价值:

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

并在用户界面(片段)上进行观察,如下所示:

viewModel.getSnackbarStringSingleLiveEvent().observe(this, new EventObserver<String>(data -> {
        // your unhandled data would be here for one time.
    }));

答案 1 :(得分:0)

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);
           }
       }
   }
}

示例,在ViewModel类中

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))
                          )
   }
}

在登录片段中,

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

使用Event和EventObserver类可以实现与SingleLiveEvent类相同的功能,但是如果您想使用很多样板代码,则请避免使用此方法。希望对您有所帮助,并提供一些有关为什么我们在LiveData中使用SingleEvent的想法。

答案 2 :(得分:0)

我知道 Google 提供了在 ViewModel 和 UI 之间使用 LiveData 的指南,但在某些极端情况下,将 LiveData 用作 SingleLiveEvent 就像重新发明轮子。对于视图模型和用户界面之间的单次消息传递,我们可以使用委托设计模式。在活动中初始化视图模型时,我们只需将活动设置为接口的实现者。然后在我们的视图模型中,我们可以调用委托方法。

界面

public interface Snackable:
   void showSnackbarMessage(String message);

界面

public class MyActivity extends AppCompatActivity implements Snackable {
    private MyViewModel myViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_layout);

        this.myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        this.myViewModel.setListener(this);
    }


    @Override
    public void showSnackbarMessage(String message) {
        Toast.makeText(this, "message", Toast.LENGTH_LONG).show();
    }
}

查看模型

public class MyViewModel extends AndroidViewModel {

    private Snackable listener;

    public MyViewModel(@NonNull Application application) {
         super(application);
    }

    public void setListener(MyActivity activity){
         this.listener = activity;
    }

    private void sendSnackbarMessage(String message){
        if(listener != null){
            listener.showSnackbarMessage(message);
        }
    }

    private void anyFunctionInTheViewModel(){
        sendSnackbarMessage("Hey I've got a message for the UI!");
    }
}