I have a content provider in on apk which is providing Rest Based Search results for multiple client apps.
Basically, the client requests a cursor via content resolver, then fires an intent service request to trigger the search..
In the provider, the cursor is returned to a table, and each intent service request populates the corresponding table, and triggers a NotifyDataSetChanged.
Works really well.. The only issue I am having is that if the Provider is killed for some reason (tested by doing pkill -9 com.myprovider.name) the cursor on the client apk does not change, but the app is not notified that the provider went away, and would need to re-connected to the db.. So it will continue to fire off service intents, and the cursor never gets updated b/c it is no longer tied to the underlying db..
I went through our code to try and see if we were catching some exceptions, that maybe hiding the underlying issue, but I am not seeing it..
I tried explicitly doing a cursor.close() in the provider in finalize / onLowMemory / shutdown().. None appear to get triggered.
I noticed that this happens in the logcat
09-10 13:58:03.015: I/ActivityThread(4268): Removing dead content provider: com.myprovider.name (called from the calling app)
Any way to get this notification from the calling app?
So after some research, I have a workable solution, but would if anyone has any other suggestions, they would be appreciated.
Due to the fact we are using a contentProvider to get a cursor, then just updating the back end data via IntentService. We grab the cursor when our user types the first character in of their search, if the cursor count = 0, then we show an empty list message, otherwise we display the list of items in that cursor..
** NOTE: Some of the cursor handling may need to be tweaked a bit, I did not see any crashes w/ our Adapter having a cursor ripped out and becoming null, but your mileage may vary.. ( This is more about using the contentProviderClient() )
** NOTE: Per the documentation, you MUST release the contentProviderClient() when not in use anymore.
Since a content provider should be returning a type, we can do the following..
So we define a member variable
private ContentProviderClient contentProviderClient = null;
Then when our user changes the search string we end up calling
public void setFilter( String searchFilter ) {
searchedString = !TextUtils.isEmpty(filter) ? filter : "";
boolean reload = false; // Reloads content provider
// contentProvider is null on first connection, assumed to be up on
// subsequent connections as we only close the cursor and
// contentProviderClient when we exit
if (contentProviderClient != null) {
try {
// getType will throw an exception if the instance of the
// contentProvider went away (killed by user/memory collection etc)
contentProviderClient.getType(searchFactoryUri);
} catch (RemoteException e) {
if( searchAdapter != null ) {
Cursor cursor = searchAdapter.getCursor();
cursor = null;
reload = true;
contentProviderClient.release();
}
}
}
// This is for first search initialization or reloading cursor
// if the content provider went away for some unknown reason.
if( this.searchAdapter == null || reload ){
Cursor cursor = getActivity().getContentResolver().query(
MY_URI,
null,
null,
null,
null);
contentProviderClient = getActivity()
.getContentResolver()
.acquireContentProviderClient(MY_URI);
cursor.setNotificationUri(getActivity.getContentResolver(), MY_URI);
// DO what ever you need to after getting cursor, a cursor loader
// would be a better implementation here, but simplifying it as
// I don't want to over-complicate the example.
myList.setAdapter(searchAdapter);
}
getActivity().startService(MyUtil.getSearchIntent( MY_URI, searchedString );
}
@Override
public void onDestroy() {
super.onDestroy();
// Cleanup adapter, cursor, provider client
if (searchAdapter != null) {
searchAdapter.changeCursor(null); // Closes existing cursor
}
if (contentProviderClient != null) {
contentProviderClient.release(); // Release client
}
}