Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Contact Provider using View Model and Live Data

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

like image 643
Deepak Goyal Avatar asked Jan 07 '19 17:01

Deepak Goyal


People also ask

What is ViewModel and live data?

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.

What is ViewModel provider?

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.

What is the difference between LiveData and ViewModel?

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.

What is the difference between live data and mutable live data?

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.


1 Answers

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());
    }
});
like image 146
Aliton Oliveira Avatar answered Oct 23 '22 12:10

Aliton Oliveira