4

getLoaderManager has been deprecated with android 28. ViewModel, Repository and Room replace the loader functionality

I have found 2 articles:

https://medium.com/@JasonCromer/making-room-less-database-queries-using-the-new-android-architecture-components-81180ba6e7e0

and

https://medium.com/androiddevelopers/lifecycle-aware-data-loading-with-android-architecture-components-f95484159de4

but neither shows how to pass a cursor to my custom adapter (extends RecyclerViewCursorAdapter).

My PlaylistViewModel

  public class PlaylistViewModel extends AndroidViewModel {
@NonNull
private  MutableLiveData<Cursor> mLiveData = new MutableLiveData<>() ;
@NonNull
 private final DatabaseQueryRepository mDatabaseRepository;
private LiveData<Cursor> mResult;

public PlaylistViewModel(Application application) {
    super(application);
    mDatabaseRepository = DatabaseQueryRepository.newInstance();
    loadData();
}

private void loadData() {
mResult=  mDatabaseRepository.fetchData(getApplication().
            getContentResolver());
    }
}

My DatabaseRepository

  public class DatabaseQueryRepository {
private static final int TOKEN_QUERY=0;
private QueryHandler mQueryHandler;
private String[] datacolumns;
private String sortorder;
@NonNull
public static DatabaseQueryRepository newInstance(){
    return new DatabaseQueryRepository();
}

public MutableLiveData<Cursor> fetchData(ContentResolver contentResolver){
mQueryHandler = new QueryHandler(contentResolver);
    datacolumns = new String[]{
            MediaStore.Audio.Playlists.DATA,
            MediaStore.Audio.Playlists.NAME,
            MediaStore.Audio.Playlists._ID};
    sortorder = MediaStore.Audio.Playlists.NAME;
    Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
    final String criteria =
            MediaStore.Audio.Playlists.NAME
                 ;


    final MutableLiveData<Cursor> result = query(TOKEN_QUERY,uri,datacolumns,criteria, null, sortorder );

return result;
}
private MutableLiveData<Cursor> query(int token,
                                      Uri uri,
                                      @Nullable String[] projection,
                                      @Nullable String selection,
                                      @Nullable String[] selectionArgs,
                                      @Nullable String orderby
){
    final MutableLiveData<Cursor> result = new MutableLiveData<>();
    mQueryHandler.startQuery(token, result, uri,projection,selection, selectionArgs, orderby );

    return result;
}

private static class QueryHandler extends AsyncQueryHandler {

    public QueryHandler(ContentResolver cr) {
        super(cr);

    }

    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        //  super.onQueryComplete(token, cookie, cursor);
        MutableLiveData<String> mutabledata = (MutableLiveData<String>) cookie;

        try {
            switch (token) {
                case TOKEN_QUERY:
                    if (cursor != null && cursor.moveToFirst()) {
                        String demostring = cursor.getString(0);
                        int test = cursor.getCount();
                        String playlist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Playlists.NAME));
                        long playlist_id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID));

                        //    ((MutableLiveData<String>) cookie).setValue(demostring);

                    }
                    break;
            }
        } finally {
            if (cursor!=null){
            //    cursor.close();
            }
        }
    }


    @Override
    public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
        super.startQuery(token, cookie, uri, projection, selection, selectionArgs, orderBy);
    }
}

In protected void onQueryComplete, the cursor returns values It is this cursor that I want but the sample code closes it.????

In my fragment I have set up the observer

       @Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    PlaylistViewModel viewModel =
            ViewModelProviders.of(this).get(PlaylistViewModel.class);
    viewModel.getData().observe(this, data -> {

    });

Questions: - how do I get hold of the cursor. I know from documentation that it should be through the ViewModel. - Do I need to pass the cursor to the adapter or can I access the cursor from within the adapter. - I am confused about the Repository creating a cursor but what about mutableLivedata and how to access this?

Theo
  • 2,012
  • 1
  • 16
  • 29
  • AFIK Room is not working on cursors. When You get new cursor, You can call on CursorAdapter `changeCursor()`. Documentation says: "Change the underlying cursor to a new cursor. If there is an existing cursor it will be closed." - https://developer.android.com/reference/android/widget/CursorAdapter.html#changeCursor(android.database.Cursor). At this case closing is not needed but.. in other cases You should handle it Yourself. CursorLoaders were easy to use because they were handling it automatically. Now all goes in your hands. – deadfish Jun 04 '19 at 15:00
  • Note that you can still use Loaders, just use `LoaderManager.getInstance(this)` instead of `getLoaderManager()`. – ianhanniballake Jun 04 '19 at 15:11
  • Yes, true - `LoaderManager.getInstance()` is still in use. I was assuming OP needs solution with current code where he is already using `MutableLiveData` return values. – deadfish Jun 04 '19 at 15:14

1 Answers1

1

Update on my original question. Set up observer,variables and call that triggers the change. The onLiveDataFinished is my own method to set up the adapter

    private Observer<Cursor> mObserver = new Observer<Cursor>() {
    @Override
    public void onChanged(@Nullable Cursor cursor) {
        if(cursor!=null && cursor.moveToFirst()) {
            mAdapter = new PlaylistFragmentAdapter(getContext(), listener);
            onLiveDataFinished(cursor);
        }else{
            // switch off progressbar when nothing found
            mCallbacks.onPostExecute();
        }
    }
};


        mContext = getActivity();
    datacolumns = new String[]{

            MediaStore.Audio.Playlists.NAME,
            MediaStore.Audio.Playlists._ID};
    sort_order = MediaStore.Audio.Playlists.NAME;
    selection=
            MediaStore.Audio.Playlists.NAME
                          + " NOT LIKE \'%.m3u%\' AND "
                    + MediaStore.Audio.Playlists.NAME
                    + " NOT LIKE \'smb_%\'";

    selectionArgs=null;
    media_uri=uris.get_audio_playlists_uri();

       ViewModelFactory factory =
            new ViewModelFactory(
                    getActivity().getApplication(),
                    mId,
                    media_uri,
                    datacolumns,
                    selection,
                    selectionArgs,
                    sort_order);


    viewModel = ViewModelProviders.of(this, factory).get(com.flyingdutchman.newplaylistmanager.architecture_components.viewModel.class);
    viewModel.getData().observe(getViewLifecycleOwner(), mObserver);
    viewModel.setId(mId,media_uri,datacolumns,selection, selectionArgs, sort_order);
Theo
  • 2,012
  • 1
  • 16
  • 29