Reputation: 1152
So I've been working with IOS development for the past couple of months and have used Core Data and NSFetchedResultsController extensively.
Core Data + NSFetchedResultsController : Detects changes to the database automatically and updates table elements accordingly.
Now I've switched over to Android development and I've been looking for an equivalent of the above. I've looked at various different classes available and am a little confused.
I think it's also worth mentioning I am using greendao, and I am using its generated ContentProvider.
This is where I got a little iffy. This is my assumption.
swap()
inside the onLoadFinished()
, resulting in a update of the table. Keeping the above in mind, I created a ContentProvider and implemented LoaderManager but the function onLoadFinished()
isn't being called when I persist a new object (using greenDao) -- which led me to start questioning whether I am understanding the process correctly. Here is a snippet of code to show what I have generally coded so far.
public class MissionPageFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
// Various initialization methods
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(0, savedInstanceState, this);
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
ContentProvider.daoSession = Main.daoSession;
Uri uri = ContentProvider.CONTENT_URI;
// Other initializations
CursorLoader cursorLoader = new CursorLoader(getContext(), uri, projections, null, null, null);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
System.out.println("Should be called when a new item is persisted... but not called =(");
}
// Other methods
}
If someone could confirm whether I am thinking about the process correctly and/or shed light on what might be going wrong, I would appreciate it a lot.
Here is a snippet of the query()
function in the ContentProvider subclass generated by greenDao.
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case MISSION_DIR:
queryBuilder.setTables(TABLENAME);
break;
case MISSION_ID:
queryBuilder.setTables(TABLENAME);
queryBuilder.appendWhere(PK + "="
+ uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
SQLiteDatabase db = getDatabase();
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
I just wanted to share how I implemented all of the aforementioned classes with GreenDao. Let me know if there's a better way to do this, but I feel like this is good enough.
Generate a ContentProvider subclass by adding the following code snippet to the GreenDaoGenerator class
Entity entity = new Entity();
entity.addContentProvider();
Check if the ContentProvider subclass methods are called appropriately. Add the javadoc part to AndroidManifest.xml. For me, I also had to change the generated ContentProvider class BASE_PATH
variable to another variable like the following.
public static final String BASE_PATH = "MYTABLENAME";
Add LoaderManager, Loader to the Activity/Fragment class like the one I wrote above. You will also have to set the daoSession
object before the ContentProvider is used. I've moved the below snippet to another initializer class so that other Activity/Fragment classes may use it as well.
ContentProvider.daoSession = Main.daoSession;
I'm using a RecyclerView so I subclassed the CursorRecyclerViewAdapter class provided here https://gist.github.com/skyfishjy/443b7448f59be978bc59 with a little customization. You should be able to use a simple CursorAdapter if you are using a ListView. Since the Loader
is listening for updates, the Adapter
doesn't need to listen for updates.
Now the ContentProvider subclass is able to update the view due to this line
getContext().getContentResolver().notifyChange(uri, null);
which is automatically generated by GreenDao. What this means is that you can use ContentProvider for all CRUD operations and the view will automatically update accordingly. This seemed to defeat the purpose of using an ORM. As such, I now do normal GreenDao operations and call the notifyChange
manually after each insertion, deletion, update.
GreenDaoObj obj = new GreenDaoObj();
obj.insert();
getContext.getContentResolver().notifyChange(MyContentProvider.CONTENT_URI, null);
I don't think there is a better way than this as I don't really want to touch the GreenDao generated code for Model and Dao objects. I suppose one could add custom CRUD functions which nests the generated CRUD methods, but that should be trivial.
I've been looking around and there wasn't a well documented way to use GreenDao with Loader/LoaderManager so I thought I would organize this here. Hopefully this helps anyone who is planning on implementing this.
Upvotes: 2
Views: 666
Reputation: 7772
There are just a couple of things you didn't get exactly right.
ContentProvider
s are Android components that provide access to data. That data can be stored in a relational database, flat file, a remote server, etc. Providers have a common REST-like interface and serve as an abstraction layer over the different options for data storage you have. ContentProvider
s can also be accessed from external applications (if configured properly) and this makes them the default way of sharing data between apps. Accessing them doesn't require a LoaderManager
. They should be accessed via a ContentResolver
.LoaderManager
is responsible for the Loader
lifecycle and coordinating it with the Activity
and Fragment
lifecycles.Loader
s provide a lifecycle aware way to load data into activities or fragments. The CursorLoader
is a particular implementation that comes with a tone of features - it uses a worker thread to keep the loading of the UI thread, it abstract using a ContentResolver
to access a ContentProvider
and also sets up a ContentObserver
. The ContentObserver
will listen for updates on the query URL and reload data if needed.In order to get ContentObserver
notifications to work, you have to make sure a couple of things are in place. First, your ContentProvider
should propagate changes to the observers. This is done using getContext().getContentResolver().notifyChange(url, observer)
. Then, if you have a CursorLoader
that executed a query on the notified URL, it will be automatically reloaded.
Upvotes: 3