AndroidViewModel - 进行重复调用不会返回observe函数中的数据

Roh*_*wal 6 android android-livedata android-architecture-lifecycle android-viewmodel android-architecture-components

我的问题与ViewModel有关,第二次返回null,其中observe如果我重复调用服务器,我在函数中没有得到回调.以下是我使用的代码 -

@Singleton
public class NetworkInformationViewModel extends AndroidViewModel {
  private LiveData<Resource<NetworkInformation>> networkInfoObservable;
  private final APIClient apiClient;

  @Inject
  NetworkInformationViewModel(@NonNull APIClient apiClient, @NonNull Application application) {
    super(application);
    this.apiClient = apiClient;
    getNetworkInformation();
  }

  public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() {
    return networkInfoObservable;
  }

  // making API calls and adding it to Observable
  public void getNetworkInformation() {
    networkInfoObservable = apiClient.getNetworkInformation();
  }
}
Run Code Online (Sandbox Code Playgroud)

在Activity中,ViewModel定义如下 -

final NetworkInformationViewModel networkInformationViewModel =
      ViewModelProviders.of(this, viewModelFactory).get(NetworkInformationViewModel.class);
    observeViewModel(networkInformationViewModel);
Run Code Online (Sandbox Code Playgroud)

observeViewModel函数用于添加observable ViewModel.

public void observeViewModel(final NetworkInformationViewModel networkInformationViewModel) {
    networkInformationViewModel.getNetworkInfoObservable()
      .observe(this, networkInformationResource -> {
        if (networkInformationResource != null) {
          if (networkInformationResource.status == APIClientStatus.Status.SUCCESS) {
            Timber.d("Got network information data");
          } else {
            final Throwable throwable = networkInformationResource.throwable;
            if (throwable instanceof SocketTimeoutException) {
              final NetworkInformation networkInformation = networkInformationResource.data;
              String error = null;
              if (networkInformation != null) {
                error = TextUtils.isEmpty(networkInformation.error) ? networkInformation.reply : networkInformation.error;
              }
              Timber.e("Timeout error occurred %s %s", networkInformationResource.message, error);

            } else {
              Timber.e("Error occurred %s", networkInformationResource.message);
            }
            if (count != 4) {
              networkInformationViewModel.getNetworkInformation();
              count++;
              // Uncommenting following line enables callback to be received every time 
              //observeViewModel(networkInformationViewModel);
            }
          }
        }
      });
  }
Run Code Online (Sandbox Code Playgroud)

在上面的函数中取消注释以下行允许每次都进行回调,但必须有一个正确的方法来执行此操作.

//observeViewModel(networkInformationViewModel);
Run Code Online (Sandbox Code Playgroud)

请注意: - 我不需要RxJava实现来实现它.

Lyl*_*yla 1

现在的getNetworkInformation()你是:

  1. 创建一个新的LiveData
  2. 更新LiveData使用setValue

相反,您应该创建一个LiveDataforAPIClient作为成员变量,然后在 getNetworkInformation() 中更新该成员LiveData

更一般地说,您APIClient是一个数据源。对于数据源,您可以让它们包含在数据更改时更新的成员 LiveData 对象。您可以为这些 LiveData 对象提供 getter,以便在 ViewModel 中访问它们,并最终在您的 Activity/Fragment 中监听它们。这与您获取另一个数据源(例如 Room)并监听 Room 返回的 LiveData 的方式类似。

因此,本例中的代码如下所示:

@Singleton
public class APIClient {
    private final MutableLiveData<Resource<NetworkInformation>> mNetworkData = new MutableLiveData<>(); // Note this needs to be MutableLiveData so that you can call setValue

    // This is basically the same code as the original getNetworkInformation, instead this returns nothing and just updates the LiveData
    public void fetchNetworkInformation() {
        apiInterface.getNetworkInformation().enqueue(new Callback<NetworkInformation>() {
          @Override
          public void onResponse(
            @NonNull Call<NetworkInformation> call, @NonNull Response<NetworkInformation> response
          ) {
            if (response.body() != null && response.isSuccessful()) {
              mNetworkData.setValue(new Resource<>(APIClientStatus.Status.SUCCESS, response.body(), null));
            } else {
              mNetworkData.setValue(new Resource<>(APIClientStatus.Status.ERROR, null, response.message()));
            }
          }

          @Override
          public void onFailure(@NonNull Call<NetworkInformation> call, @NonNull Throwable throwable) {
            mNetworkData.setValue(
              new Resource<>(APIClientStatus.Status.ERROR, null, throwable.getMessage(), throwable));
          }
        });
    }

    // Use a getter method so that you can return immutable LiveData since nothing outside of this class will change the value in mNetworkData
    public LiveData<Resource<NetworkInformation>> getNetworkData(){
        return mNetworkData;
    }

}
Run Code Online (Sandbox Code Playgroud)

然后在你的 ViewModel 中...

// I don't think this should be a Singleton; ViewModelProviders will keep more than one from being instantiate for the same Activity/Fragment lifecycle
public class SplashScreenViewModel extends AndroidViewModel {

private LiveData<Resource<NetworkInformation>> networkInformationLiveData;

  @Inject
  SplashScreenViewModel(@NonNull APIClient apiClient, @NonNull Application application) {
    super(application);
    this.apiClient = apiClient;

    // Initializing the observable with empty data
    networkInfoObservable = apiClient.getNetworkData()

  }

  public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() {
    return networkInformationLiveData;
  }

}
Run Code Online (Sandbox Code Playgroud)

您的活动可以与您最初编码的活动相同;它只会从 ViewModel 获取并观察 LiveData。

那么 Transformations.switchMap 是做什么用的呢?

switchMap此处没有必要,因为您不需要更改LiveData中的基础实例APIClient。这是因为实际上只有一项正在变化的数据。假设您的 APIClient 由于某种原因需要 4 个不同的 LiveData,并且您想要更改LiveData您观察到的数据:

public class APIClient {
    private MutableLiveData<Resource<NetworkInformation>> mNetData1, mNetData2, mNetData3, mNetData4;

    ...
}
Run Code Online (Sandbox Code Playgroud)

那么假设您的 fetchNetworkInformation 将根据情况引用不同的 LiveData 来观察。它可能看起来像这样:

public  LiveData<Resource<NetworkInformation>> getNetworkInformation(int keyRepresentingWhichLiveDataToObserve) {
    LiveData<Resource<NetworkInformation>> currentLiveData = null;
    switch (keyRepresentingWhichLiveDataToObserve) {
        case 1:
            currentLiveData = mNetData1; 
            break;
        case 2:
            currentLiveData = mNetData2; 
            break;
        //.. so on
    }

    // Code that actually changes the LiveData value if needed here

    return currentLiveData;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,实际的LiveData来源getNetworkInformation是变化的,并且您还使用某种参数来确定您想要的 LiveData。在这种情况下,您将使用switchMap,因为您希望确保您在 Activity/Fragment 中调用的观察语句能够观察到从 中返回的 LiveData APIClient,即使您更改了底层 LiveData 实例也是如此。而且你不想再次调用observe。

现在,这是一个有点抽象的示例,但它基本上就是您对Room Dao 的调用所做的事情 - 如果您有一个基于 idDao查询并返回 a 的方法,它将根据 id 返回一个不同的实例。RoomDatabaseLiveDataLiveData