I have android app that talks to server and syncs some data int SQLite database. There is service that runs every 5 minutes and spans AsyncTask that grabs data and insert/update data into database.
Here is my issue. When background process updates database it might take some considerable time. If user trying to use application at the same time and opens ListView with cursor bound to that same table - I get deadlock situation and ANR report.
How should I solve this problem properly?
I FEEL like I need to check from UI if asyncprocess running (how?) and if yes then show user that background refresh going on and load list as soon as process accessing data finished on background.
What is proper way to solve this issue?
EDIT
Here is report
DALVIK THREADS:
"main" prio=5 tid=1 WAIT
| group="main" sCount=1 dsCount=0 s=N obj=0x2aac88b8 self=0xcd58
| sysTid=3967 nice=0 sched=0/0 cgrp=unknown handle=1876207664
at java.lang.Object.wait(Native Method)
- waiting on <0x2aac8948> (a java.lang.VMThread)
at java.lang.Thread.parkFor(Thread.java:1535)
at java.lang.LangAccessImpl.parkFor(LangAccessImpl.java:48)
at sun.misc.Unsafe.park(Unsafe.java:317)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:131)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:790)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:823)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1153)
at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:261)
at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:375)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:61)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:283)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:264)
at android.content.ContentResolver.query(ContentResolver.java:251)
****************at com.idatt.data.PreferenceData.getPreferenceString(PreferenceData.java:119)
at com.idatt.data.PreferenceData.getPreferenceBoolean(PreferenceData.java:109)
at com.idatt.common.Preferences.getIsShowRates(Preferences.java:136)
at com.idatt.activities.TripListActivity$TripViewDataHolder.populateFrom(TripListActivity.java:211)
at com.idatt.activities.TripListActivity$TripListCursorAdapter.getView(TripListActivity.java:147)
at android.widget.AbsListView.obtainView(AbsListView.java:1294)
at android.widget.ListView.measureHeightOfChildren(ListView.java:1198)
at android.widget.ListView.onMeasure(ListView.java:1109)
at android.view.View.measure(View.java:8187)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3146)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1012)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:381)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:304)
at android.view.View.measure(View.java:8187)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3146)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1012)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:381)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:304)
at android.view.View.measure(View.java:8187)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3146)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:245)
at android.view.View.measure(View.java:8187)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3146)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:245)
at android.view.View.measure(View.java:8187)
at android.view.ViewRoot.performTraversals(ViewRoot.java:801)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1727)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4644)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636)
at dalvik.system.NativeStart.main(Native Method)
"Binder Thread #3" prio=5 tid=14 NATIVE
| group="main" sCount=1 dsCount=0 s=N obj=0x3033f218 self=0x2394d0
| sysTid=4305 nice=0 sched=0/0 cgrp=unknown handle=2332488
at dalvik.system.NativeStart.run(Native Method)
"AsyncTask #5" prio=5 tid=13 WAIT
| group="main" sCount=1 dsCount=0 s=N obj=0x300dc038 self=0x25f0e0
| sysTid=4073 nice=10 sched=0/0 cgrp=unknown handle=2965040
at java.lang.Object.wait(Native Method)
- waiting on <0x3013c610> (a java.lang.VMThread)
at java.lang.Thread.parkFor(Thread.java:1535)
at java.lang.LangAccessImpl.parkFor(LangAccessImpl.java:48)
at sun.misc.Unsafe.park(Unsafe.java:317)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:131)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:790)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:823)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1153)
at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:261)
at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:375)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1533)
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1410)
at com.idatt.data.Provider.insert(Provider.java:198)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:174)
at android.content.ContentResolver.insert(ContentResolver.java:587)
*************at com.idatt.data.TripData.InsertTrip(TripData.java:272)
at com.idatt.common.AsyncProcessor.GetUserTrips(AsyncProcessor.java:252)
at com.idatt.common.AsyncProcessor.doInBackground(AsyncProcessor.java:101)
at com.idatt.common.AsyncProcessor.doInBackground(AsyncProcessor.java:27)
at android.os.AsyncTask$2.call(AsyncTask.java:185)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
at java.lang.Thread.run(Thread.java:1096)
"AsyncTask #4" prio=5 tid=12 WAIT
| group="main" sCount=1 dsCount=0 s=N obj=0x302ffd28 self=0x4e8f30
| sysTid=4072 nice=10 sched=0/0 cgrp=unknown handle=5163584
at java.lang.Object.wait(Native Method)
- waiting on <0x302ffbd8> (a java.lang.VMThread)
at java.lang.Thread.parkFor(Thread.java:1535)
at java.lang.LangAccessImpl.parkFor(LangAccessImpl.java:48)
at sun.misc.Unsafe.park(Unsafe.java:317)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:131)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:790)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:823)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1153)
at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:261)
at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:375)
at android.database.sqlite.SQLiteProgram.close(SQLiteProgram.java:291)
at android.database.sqlite.SQLiteQuery.close(SQLiteQuery.java:133)
at android.database.sqlite.SQLiteCursor.close(SQLiteCursor.java:502)
at android.database.CursorWrapper.close(CursorWrapper.java:45)
at android.content.ContentResolver$CursorWrapperInner.close(ContentResolver.java:1355)
************at com.idatt.data.LogData.getLogItems(LogData.java:118)
at com.idatt.data.LogData.getLogItems(LogData.java:125)
at com.idatt.common.AsyncProcessor.PostDeviceLogs(AsyncProcessor.java:210)
at com.idatt.common.AsyncProcessor.doInBackground(AsyncProcessor.java:96)
at com.idatt.common.AsyncProcessor.doInBackground(AsyncProcessor.java:27)
at android.os.AsyncTask$2.call(AsyncTask.java:185)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
at java.lang.Thread.run(Thread.java:1096)
"AsyncTask #3" prio=5 tid=11 WAIT
| group="main" sCount=1 dsCount=0 s=N obj=0x302ec858 self=0x4ff440
| sysTid=4071 nice=10 sched=0/0 cgrp=unknown handle=2984848
at java.lang.Object.wait(Native Method)
- waiting on <0x302ec9b0> (a java.lang.VMThread)
at java.lang.Thread.parkFor(Thread.java:1535)
at java.lang.LangAccessImpl.parkFor(LangAccessImpl.java:48)
at sun.misc.Unsafe.park(Unsafe.java:317)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:131)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1996)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:359)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1001)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1061)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
at java.lang.Thread.run(Thread.java:1096)
"AsyncTask #2" prio=5 tid=10 WAIT
| group="main" sCount=1 dsCount=0 s=N obj=0x302c31a8 self=0x4dca78
| sysTid=4062 nice=10 sched=0/0 cgrp=unknown handle=5111608
at java.lang.Object.wait(Native Method)
- waiting on <0x3027ab38> (a java.lang.VMThread)
at java.lang.Thread.parkFor(Thread.java:1535)
at java.lang.LangAccessImpl.parkFor(LangAccessImpl.java:48)
at sun.misc.Unsafe.park(Unsafe.java:317)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:131)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:790)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:823)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1153)
at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:261)
at android.database.sqlite.SQLiteDatabase....
See trace attached. Check lines that I marked with ** I'm not great at reading it but here is my conclusions.. Seems like it is blocking when trying to execute
com.idatt.data.PreferenceData.getPreferenceString
This call opens DB in read-only mode (it's going through my COntentProvider). And this call happens on UI thread.
What's bad - I noticed 2 other calls: InsertTrips and getLogData. InsertTrips goes into database but getLogData should be read-only as well. However, they both part of same AsyncTask. So, it seems like one instance of AsyncTask was originated from Service/Alarm (every 5 minutes deal) and second instance was originated by user (I give them option to manually refresh).
Situation look like this:
- App updates itself on schedule
- User uses app and calls refresh manually
- After user initiated manual refresh - she tries to use app where it's accessing same data..