Realm with Content Provider

2020-07-25 08:46发布

问题:

So, I've been used Realm for a while. For now, I have a task to share the login data with my other apps.

Since the login data is stored using Realm. I choose to use Content Provider.

I found an example: https://speakerdeck.com/androhi/realm-with-contentprovider

Unfortunately, I was unable to make it work. This is my Content Provider in app A

static final String[] sColumns = new String[]{
        "LoginResultData"
};

public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                    @Nullable String[] selectionArgs, @Nullable String sortOrder) {

    Realm mRealm = Realm.getDefaultInstance();

    RealmQuery<LoginResultData> query = mRealm.where(LoginResultData.class);
    LoginResultData result = query.findFirst();

    String json = new Gson().toJson(result);

    MatrixCursor matrixCursor = new MatrixCursor(sColumns);
    Object[] rowData = new Object[]{json};
    matrixCursor.addRow(rowData);

    return matrixCursor;
}

App B (which need to get the login data) got hang when I

getContentResolver.query(uri, null, null, null, null);

I don't know why but it worked well when I use SQlite. So I'm assuming that Realm doesn't work well with Content Provider smh. Is that true?

If not, please show me a sample to using Content Provider with Realm.

Thanks!

回答1:

Content Provider works well with RealmDB.

All you need to do is to override the CRUD methods inside the ContentProvider. Please take a look at this content provider class below. 3 things to note:

  1. RealmDB is initialized in the onCreate() method of the ContentProvider (not the app activity)

  2. You override the CRUD methods(Query, Insert, Delete, Update) in a manner appropriate for RealmDB. Check below samples.

  3. This is all you need to do. In the rest of the code, you will be using native components like recyclerview, adapter, loaders, services. Everywhere you need a query, you will call it with getContentResolver.query(uri, null, null, null, null);

TaskProvider.java

package com.example.rgher.realmtodo.data;

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.format.DateUtils;
import android.util.Log;

import io.realm.DynamicRealm;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmMigration;
import io.realm.RealmResults;
import io.realm.RealmSchema;

import com.example.rgher.realmtodo.data.DatabaseContract.TaskColumns;

public class TaskProvider extends ContentProvider {
    private static final String TAG = TaskProvider.class.getSimpleName();

    private static final int CLEANUP_JOB_ID = 43;
    private static final int TASKS = 100;
    private static final int TASKS_WITH_ID = 101;

    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        // content://com.example.rgher.realmtodo/tasks
        sUriMatcher.addURI(DatabaseContract.CONTENT_AUTHORITY,
                DatabaseContract.TABLE_TASKS,
                TASKS);

