Firstly I have investigated other StaleDataExceptions on SO but none really answer, or lead me to a solution to my current App design problem.
I'm creating a Music Player App, which I initially designed to query the MediaStore.Audio
with a CursorLoader
to retrieve information from the Content Provider - this works fine, however obviously if you have a big music library there could be a more than desirable wait time getting all the information back when the app loads each time. My second approach was to start a remote Service
on app first launch, which queries the MediaStore.Audio
using a CursorLoader
and registers for any updates. When the Cusror
is returned I then run an AsyncTask
to process the information into an ArrayList of Custom POJO class that implements the Parcelable Interface
which then gets marshalled/demarshalled via an Intent
back to a handler in the Music App. This solution works well because if the app is closed, and reopened it just queries the Service
for the latest ArrayList
of custom Objects
, rather than having to query the MediaStore.Audio
at all - the Service
implements LoaderCallback<Cursor>
and always updates ArrayList
of custom Objects
and notifies the App if open, if not it has them ready for when the app is reopened.
Right - to the problem (ok took a bit of time, but necessary background information). The issue is : If I'm in another media app and start deleting music files in quick succession the registered Listener
in my service is calledback and a new cursor is delivered, however the AsyncTask
I am using to iterate over the existing cursor has a Race Condition
where it is closed by another Thread
/ CursorLoader
as the Cursor
is updated - exception thrown : android.database.StaleDataException: Attempted to access a cursor after it has been closed.
The current workaround I'm using is use setUpdateThrottle
to 20 seconds when registering the listener, this works, however means there is a delay in getting updated information back to the Service.
Is there any viable solution or approach where I can avoid this problem?
Thanks in advance.
UPDATE
A working viable solution, was actually quite easy to achieve - it initial problem however does illustrate my lack of experience with Loaders, however I've gained some knowledge on the way to finding a solution.
Solution:
I am still using a remote Service
. However instead of using a CursorLoader
- which was closing the cursor
whilst I was still processing it in the onLoadComplete()
callback, I am using a AsyncTaskLoader
. The AsyncTaskLoader
coupled with a ContentObserver
can still monitor for underlying data changes from a Uri
but is not directly coupled with the Cursor
object like a CursorLoader
. I am able to monitor for data changes and let the AsyncTaskLoader
know it needs to reload data (forceLoad()
), however this is queued, and doesn't affect the processing of loadInBackground() method - Once this returns, it simply cancels the task and starts the new task.