问题
The suggested way to implement ViewModel
is to expose the changing data by using LiveData
objects to activities, fragments and views. There are cases, when LiveData
is not an ideal answer or no answer at all.
The natural alternative would be, to apply the observer pattern to the ViewModel
, make it an observable. When registering observers to the ViewModel
, the ViewModel
will hold callback references to notify the observers.
The documentation says, a ViewModel
must not hold references to activities, fragments or views. The only answer to the question "why" I found is, that this may cause memory leaks. Then how about cleaning up the references to avoid memory leaks?
For views this is a difficulty. There is no defined moment, when the view goes away. But activities and fragments have a defined lifecycle. So there are places to unregister as observers.
What do you think? Is it valid to register activities as observers to ViewModels
if you take care to always unregister them? Did you hit upon any valid information about this question?
I set a small reward for the best answer. It's not because I think it a recommended solution (as it does not work with views). I just want to know and extend my options.
public class ExampleViewModel extends ViewModel {
public interface OnEndListener {
public void onEnd();
}
private List<OnEndListener> onEndListeners = new ArrayList<>();
public void setOnEndListener(OnEndListener onEndListener) {
onEndListeners.add(onEndListener);
}
public void removeOnEndListener(OnEndListener onEndListener) {
onEndListeners.remove(onEndListener);
}
public void somethingHappens() {
for (OnEndListener onEndListener: new ArrayList<OnEndListener>(onEndListeners) ) {
onEndListener.onEnd();
}
}
}
public class ExampleActivity extends AppCompatActivity {
ExampleViewModel exampleViewModel;
ExampleViewModel.OnEndListener onEndListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onEndListener = new ExampleViewModel.OnEndListener() {
@Override
public void onEnd() {
finish();
}
};
exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
exampleViewModel.setOnEndListener(onEndListener);
}
@Override
protected void onDestroy() {
super.onDestroy();
exampleViewModel.removeOnEndListener(onEndListener);
}
}
回答1:
To ask "am I allowed..." is not really a useful question, IMO. The docs are clear that what you are suggesting is discouraged and why. That said, I expect that your code would probably work as expected and is therefore "allowed" (i.e. not prevented by a technical constraint).
One possible gotcha scenario: InstanceA of ExampleActivity
is started and kicks off some long-running task on the ExampleViewModel
. Then, before the task completes, the device is rotated and InstanceA is destroyed because of the configuration change. Then, in between the time when InstanceA is destroyed and a new InstanceB is created, the long-running task completes and your view model calls onEndListener.onEnd()
. Except: Oh no! The onEndListener
is null
because it was cleared when InstanceA was destroyed and hasn't yet been set by InstanceB: NullPointerException
ViewModel
was designed (in part) precisely to handle edge cases like the gotcha scenario above. So instead of working against the intended use of the ViewModel
, why not just use the tools it offers along with LiveData
to accomplish the same thing? (And with less code, I might add.)
public class ExampleActivity extends AppCompatActivity {
ExampleViewModel exampleViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
exampleViewModel.getOnEndLive().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean onEnd) {
if (onEnd != null && onEnd) {
finish();
}
}
});
}
}
public class ExampleViewModel extends ViewModel {
private MutableLiveData<Boolean> onEndLive = new MutableLiveData<>();
public MutableLiveData<Boolean> getOnEndLive() {
return onEndLive;
}
public void somethingHappens() {
onEndLive.setValue(true);
}
}
Think of the LiveData in this case not as actual "data" per se, but as a signal that you can pass from your ViewModel to your Activity. I use this pattern all the time.
来源:https://stackoverflow.com/questions/50000975/am-i-allowed-to-observe-a-viewmodel-if-i-clean-up-the-back-references