        // content://com.example.rgher.realmtodo/tasks/id
        sUriMatcher.addURI(DatabaseContract.CONTENT_AUTHORITY,
                DatabaseContract.TABLE_TASKS + "/#",
                TASKS_WITH_ID);
    }

    @Override
    public boolean onCreate() {

        //Innitializing RealmDB
        Realm.init(getContext());
        RealmConfiguration config = new RealmConfiguration.Builder()
                .schemaVersion(1)
                .migration(new MyRealmMigration())
                .build();
        Realm.setDefaultConfiguration(config);

        manageCleanupJob();

        return true;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null; /* Not used */
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {

        int match = sUriMatcher.match(uri);

        //Get Realm Instance
        Realm realm = Realm.getDefaultInstance();
        MatrixCursor myCursor = new MatrixCursor( new String[]{TaskColumns._ID, TaskColumns.DESCRIPTION
                , TaskColumns.IS_COMPLETE, TaskColumns.IS_PRIORITY
                , TaskColumns.DUE_DATE
        });

        try {
            switch (match) {
                //Expected "query all" Uri: content://com.example.rgher.realmtodo/tasks

                case TASKS:
                    RealmResults<RealmTask> tasksRealmResults = realm.where(RealmTask.class).findAll();
                    for (RealmTask myTask : tasksRealmResults) {
                        Object[] rowData = new Object[]{myTask.getTask_id(), myTask.getDescription(), myTask.getIs_complete()
                                , myTask.getIs_priority(), myTask.getDue_date()};
                        myCursor.addRow(rowData);
                        Log.v("RealmDB", myTask.toString());
                    }
                    break;

                //Expected "query one" Uri: content://com.example.rgher.realmtodo/tasks/{id}
                case TASKS_WITH_ID:
                    Integer id = Integer.parseInt(uri.getPathSegments().get(1));
                    RealmTask myTask = realm.where(RealmTask.class).equalTo("task_id", id).findFirst();
                    myCursor.addRow(new Object[]{myTask.getTask_id(), myTask.getDescription(), myTask.getIs_complete(), myTask.getIs_priority(), myTask.getDue_date()});
                    Log.v("RealmDB", myTask.toString());
                    break;
                default:
                    throw new UnsupportedOperationException("Unknown uri: " + uri);
            }


        myCursor.setNotificationUri(getContext().getContentResolver(), uri);
        } finally {
            realm.close();
        }
        return myCursor;

    }

    @Nullable
    @Override
    public Uri insert(Uri uri, final ContentValues contentValues) {
        //COMPLETE: Expected Uri: content://com.example.rgher.realmtodo/tasks

        //final SQLiteDatabase taskDb = mDbHelper.getReadableDatabase();
        int match = sUriMatcher.match(uri);
        Uri returnUri;

        //Get Realm Instance
        Realm realm = Realm.getDefaultInstance();
        try {
            switch (match) {
                case TASKS:
                    realm.executeTransaction(new Realm.Transaction() {
                        @Override
                        public void execute(Realm realm) {

                            Number currId = realm.where(RealmTask.class).max(TaskColumns._ID);
                            Integer nextId = (currId == null) ? 1 : currId.intValue() + 1;

                            RealmTask myNewTask = realm.createObject(RealmTask.class, nextId);
                            myNewTask.setDescription(contentValues.get(TaskColumns.DESCRIPTION).toString());
                            myNewTask.setIs_complete((Integer) contentValues.get(TaskColumns.IS_COMPLETE));
                            myNewTask.setIs_priority((Integer) contentValues.get(TaskColumns.IS_PRIORITY));
                            myNewTask.setDue_date((Long) contentValues.get(TaskColumns.DUE_DATE));
                        }
                    });
                    returnUri = ContentUris.withAppendedId(DatabaseContract.CONTENT_URI, '1');
                    break;

                default:
                    throw new UnsupportedOperationException("Unknown uri: " + uri);
            }

            getContext().getContentResolver().notifyChange(uri, null);
        }finally {
            realm.close();
        }
        return returnUri;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

        //Expected Uri: content://com.example.rgher.realmtodo/tasks/{id}
        Realm realm = Realm.getDefaultInstance();

            int match = sUriMatcher.match(uri);
            int nrUpdated = 0;
        try {
            switch (match) {
                case TASKS_WITH_ID:
                    Integer id = Integer.parseInt(uri.getPathSegments().get(1));
                    RealmTask myTask = realm.where(RealmTask.class).equalTo("task_id", id).findFirst();
                    realm.beginTransaction();
                    myTask.setIs_complete(Integer.parseInt(values.get(TaskColumns.IS_COMPLETE).toString()));
                    if (values.get(TaskColumns.DUE_DATE) != null) {
                        myTask.setDue_date(Long.valueOf(values.get(TaskColumns.DUE_DATE).toString()));
                    }
                    nrUpdated++;
                    realm.commitTransaction();
                    break;
                default:
                    throw new UnsupportedOperationException("Unknown uri: " + uri);
            }


        } finally {
            realm.close();
        }

        if (nrUpdated != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return nrUpdated;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count = 0;
        Realm realm = Realm.getDefaultInstance();
        try {
            switch (sUriMatcher.match(uri)) {
                case TASKS:
                    selection = (selection == null) ? "1" : selection;
                    RealmResults<RealmTask> tasksRealmResults = realm.where(RealmTask.class).equalTo(selection, Integer.parseInt(selectionArgs[0])).findAll();
                    realm.beginTransaction();
                    tasksRealmResults.deleteAllFromRealm();
                    count++;
                    realm.commitTransaction();
                    break;
                case TASKS_WITH_ID:
                    Integer id = Integer.parseInt(String.valueOf(ContentUris.parseId(uri)));
                    RealmTask myTask = realm.where(RealmTask.class).equalTo("task_id", id).findFirst();
                    realm.beginTransaction();
                    myTask.deleteFromRealm();
                    count++;
                    realm.commitTransaction();
                    break;
                default:
                    throw new IllegalArgumentException("Illegal delete URI");
            }
        } finally {
            realm.close();
        }
        if (count > 0) {
            //Notify observers of the change
            getContext().getContentResolver().notifyChange(uri, null);
        }

        return count;
    }

}

// Example of REALM migration
class MyRealmMigration implements RealmMigration {
    @Override
    public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

        RealmSchema schema = realm.getSchema();

        if (oldVersion != 0) {
            schema.create(DatabaseContract.TABLE_TASKS)
                    .addField(DatabaseContract.TaskColumns._ID, Integer.class)
                    .addField(DatabaseContract.TaskColumns.DESCRIPTION, String.class)
                    .addField(DatabaseContract.TaskColumns.IS_COMPLETE, Integer.class)
                    .addField(DatabaseContract.TaskColumns.IS_PRIORITY, Integer.class);
            oldVersion++;
        }

    }
}

You can find the full working app here https://github.com/rgherta/RealmTodo

Good luck