AndroidViewModel - 进行重复调用不会 return observe 函数中的数据
AndroidViewModel - Making duplicate calls doesn't return data in observe function
-
android
-
android-livedata
-
android-architecture-lifecycle
-
android-viewmodel
-
android-architecture-components
我的问题与 有关,如果我重复调用服务器,我不会在 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();
}
}
在Activity中,ViewModel定义如下-
final NetworkInformationViewModel networkInformationViewModel =
ViewModelProviders.of(this, viewModelFactory).get(NetworkInformationViewModel.class);
observeViewModel(networkInformationViewModel);
observeViewModel
函数用于在ViewModel
上添加observable。
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);
}
}
}
});
}
在上面的函数中取消注释以下行允许回调每次都来,但是必须有一个正确的方法来做到这一点。
//observeViewModel(networkInformationViewModel);
请注意:-
我不需要 RxJava 实现来实现这个。
我已经更新了。重新发布在这里,因为我已经悬赏了这个问题,希望有人会验证这是处理问题的正确方法。
以下是更新后的工作解决方案 -
@Singleton
public class SplashScreenViewModel extends AndroidViewModel {
private final APIClient apiClient;
// This is the observable which listens for the changes
// Using 'Void' since the get method doesn't need any parameters. If you need to pass any String, or class
// you can add that here
private MutableLiveData<Void> networkInfoObservable;
// This LiveData contains the information required to populate the UI
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 = new MutableLiveData<Void>();
// Using the Transformation switchMap to listen when the data changes happen, whenever data
// changes happen, we update the LiveData object which we are observing in the MainActivity.
networkInformationLiveData = Transformations.switchMap(networkInfoObservable, input -> apiClient.getNetworkInformation());
}
/**
* Function to get LiveData Observable for NetworkInformation class
* @return LiveData<Resource<NetworkInformation>>
*/
public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() {
return networkInformationLiveData;
}
/**
* Whenever we want to reload the networkInformationLiveData, we update the mutable LiveData's value
* which in turn calls the `Transformations.switchMap()` function and updates the data and we get
* call back
*/
public void setNetworkInformation() {
networkInfoObservable.setValue(null);
}
}
Activity 的代码将更新为 -
final SplashScreenViewModel splashScreenViewModel =
ViewModelProviders.of(this, viewModelFactory).get(SplashScreenViewModel.class);
observeViewModel(splashScreenViewModel);
// This function will ensure that Transformation.switchMap() function is called
splashScreenViewModel.setNetworkInformation();
看着她 droidCon NYC video for more information on LiveData. The official Google repository for LiveData is https://github.com/googlesamples/android-architecture-components/ 寻找 GithubBrowserSample
项目。
apiClient.getNetworkInformation()
调用不需要任何参数来获取其他信息。因此,'Void' 添加到 MutableLiveData
.
public LiveData<Resource<NetworkInformation>> getNetworkInformation() {
final MutableLiveData<Resource<NetworkInformation>> data = new MutableLiveData<>();
apiInterface.getNetworkInformation().enqueue(new Callback<NetworkInformation>() {
@Override
public void onResponse(
@NonNull Call<NetworkInformation> call, @NonNull Response<NetworkInformation> response
) {
if (response.body() != null && response.isSuccessful()) {
data.setValue(new Resource<>(APIClientStatus.Status.SUCCESS, response.body(), null));
} else {
data.setValue(new Resource<>(APIClientStatus.Status.ERROR, null, response.message()));
}
}
@Override
public void onFailure(@NonNull Call<NetworkInformation> call, @NonNull Throwable throwable) {
data.setValue(
new Resource<>(APIClientStatus.Status.ERROR, null, throwable.getMessage(), throwable));
}
});
return data;
}
我没有遇到同样的问题,但我遇到了类似的事情,每次我将数据保存在数据库中时,观察者的数量都在增加。我调试的方式是调用了多少个实例或观察者的不同实例,我开始知道当您从视图模型中获取实时数据时,需要检查它是否为非空,或者您可以说只返回 1 个实例-
private LiveData<T> data;
public LiveData<T> getLiveData(){
if(data ==null){
data = //api call or fetch from db
}
return data;
}
在我简单地返回 data
对象之前,然后在检查源之后我得出的结论是 livedata 自动更新你的对象并且每次没有空检查都会创建新实例并且新观察者正在获取随附的。如果我对 livedata 的理解有误,有人可以纠正我。
现在 getNetworkInformation()
你是:
- 创建一个新的
LiveData
- 使用
setValue
更新 LiveData
相反,您应该为 APIClient
创建一个 LiveData
作为成员变量,然后在 getNetworkInformation() just 中更新该成员 LiveData
.
更一般地说,您的 APIClient
是一个数据源。对于数据源,您可以让它们包含在数据更改时更新的成员 LiveData 对象。您可以为这些 LiveData 对象提供 getter,以便在 ViewModel 中访问它们,并最终在您的 Activities/Fragments 中收听它们。这类似于您获取另一个数据源(例如 Room)并收听由 Room 编辑的 LiveData return。
因此本例中的代码如下所示:
@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;
}
}
然后在您的 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;
}
}
您的 activity 可以与您最初编码的相同;它只会从 ViewModel 获取并观察 LiveData。
那么Transformations.switchMap有什么用呢?
switchMap
在这里不是必需的,因为您不需要更改 APIClient
中的基础 LiveData
实例。这是因为实际上只有一个变化的数据。假设您的 APIClient 出于某种原因需要 4 个不同的 LiveData,并且您想更改观察到的 LiveData
:
public class APIClient {
private MutableLiveData<Resource<NetworkInformation>> mNetData1, mNetData2, mNetData3, mNetData4;
...
}
那么假设你的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;
}
在这种情况下,来自 getNetworkInformation
的实际 LiveData
是更改,并且您还使用某种参数来确定所需的 LiveData。在这种情况下,您将使用 switchMap
,因为您要确保在 Activity/Fragment 中调用的观察语句观察来自 APIClient
的 LiveData returned ,即使您更改了底层的 LiveData 实例。而且你不想再调用 observe 了。
现在这是一个有点抽象的例子,但它基本上就是你调用 Room Dao 所做的——如果你有一个 Dao
方法来查询你的 RoomDatabase
在一个 id 和 returns 一个 LiveData
上,它会 return 一个基于 id 的不同的 LiveData
实例。
android
android-livedata
android-architecture-lifecycle
android-viewmodel
android-architecture-components
我的问题与 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();
}
}
在Activity中,ViewModel定义如下-
final NetworkInformationViewModel networkInformationViewModel =
ViewModelProviders.of(this, viewModelFactory).get(NetworkInformationViewModel.class);
observeViewModel(networkInformationViewModel);
observeViewModel
函数用于在ViewModel
上添加observable。
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);
}
}
}
});
}
在上面的函数中取消注释以下行允许回调每次都来,但是必须有一个正确的方法来做到这一点。
//observeViewModel(networkInformationViewModel);
请注意:- 我不需要 RxJava 实现来实现这个。
我已经更新了
以下是更新后的工作解决方案 -
@Singleton
public class SplashScreenViewModel extends AndroidViewModel {
private final APIClient apiClient;
// This is the observable which listens for the changes
// Using 'Void' since the get method doesn't need any parameters. If you need to pass any String, or class
// you can add that here
private MutableLiveData<Void> networkInfoObservable;
// This LiveData contains the information required to populate the UI
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 = new MutableLiveData<Void>();
// Using the Transformation switchMap to listen when the data changes happen, whenever data
// changes happen, we update the LiveData object which we are observing in the MainActivity.
networkInformationLiveData = Transformations.switchMap(networkInfoObservable, input -> apiClient.getNetworkInformation());
}
/**
* Function to get LiveData Observable for NetworkInformation class
* @return LiveData<Resource<NetworkInformation>>
*/
public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() {
return networkInformationLiveData;
}
/**
* Whenever we want to reload the networkInformationLiveData, we update the mutable LiveData's value
* which in turn calls the `Transformations.switchMap()` function and updates the data and we get
* call back
*/
public void setNetworkInformation() {
networkInfoObservable.setValue(null);
}
}
Activity 的代码将更新为 -
final SplashScreenViewModel splashScreenViewModel =
ViewModelProviders.of(this, viewModelFactory).get(SplashScreenViewModel.class);
observeViewModel(splashScreenViewModel);
// This function will ensure that Transformation.switchMap() function is called
splashScreenViewModel.setNetworkInformation();
看着她 droidCon NYC video for more information on LiveData. The official Google repository for LiveData is https://github.com/googlesamples/android-architecture-components/ 寻找 GithubBrowserSample
项目。
apiClient.getNetworkInformation()
调用不需要任何参数来获取其他信息。因此,'Void' 添加到 MutableLiveData
.
public LiveData<Resource<NetworkInformation>> getNetworkInformation() {
final MutableLiveData<Resource<NetworkInformation>> data = new MutableLiveData<>();
apiInterface.getNetworkInformation().enqueue(new Callback<NetworkInformation>() {
@Override
public void onResponse(
@NonNull Call<NetworkInformation> call, @NonNull Response<NetworkInformation> response
) {
if (response.body() != null && response.isSuccessful()) {
data.setValue(new Resource<>(APIClientStatus.Status.SUCCESS, response.body(), null));
} else {
data.setValue(new Resource<>(APIClientStatus.Status.ERROR, null, response.message()));
}
}
@Override
public void onFailure(@NonNull Call<NetworkInformation> call, @NonNull Throwable throwable) {
data.setValue(
new Resource<>(APIClientStatus.Status.ERROR, null, throwable.getMessage(), throwable));
}
});
return data;
}
我没有遇到同样的问题,但我遇到了类似的事情,每次我将数据保存在数据库中时,观察者的数量都在增加。我调试的方式是调用了多少个实例或观察者的不同实例,我开始知道当您从视图模型中获取实时数据时,需要检查它是否为非空,或者您可以说只返回 1 个实例-
private LiveData<T> data;
public LiveData<T> getLiveData(){
if(data ==null){
data = //api call or fetch from db
}
return data;
}
在我简单地返回 data
对象之前,然后在检查源之后我得出的结论是 livedata 自动更新你的对象并且每次没有空检查都会创建新实例并且新观察者正在获取随附的。如果我对 livedata 的理解有误,有人可以纠正我。
现在 getNetworkInformation()
你是:
- 创建一个新的
LiveData
- 使用
setValue
更新
LiveData
相反,您应该为 APIClient
创建一个 LiveData
作为成员变量,然后在 getNetworkInformation() just 中更新该成员 LiveData
.
更一般地说,您的 APIClient
是一个数据源。对于数据源,您可以让它们包含在数据更改时更新的成员 LiveData 对象。您可以为这些 LiveData 对象提供 getter,以便在 ViewModel 中访问它们,并最终在您的 Activities/Fragments 中收听它们。这类似于您获取另一个数据源(例如 Room)并收听由 Room 编辑的 LiveData return。
因此本例中的代码如下所示:
@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;
}
}
然后在您的 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;
}
}
您的 activity 可以与您最初编码的相同;它只会从 ViewModel 获取并观察 LiveData。
那么Transformations.switchMap有什么用呢?
switchMap
在这里不是必需的,因为您不需要更改 APIClient
中的基础 LiveData
实例。这是因为实际上只有一个变化的数据。假设您的 APIClient 出于某种原因需要 4 个不同的 LiveData,并且您想更改观察到的 LiveData
:
public class APIClient {
private MutableLiveData<Resource<NetworkInformation>> mNetData1, mNetData2, mNetData3, mNetData4;
...
}
那么假设你的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;
}
在这种情况下,来自 getNetworkInformation
的实际 LiveData
是更改,并且您还使用某种参数来确定所需的 LiveData。在这种情况下,您将使用 switchMap
,因为您要确保在 Activity/Fragment 中调用的观察语句观察来自 APIClient
的 LiveData returned ,即使您更改了底层的 LiveData 实例。而且你不想再调用 observe 了。
现在这是一个有点抽象的例子,但它基本上就是你调用 Room Dao 所做的——如果你有一个 Dao
方法来查询你的 RoomDatabase
在一个 id 和 returns 一个 LiveData
上,它会 return 一个基于 id 的不同的 LiveData
实例。