My android app is using contacts providers
to display all the contacts to the user. I'm using Loaders
to load the contacts by following the tutorial/documentation at https://developer.android.com/training/contacts-provider/retrieve-names
But from the link https://developer.android.com/guide/components/loaders, it is mentioned that loaders are deprecated as of Android P.
Loaders have been deprecated as of Android P (API 28). The recommended option for dealing with loading data while handling the Activity and Fragment lifecycles is to use a combination of ViewModels and LiveData. ViewModels survive configuration changes like Loaders but with less boilerplate. LiveData provides a lifecycle-aware way of loading data that you can reuse in multiple ViewModels. You can also combine LiveData using MediatorLiveData, and any observable queries, such as those from a Room database, can be used to observe changes to the data. ViewModels and LiveData are also available in situations where you do not have access to the LoaderManager, such as in a Service. Using the two in tandem provides an easy way to access the data your app needs without having to deal with the UI lifecycle. To learn more about LiveData see the LiveData guide and to learn more about ViewModels see the ViewModel guide.
So my question is:
1. How can we fetch the contacts using android view Model and live data from contact providers?
2. Can we use Room database for contact providers?
Below you can find the link to the source code where I tried to use the Android View Model and Live data to fetch the contacts from ContactProviders.
https://github.com/deepak786/phonebook-contacts
3. What can be improved in the above source code so that fetching will be faster?
Thanks & Regards
Deepak
ViewModel allows the app's data to survive configuration changes. In this codelab, you'll learn how to integrate LiveData with the data in the ViewModel . The LiveData class is also part of the Android Architecture Components and is a data holder class that can be observed.
android.arch.lifecycle.ViewModelProvider. An utility class that provides ViewModels for a scope. Default ViewModelProvider for an Activity or a Fragment can be obtained from ViewModelProviders class.
ViewModel : Provides data to the UI and acts as a communication center between the Repository and the UI. Hides the backend from the UI. ViewModel instances survive device configuration changes. LiveData : A data holder class that follows the observer pattern, which means that it can be observed.
LiveData has no publicly available methods to update the stored data. The MutableLiveData class exposes the setValue(T) and postValue(T) methods publicly and you must use these if you need to edit the value stored in a LiveData object.
Below you can find a very simple solution for loading contacts using MVVM
:
https://github.com/NaarGes/Android-Contact-List
Here comes a bit of code in case the link is no longer working.
First, let's create a simple POJO
for contacts UserObject.java
public class UserObject {
private String email, name, phone;
public UserObject() {
// EMPTY CONSTRUCTOR FOR FIREBASE REALTIME DATABASE
}
public UserObject(String email, String name, String phone) {
this.email = email;
this.name = name;
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Now, let's create our repository ContactRepository.java
public class ContactRepository {
private Context context;
private static final String TAG = "debinf ContRepo";
public ContactRepository(Context context) {
this.context = context;
}
public List<UserObject> fetchContacts() {
List<UserObject> contacts = new ArrayList<>();
Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
Log.i(TAG, "fetchContacts: cursor.getCount() is "+cursor.getCount());
if ((cursor != null ? cursor.getCount() : 0) > 0) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
UserObject contact = new UserObject("",name, phone);
Log.i(TAG, "fetchContacts: phone is "+phone);
contacts.add(contact);
}
}
if (cursor != null) {
cursor.close();
}
return contacts;
}
}
Next, we create our ContactViewModel.java
public class ContactViewModel extends ViewModel {
private ContactRepository repository;
private MutableLiveData<List<UserObject>> contacts;
public ContactViewModel(Context context) {
repository = new ContactRepository(context);
contacts = new MutableLiveData<>();
}
public MutableLiveData<List<UserObject>> getContacts() {
contacts.setValue(repository.fetchContacts());
return contacts;
}
}
Next, we create a factory
for our ViewModel
ContactViewModelFactory.java
public class ContactViewModelFactory implements ViewModelProvider.Factory {
private Context context;
public ContactViewModelFactory(Context context) {
this.context = context;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(ContactViewModel.class)) {
return (T) new ContactViewModel(context);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
Let's not forget to add permission in our AndroidManifest
<uses-permission android:name="android.permission.READ_CONTACTS" />
And finally, we ask for permission in our MainActivity.java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS,Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST);
}
and bring our contacts to surface
ContactViewModelFactory factory = new ContactViewModelFactory(this);
viewModel = ViewModelProviders.of(this, factory).get(ContactViewModel.class);
viewModel.getContacts().observe(this, new Observer<List<UserObject>>() {
@Override
public void onChanged(@Nullable List<UserObject> userObjects) {
Log.i(TAG, "ViewModel: userObjects size is "+userObjects.size());
Log.i(TAG, "ViewModel: userObjects size is "+userObjects.get(1).getPhone());
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With