I am a bit confused about CursorLoader and a regular call with the Content Resolver :
There is an example with the CursorManager : (http://developer.android.com/reference/android/app/LoaderManager.html) ,
where we use return new CursorLoader to fetch the data.
And another example with contentResolver, to insert some data in the content Provider :
mUri = getContentResolver().insert(intent.getData(), null);
What if we wanted to use a Loader instance to insert data in the content provider, instead of, like in the example above, just query the data. How should we do? And would it be necessary?
Thanks
Loaders are not for insert/update/delete queries, only for loading data. You'd want to use an AsyncQueryHandler to do those operations (best for operations tied to user interactions):
AsyncQueryHandler handler = new AsyncQueryHandler(getContentResolver()) {
#Override
protected void onInsertComplete(int token, Object cookie, Uri uri) {
// Do something now that your insert is complete
}
};
handler.startInsert(
0, // token, passed on to onInsertComplete
null, // cookie, passed on to onInsertComplete
initialValues); // ContentValues to insert
Related
How to check inside UI whether data exist in a table?
By using Loader or just query()?
Which way is better?
1) Using Loader.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().getSupportLoaderManager().restartLoader(POPULATE_CATEGORY_LIST_VIEW_LOADER, null, this);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
switch (loader.getId()) {
case POPULATE_CATEGORY_LIST_VIEW_LOADER:
if (!data.moveToFirst()) {
// load data from server and store data to db
}
break;
default:
break;
}
}
2) Using getContentResolver().query() from UI
Cursor locationCursor = getContext().getContentResolver().query(
MenuContract.CategoryEntry.CONTENT_URI,
new String[]{MenuContract.CategoryEntry._ID},
null,
null,
null);
if(!locationCursor.moveToFirst()) {
// load data from server and store data to db
}
I just need to check whether data exist id db. Then if there are no data in a table, load data from server and put to an Adapter, otherwise just put data to the Adapter.
How to do it correctly using Loaders?
I understood that in both ways we need to wait until data base gives an answer. So maybe just querying a Cursor is enough for this task or it is bad practice to query data base from UI thread directly?
It's highly recommended to do database queries in a separate thread but if the data you're querying is very small then you can do it in the UI thread without induce lag.
I'm developing an app based on Google IO presentation architecture using the first approach. Basically I have a Service, ContentProvider backed by SQLite DB and I also use Loaders.
I need a way to update UI when changes to my database occur. For instance a user might want to add an item into his basket. After I insert the item id into the basket table I want to update the UI. What approach should I use? I've seen very little information on ContentObserver so far. Is it the way to go?
In the query method of your ContentProvider attach a listener to the returned cursor:
Cursor cursor = queryBuilder.query(dbConnection, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
Then in your insert/update/delete methods use code like this:
final long objectId = dbConnection.insertOrThrow(ObjectTable.TABLE_NAME, null, values);
final Uri newObjectUri = ContentUris.withAppendedId(OBJECT_CONTENT_URI, objectId );
getContext().getContentResolver().notifyChange(newObjectUri , null);
Your CursorLoader will be notified and the OnLoadFinished(Loader, Cursor) will be called again.
If you're not using a Loader, the ContentObserver is the way to go, with a few lines of code you are notified on db changes (but you will need to requery manually).
private ContentObserver objectObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
restartObjectLoader();
}
};
Remember to call in onResume():
getContentResolver().registerContentObserver(ObjectProvider.OBJECT_CONTENT_URI, false, objectObserver);
and in onPause():
getContentResolver().unregisterContentObserver(objectObserver);
Update: UI Changes
This is a larger topic because it depends on the Adapter you use to fill the ListView or RecyclerView.
CursorAdapter
In onLoadFinished(Loader loader, Cursor data)
mAdapter.swapCursor(data);
ArrayAdapter
In onLoadFinished(Loader loader, Cursor data)
Object[] objects = transformCursorToArray(data); //you need to write this method
mAdapter.setObjects(objects); //You need to wrie this method in your implementation on the adapter
mAdapter.notifyDataSetChange();
RecyclerView.Adapter
In onLoadFinished(Loader loader, Cursor data)
Object[] objects = transformCursorToArray(data); //you need to write this method
//Here you have more mAdapter.notify....()
Read from here for different way to notify the RecyclerView.Adapter.
If you are using a list, you can fill adapter again and set it to your list. Or try to inform data set change.
I currently use Loaders to grab data from my ContentProvider (to enable auto-updating of my Cursors). This approach is straight-forward for Querying the database, though, it seems ill suited for any other DB operation (such as Insert, Update, Delete).
My questions are:
Do all SQLite operations need to be on a background thread, or is it safe to do simple operations like Inserting, Updating, or Deleting a single row on the UI thread?
What is a nice design patter to ensure all queries go through a background thread? I would like to implement AsyncTask, should I create a SuperTask so to speak that extends AsyncTask and Executes each SQLite operation? (Bonus: Can you provide bare-bones example?)
I have done SQLite operations on my UI Thread. I guess the question really becomes whether your queries will ever take a long time or not. I've never had my application crash from taking too long to execute SQL calls on my SQLite database.
With that said, if you plan on writing complex queries that can take time to load you would want to run it as an AsyncTask or Thread and use callbacks to update your UI if need be.
This is a great tutorial on SQLite on Android (It also addresses some of the complex sql timing issues you were talking about):
http://www.vogella.com/tutorials/AndroidSQLite/article.html
All SQLite operations do not need to be on a background, but should be. Even simple row updates can impact the UI thread and therefore application responsiveness.
Android includes the AsyncQueryHandler abstract class:
A helper class to help make handling asynchronous ContentResolver queries easier.
Here are two example implementations from Using AsyncQueryHandler to Access Content Providers Asynchronously in Android. A member class:
class MyQueryHandler extends AsyncQueryHandler {
public MyQueryHandler(ContentResolver cr) {
super(cr);
}
#Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// query() completed
}
#Override
protected void onInsertComplete(int token, Object cookie, Uri uri) {
// insert() completed
}
#Override
protected void onUpdateComplete(int token, Object cookie, int result) {
// update() completed
}
#Override
protected void onDeleteComplete(int token, Object cookie, int result) {
// delete() completed
}
}
An anonymous class:
AsyncQueryHandler queryHandler = new AsyncQueryHandler(getContentResolver()) {
#Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if (cursor == null) {
// Some providers return null if an error occurs whereas others throw an exception
}
else if (cursor.getCount() < 1) {
// No matches found
}
else {
while (cursor.moveToNext()) {
// Use cursor
}
}
}
};
Further details:
Implementing AsyncQueryHandler
http://www.trustydroid.com/blog/2014/10/07/using-asyncqueryhandler-with-content-provider/
I have been working on a small To-Do list app. I used CursorLoader to update the ToDolistview from a content provider. I have a written a function onNewItemAdded(), which is called when user enters a new item in the text view and clicks enter. Refer below:
public void onNewItemAdded(String newItem) {
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(ToDoContentProvider.KEY_TASK, newItem);
cr.insert(ToDoContentProvider.CONTENT_URI, values);
// getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}
#Override
protected void onResume() {
super.onResume();
//getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(this,
ToDoContentProvider.CONTENT_URI, null, null, null, null);
Log.e("GOPAL", "In the onCreateLoader");
return loader;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
int keyTaskIndex = cursor.getColumnIndexOrThrow(ToDoContentProvider.KEY_TASK);
Log.e("GOPAL", "In the onLoadFinished");
todoItems.clear();
if (cursor.moveToNext() == false) Log.e("GOPAL", "Empty Cursor");
else {
while (cursor.moveToNext()) {
ToDoItem newItem = new ToDoItem(cursor.getString(keyTaskIndex));
todoItems.add(newItem);
}
aa.notifyDataSetChanged(); // aa is arrayadapter used for the listview
}
}
I have read, CursorLoader automatically updates the view, whenever there is a data change in the content provider db. That means I suppose, getLoaderManager().restartLoader(0, null, this) has to be called implicitly whenever there is a change in data, right?
But that is not happening. Whenever I add a new item (the item is added to the db from onNewItemAdded, but restartLoader is not explicitly called), pause this activity and resume it back. I don't see any implicit call to restartLoader(even though db is changed) and the listview also is not updated with new item added. Why is that? How does a CursorLoader automatically updates the view even if app is not active???
Thanks :)
EDIT: I have also used getContext().getContentResolver().notifyChange(insertedId, null) in insert of my content provider.
I found the answer for my question. In general, CursorLoader doesn't automatically detect data changes and load them to view. We need to track URI for changes. This can be done by following steps:
Registering an Observer in content resolver through cursor using: (Done in the query method of ContentProvider)
cursor.setNotificationUri(getContext().getContentResolver(), uri);
Now when there is any change in URI underlying data using insert()/delete()/update(), we notify the ContentResolver about the change using:
getContext().getContentResolver().notifyChange(insertedId, null);
This is received by the observer, we registered in step-1 and this calls to ContentResolver.query(), which inturn calls ContentProvider's query() method to return a fresh cursor to LoaderManager. LoaderManager calls onLoadFinished() passing this cursor, along with the CursorLoader where we update the View (using Adapter.swapCursor()) with fresh data.
For Custom AsyncTaskLoaders:
At times we need our custom loader instead of CursorLoader. Here we can use someother object other than cursor to point to the loaded data (like list etc). In this we won't be having previlige to notify ContentResolver through cursor. The application may also not have a content Provider, to track URI changes. In this scenario we use BroadcastReceiver or explicit ContentObserver to achieve automatic view updation. This is as follows:
We need to define our custom loader which extends AsyncTaskLoader and implements all its abstract methods. Unlike CursorLoader, our Custom Loader may or may not use a content Provider and it's constructor may not call to ContentResolver.query(), when this loader is instatiated. So we use a broadcast receiver to serve the purpose.
We need to instantiate a BroadCastReceiver or ContentObserver in OnStartLoading() method of abstract AsyncTaskLoader class.
This BroadCast receiver should be defined to receive data-changing broadcasts from content provider or any system events(Like new app installed) and it has to call loader's onContentChanged() method, to notify the loader about the data change. Loader automatically does the rest to load the updated data and call onLoadFinished() to update the view.
For more details refer this: http://developer.android.com/reference/android/content/AsyncTaskLoader.html
I found this very useful for clear explanation : http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html
Well, I think you can restart the loader on certain events. E.g. in my case I have an activity of TODOs. On clicking 'add' option, it launches new activity which has view to feed new TODO.
I am using following code in parent activity's onActivityResult()
getLoaderManager().restartLoader(0, null, this);
It works fine for me. Please share if there is any better approach.
get a reference to your loader while initializing as follows
Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this);
then create a class that extends ContentObserver as follows
class DataObserver extends ContentObserver {
public DataObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
dayWeatherLoader.forceLoad();
}
}
Then register content observer inside onResume lifecycle method as follows
#Override
public void onResume() {
super.onResume();
getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler()));
}
Whenever there is a change in the underlying data of content provider, the onChange method of contentobserver will be called where you can ask loader to load the data again
I want to know the context in which getContentResolver() is called?
I have a scenario like this:
I have an activity A that calls a method myFunc() of class B which is not an activity.
So, in class B I have to use getContentResolver(). I directly called getContentResolver(). It was showing error. Then I called myFunc(Acitivy act) from the activity and called act.getContentResolver() which solved my problem. Is this the only way to call getContentResolver(), which means it can be used in context with activity or can be used alone.
getContentResolver() is method of class android.content.Context, so to call it you definitely need an instance
of Context ( Activity or Service for example).
You can use like this:
getApplicationContext().getContentResolver()
with the proper context.
The getContentResolver()method is also used when you query a Contact, using a Cursor object. I have used getContentResolver() to query the Android phone Contacts app, looking for contact info from a person's phone number, to include in my app. The different elements in a query (as shown below) represent what kind of contact details you want, and if they should be ordered, etc. Here is another example.
From the Content Provider Basics page from Android docs.
// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
mProjection, // The columns to return for each row
mSelectionClause // Selection criteria
mSelectionArgs, // Selection criteria
mSortOrder); // The sort order for the returned rows
import android.content.Context;
import android.content.ContentResolver;
context = (Context)this;
ContentResolver result = (ContentResolver)context.getContentResolver();
//create activity object to get activity from Activity class for use to content resolver
private final Activity ActivityObj;
//create constructor with ActivityObj to get activity from Activity class
public RecyclerViewAdapterClass(Activity activityObj) {
this.ActivityObj = activityObj;
}
ActivityObj.getContentResolver(),.....,.....,null);
Access contentResolver in Kotlin , inside activities, Object classes &... :
Application().contentResolver
This one worked for me
getBaseContext();
A solution would be to get the ContentResolver from the context
ContentResolver contentResolver = getContext().getContentResolver();
Link to the documentation : ContentResolver