I am trying learn View-model android ,in my first phase of learning i am trying to update UI (Textview) by using view-model and data-binding. In View model i am having aynctask
I suggest to follow next basic principles:
LiveData
to deliver dataImplementation notes:
firstName
is read only on viewlastName
is editable on viewloadUser()
is not threadsafe save()
method until data is not loadedSuppose, we have UserData
object with first and last name. So, getters it's (usually) all what we need:
public class UserData {
private String firstName;
private String lastName;
public UserData(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
To follow this suggestion we should to use only view model in data binding layout:
Note: you can use a few view models in one layout, but not raw data
This mean, you shouldn't to expose complex data objects (UserData
in our case) directly from view model. Preferable to expose privative types which view can use as-is. In example below we don't need to hold UserData
object because it used only to loading grouped data. We, probably, need to create UserData
to save it but it depends on your repository implementation.
public class UserDataViewModel extends ViewModel {
private ListTask loadTask;
private final MutableLiveData firstName = new MediatorLiveData<>();
private final MutableLiveData lastName = new MediatorLiveData<>();
private final MutableLiveData error = new MutableLiveData<>();
/**
* Expose LiveData if you do not use two-way data binding
*/
public LiveData getFirstName() {
return firstName;
}
/**
* Expose MutableLiveData to use two-way data binding
*/
public MutableLiveData getLastName() {
return lastName;
}
public LiveData getError() {
return error;
}
@MainThread
public void loadUser(String userId) {
// cancel previous running task
cancelLoadTask();
loadTask = new ListTask();
Observer observer = new Observer() {
@Override
public void onChanged(@Nullable UserData userData) {
// transform and deliver data to observers
firstName.setValue(userData == null? null : userData.getFirstName());
lastName.setValue(userData == null? null : userData.getLastName());
// remove subscription on complete
loadTask.getUserData().removeObserver(this);
}
};
// it can be replaced to observe() if LifeCycleOwner is passed as argument
loadTask.getUserData().observeForever(observer);
// start loading task
loadTask.execute(userId);
}
public void save() {
// clear previous error message
error.setValue(null);
String fName = firstName.getValue(), lName = lastName.getValue();
// validate data (in background)
if (fName == null || lName == null) {
error.setValue("Opps! Data is invalid");
return;
}
// create and save object
UserData newData = new UserData(fName, lName);
// ...
}
@Override
protected void onCleared() {
super.onCleared();
cancelLoadTask();
}
private void cancelLoadTask() {
if (loadTask != null)
loadTask.cancel(true);
loadTask = null;
}
}
LiveData
to deliver datapublic class ListTask extends AsyncTask {
private final MutableLiveData data= new MediatorLiveData<>();
public LiveData getUserData() {
return data;
}
@Override
protected void onPostExecute(UserData userData) {
data.setValue(userData);
}
@Override
protected UserData doInBackground(String[] userId) {
// some id validations
return loadRemoiteUser(userId[0]);
}
}
public class MainActivity extends AppCompatActivity {
private UserDataViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get view model
viewModel = ViewModelProviders.of(this).get(UserDataViewModel.class);
// create binding
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// set view model to data binding
binding.setVm(viewModel);
// don't forget to set LifecycleOwner to data binding
binding.setLifecycleOwner(this);
// start user loading (if necessary)
viewModel.loadUser("user_id");
// ...
}
}
PS: try to use RxJava library instead of AsyncTask
to perform background work.