Step One: The Repository

The Repository is responsible for handling all of the logic for making the database queries. In general, the Repository is responsible for handling the logic of any type of query, local or network, when using the new Architecture Components. This way, we can reuse it anywhere in our app, instead of planting duplicate code and Async Queries in our fragments and activities, where they don’t belong. Since we aren’t using room, we will be using an Handler for making the database query. Furthermore, we’ll subclass this in our Repository and extend AsyncQueryHandler for the class:

private static class QueryHandler extends AsyncQueryHandler { public QueryHandler(ContentResolver cr) {

super(cr);

} @Override

protected void onQueryComplete(int token, Object cookie, Cursor cursor) {



} protected void onQueryComplete(int token, Object cookie, Cursor cursor) {

The idea here is pretty simple: We can use our QueryHandler to make queries to a database using a Content Resolver, pass an Object in as a cookie to modify after the query completes (which will be of great use later), and we also have the ability to switch on a token in the event we are doing multiple queries and want to differentiate what we do with the result. A simple query to this type of class can look like the following:

QueryHandler queryHandler = new QueryHandler(contentResolver);

queryHandler.startQuery(token, null /* cookie */, uri, projection, selection, selectionArgs, orderby);

The next step we take is going to be somewhat of a big one, but it should make sense after a few minutes. I’m going to go all out and combine a basic implementation of the Repository with our QueryHandler , and fill in the QueryHandler with LiveData. Our DatabaseQueryRepository.java :

public class DatabaseQueryRepository { private static final int TOKEN_QUERY = 0; private QueryHandler mQueryHandler; @NonNull

public static DatabaseQueryRepository newInstance() {

return new DatabaseQueryRepository();

} public static DatabaseQueryRepository newInstance() {return new DatabaseQueryRepository(); // Use id in some way, but we're not in this example

public MutableLiveData<String> fetchData(ContentResolver contentResolver, long id) {

mQueryHandler = new QueryHandler(contentResolver); final LiveData<String> result = query(TOKEN_QUERY,

YOUR_DB_URI, YOUR_DB_PROJECTION, YOUR_DB_SELECTION,

YOUR_DB_SELECTION_ARGS, YOUR_DB_ORDER_BY); return result;

}





MutableLiveData<String> result = new MutableLiveData<>(); private MutableLiveData query(int token, Uri uri, @Nullabl e String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String orderby) {MutableLiveData result = new MutableLiveData<>(); // Pass MutableLiveData in as a cookie, so we can set the result

// in OnQueryComplete

mQueryHandler.startQuery(token, result, uri, projection,

selection, selectionArgs, orderby); return result;

} private static class QueryHandler extends AsyncQueryHandler { public QueryHandler(ContentResolver cr) {

super(cr);

mDataHolder = dataHolder;

} @Override

protected void onQueryComplete(int token, Object cookie, Cursor cursor) {

MutableLiveData<String> mutableData = (MutableLiveData<String>)

cookie;

try {

switch (token) {

case TOKEN_QUERY:

if (cursor != null && cursor.moveToFirst()) {

String demoString =

cursor.getString(SOME_COLUMN_GOES_HERE);

cookie.setValue(demoString);

}

break;

} finally {

if (cursor != null) {

cursor.close();

}

}

}

}

} protected void onQueryComplete(int token, Object cookie, Cursor cursor) {MutableLiveData mutableData = (MutableLiveData )cookie;try {switch (token) {case TOKEN_QUERY:if (cursor != null && cursor.moveToFirst()) {String demoString =cursor.getString(SOME_COLUMN_GOES_HERE);cookie.setValue(demoString);break;} finally {if (cursor != null) {cursor.close();

The basic idea here is that once we get an instance of this Repository, we can call fetchData(ContentResolver contentResolver) from the ViewModel, only needing a ContentResolver, while the Repository handles all the rest of the logic. The beauty of this will be exemplified when we construct the ViewModel. The query() method exists as a private method, as abstract as possible, serving only the purpose to make an arbitrary query call to our QueryHandler class.

Step Two: The ViewModel

The philosophy behind the ViewModel is that each view in the app interacts with a ViewModel, not the Repository, as shown in the image below:

Android Architecture Diagram

This allows ViewModels to be reused, and separates the logic into a cleaner, more reactive, and object-oriented way. Lets take a look of how the ViewModel will look like when put together with our Repository:

public class DemoViewModel extends AndroidViewModel { @NonNull

private final MutableLiveData<Long> mLiveData = new

MutableLiveData<>(); private final MutableLiveData mLiveData = newMutableLiveData<>(); @NonNull

private final DatabaseQueryRepository mDatabaseRepository; private final DatabaseQueryRepository mDatabaseRepository;

Transformations.switchMap(mLiveData,

new Function<Long, LiveData<String>>() {

@Override

public LiveData<String> apply(Long input) {

return

mDatabaseRepository.fetchData(getApplication().

getContentResolver(), input);

}

}); private final LiveData mResult =Transformations.switchMap(mLiveData,new Function >() {public LiveData apply(Long input) {returnmDatabaseRepository.fetchData(getApplication().getContentResolver(), input);}); public DemoViewModel(Application application) {

super(application);

mDatabaseRepository = DatabaseQueryRepository.newInstance();

} // Here we observe an id. If the reference to the id changes,

// the LiveData observes that and fetches data again

public void setId(long id) {

mLiveData.setValue(id);

} public LiveData<String> getData() {

return mResult;

}

}

There a few things we need to explain here. First, we extend AndroidViewModel instead of ViewModel , which allows us to get an instance of Application , preventing us from passing a context into the ViewModel, which can cause memory leaks. Secondly, we are using something called a transformation and specifically a switchMap . This is basically an observer on some object that performs a function when the observed object changes. In this example, we initially set an id , and use the switchMap on it, fetching new data each time the reference to the id changes. This allows the ViewModel to take care of re-fetching data, etc, and allows the View (being the fragment or activity) to be completely void of any querying logic. I should stress that the ViewModel should be void of any logic or references to a View in your application. This defeats the purpose of the Architecture Components design pattern, and could lead to memory leaks, duplicate code, and bad coding practice. If necessary, use WeakReferences and ensure that any of your references are garbage collected properly when in use in the ViewModel.

Now, lets see how the actual View calls our code.

Step Three: The View

If you haven’t read about it already, I’ll quickly mention that ViewModels should be instantiated in the onActivityCreated method. Read more about it here:

Now, for the actual code, which consists of two parts: The Observer as a member variable in the view, and the instantiation of the ViewModel in onActivityCreated :

// Your fragment or activity

... private String mResult;

private long mId;

@Override

public void onChanged(

if (TextUtils.isEmpty(result)) {

return;

} private Observer mObserver = new Observer () {public void onChanged( @Nullable String result) {if (TextUtils.isEmpty(result)) {return; // Do whatever you want here, update your UI, etc

mResult = result;

}

}; @Override

public void onActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState); public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState); DemoViewModel viewModel =

ViewModelProviders.of(this).get(DemoViewModel.class);

viewModel.getData().observe(this, mObserver);

viewModel.setId(mId);

}