Skip to content

Commit 9acc7ef

Browse files
Fix ANR in SyncManager by removing runBlocking
Refactored `SyncManager` to eliminate a synchronous blocking call (`runBlocking`) that was causing Application Not Responding (ANR) errors. Key changes: - Converted `SyncManager.start()` to a `suspend` function. - Moved the initialization logic of `ImprovedSyncManager` into a new `suspend fun initialize()` in `SyncManager`. - Called `SyncManager.initialize()` from `MainApplication.onCreate` within the `applicationScope` to ensure it runs asynchronously at startup. - Updated all call sites of `SyncManager.start()` in various fragments to launch it from a `lifecycleScope`. - Added `detectCustomSlowCalls()` to the `StrictMode` policy in `MainApplication` to help identify similar issues in the future.
1 parent 83a17ba commit 9acc7ef

File tree

7 files changed

+169
-147
lines changed

7 files changed

+169
-147
lines changed

app/src/main/java/org/ole/planet/myplanet/MainApplication.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import org.ole.planet.myplanet.model.RealmApkLog
4242
import org.ole.planet.myplanet.service.AutoSyncWorker
4343
import org.ole.planet.myplanet.service.NetworkMonitorWorker
4444
import org.ole.planet.myplanet.service.StayOnlineWorker
45+
import org.ole.planet.myplanet.service.SyncManager
4546
import org.ole.planet.myplanet.service.TaskNotificationWorker
4647
import org.ole.planet.myplanet.utilities.ANRWatchdog
4748
import org.ole.planet.myplanet.utilities.Constants.PREFS_NAME
@@ -68,6 +69,9 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
6869
@DefaultPreferences
6970
lateinit var defaultPreferences: SharedPreferences
7071

72+
@Inject
73+
lateinit var syncManager: SyncManager
74+
7175
companion object {
7276
private const val AUTO_SYNC_WORK_TAG = "autoSyncWork"
7377
private const val STAY_ONLINE_WORK_TAG = "stayOnlineWork"
@@ -190,6 +194,7 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
190194

191195
applicationScope.launch {
192196
initializeDatabaseConnection()
197+
syncManager.initialize()
193198
setupAnrWatchdog()
194199
scheduleWorkersOnStart()
195200
observeNetworkForDownloads()
@@ -228,6 +233,7 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
228233
.detectDiskReads()
229234
.detectDiskWrites()
230235
.detectNetwork()
236+
.detectCustomSlowCalls()
231237
.penaltyLog()
232238
.build()
233239
StrictMode.setThreadPolicy(threadPolicy)

app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ class SyncManager @Inject constructor(
7676
private var betaSync = false
7777
private val improvedSyncInitialized = AtomicBoolean(false)
7878

79-
fun start(listener: SyncListener?, type: String, syncTables: List<String>? = null) {
79+
suspend fun initialize() {
80+
if (improvedSyncInitialized.compareAndSet(false, true)) {
81+
improvedSyncManager.get().initialize()
82+
}
83+
}
84+
85+
suspend fun start(listener: SyncListener?, type: String, syncTables: List<String>? = null) {
8086
this.listener = listener
8187
if (!isSyncing) {
8288
settings.edit { remove("concatenated_links") }
@@ -87,9 +93,6 @@ class SyncManager @Inject constructor(
8793
val isSyncRequest = type.equals("sync", ignoreCase = true)
8894
if (useImproved && isSyncRequest) {
8995
val manager = improvedSyncManager.get()
90-
if (improvedSyncInitialized.compareAndSet(false, true)) {
91-
runBlocking { manager.initialize() }
92-
}
9396
val syncMode = if (settings.getBoolean("fastSync", false)) {
9497
SyncMode.Fast
9598
} else {

app/src/main/java/org/ole/planet/myplanet/ui/courses/CoursesFragment.kt

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -124,48 +124,50 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
124124
}
125125

126126
private fun startSyncManager() {
127-
syncManager.start(object : SyncListener {
128-
override fun onSyncStarted() {
129-
viewLifecycleOwner.lifecycleScope.launch {
130-
if (isAdded && !requireActivity().isFinishing) {
131-
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
132-
customProgressDialog?.setText(getString(R.string.syncing_courses_data))
133-
customProgressDialog?.show()
127+
lifecycleScope.launch {
128+
syncManager.start(object : SyncListener {
129+
override fun onSyncStarted() {
130+
viewLifecycleOwner.lifecycleScope.launch {
131+
if (isAdded && !requireActivity().isFinishing) {
132+
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
133+
customProgressDialog?.setText(getString(R.string.syncing_courses_data))
134+
customProgressDialog?.show()
135+
}
134136
}
135137
}
136-
}
137138

138-
override fun onSyncComplete() {
139-
viewLifecycleOwner.lifecycleScope.launch {
140-
if (isAdded) {
141-
customProgressDialog?.setText(getString(R.string.loading_courses))
142-
143-
lifecycleScope.launch {
144-
delay(3000)
145-
withContext(Dispatchers.Main) {
146-
customProgressDialog?.dismiss()
147-
customProgressDialog = null
148-
refreshCoursesData()
139+
override fun onSyncComplete() {
140+
viewLifecycleOwner.lifecycleScope.launch {
141+
if (isAdded) {
142+
customProgressDialog?.setText(getString(R.string.loading_courses))
143+
144+
lifecycleScope.launch {
145+
delay(3000)
146+
withContext(Dispatchers.Main) {
147+
customProgressDialog?.dismiss()
148+
customProgressDialog = null
149+
refreshCoursesData()
150+
}
149151
}
152+
prefManager.setCoursesSynced(true)
150153
}
151-
prefManager.setCoursesSynced(true)
152154
}
153155
}
154-
}
155156

156-
override fun onSyncFailed(msg: String?) {
157-
viewLifecycleOwner.lifecycleScope.launch {
158-
if (isAdded) {
159-
customProgressDialog?.dismiss()
160-
customProgressDialog = null
157+
override fun onSyncFailed(msg: String?) {
158+
viewLifecycleOwner.lifecycleScope.launch {
159+
if (isAdded) {
160+
customProgressDialog?.dismiss()
161+
customProgressDialog = null
161162

162-
Snackbar.make(requireView(), "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG).setAction("Retry") {
163-
startCoursesSync()
164-
}.show()
163+
Snackbar.make(requireView(), "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG).setAction("Retry") {
164+
startCoursesSync()
165+
}.show()
166+
}
165167
}
166168
}
167-
}
168-
}, "full", listOf("courses"))
169+
}, "full", listOf("courses"))
170+
}
169171
}
170172

171173
private suspend fun updateServerIfNecessary(mapping: ServerUrlMapper.UrlMapping) {

app/src/main/java/org/ole/planet/myplanet/ui/feedback/FeedbackListFragment.kt

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,40 +124,42 @@ class FeedbackListFragment : Fragment(), OnFeedbackSubmittedListener {
124124
}
125125

126126
private fun startSyncManager() {
127-
syncManager.start(object : SyncListener {
128-
override fun onSyncStarted() {
129-
viewLifecycleOwner.lifecycleScope.launch {
130-
if (isAdded && !requireActivity().isFinishing) {
131-
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
132-
customProgressDialog?.setText(getString(R.string.syncing_feedback))
133-
customProgressDialog?.show()
127+
lifecycleScope.launch {
128+
syncManager.start(object : SyncListener {
129+
override fun onSyncStarted() {
130+
viewLifecycleOwner.lifecycleScope.launch {
131+
if (isAdded && !requireActivity().isFinishing) {
132+
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
133+
customProgressDialog?.setText(getString(R.string.syncing_feedback))
134+
customProgressDialog?.show()
135+
}
134136
}
135137
}
136-
}
137138

138-
override fun onSyncComplete() {
139-
viewLifecycleOwner.lifecycleScope.launch {
140-
if (isAdded) {
141-
customProgressDialog?.dismiss()
142-
customProgressDialog = null
143-
onFeedbackSubmitted()
144-
prefManager.setFeedbackSynced(true)
139+
override fun onSyncComplete() {
140+
viewLifecycleOwner.lifecycleScope.launch {
141+
if (isAdded) {
142+
customProgressDialog?.dismiss()
143+
customProgressDialog = null
144+
onFeedbackSubmitted()
145+
prefManager.setFeedbackSynced(true)
146+
}
145147
}
146148
}
147-
}
148149

149-
override fun onSyncFailed(msg: String?) {
150-
viewLifecycleOwner.lifecycleScope.launch {
151-
if (isAdded) {
152-
customProgressDialog?.dismiss()
153-
customProgressDialog = null
150+
override fun onSyncFailed(msg: String?) {
151+
viewLifecycleOwner.lifecycleScope.launch {
152+
if (isAdded) {
153+
customProgressDialog?.dismiss()
154+
customProgressDialog = null
154155

155-
Snackbar.make(binding.root, "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG)
156-
.setAction("Retry") { startFeedbackSync() }.show()
156+
Snackbar.make(binding.root, "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG)
157+
.setAction("Retry") { startFeedbackSync() }.show()
158+
}
157159
}
158160
}
159-
}
160-
}, "full", listOf("feedback"))
161+
}, "full", listOf("feedback"))
162+
}
161163
}
162164

163165
private suspend fun updateServerIfNecessary(mapping: ServerUrlMapper.UrlMapping) {

app/src/main/java/org/ole/planet/myplanet/ui/resources/ResourcesFragment.kt

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -131,42 +131,47 @@ class ResourcesFragment : BaseRecyclerFragment<RealmMyLibrary?>(), OnLibraryItem
131131
}
132132

133133
private fun startSyncManager() {
134-
syncManager.start(object : SyncListener {
135-
override fun onSyncStarted() {
136-
viewLifecycleOwner.lifecycleScope.launch {
137-
if (isAdded && !requireActivity().isFinishing) {
138-
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
139-
customProgressDialog?.setText(getString(R.string.syncing_resources))
140-
customProgressDialog?.show()
134+
lifecycleScope.launch {
135+
syncManager.start(object : SyncListener {
136+
override fun onSyncStarted() {
137+
viewLifecycleOwner.lifecycleScope.launch {
138+
if (isAdded && !requireActivity().isFinishing) {
139+
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
140+
customProgressDialog?.setText(getString(R.string.syncing_resources))
141+
customProgressDialog?.show()
142+
}
141143
}
142144
}
143-
}
144145

145-
override fun onSyncComplete() {
146-
viewLifecycleOwner.lifecycleScope.launch {
147-
if (isAdded) {
148-
customProgressDialog?.dismiss()
149-
customProgressDialog = null
150-
refreshResourcesData()
151-
prefManager.setResourcesSynced(true)
146+
override fun onSyncComplete() {
147+
viewLifecycleOwner.lifecycleScope.launch {
148+
if (isAdded) {
149+
customProgressDialog?.dismiss()
150+
customProgressDialog = null
151+
refreshResourcesData()
152+
prefManager.setResourcesSynced(true)
153+
}
152154
}
153155
}
154-
}
155156

156-
override fun onSyncFailed(msg: String?) {
157-
viewLifecycleOwner.lifecycleScope.launch {
158-
if (isAdded) {
159-
customProgressDialog?.dismiss()
160-
customProgressDialog = null
161-
162-
Snackbar.make(requireView(), "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG
163-
).setAction("Retry") {
164-
startResourcesSync()
165-
}.show()
157+
override fun onSyncFailed(msg: String?) {
158+
viewLifecycleOwner.lifecycleScope.launch {
159+
if (isAdded) {
160+
customProgressDialog?.dismiss()
161+
customProgressDialog = null
162+
163+
Snackbar.make(
164+
requireView(),
165+
"Sync failed: ${msg ?: "Unknown error"}",
166+
Snackbar.LENGTH_LONG
167+
).setAction("Retry") {
168+
startResourcesSync()
169+
}.show()
170+
}
166171
}
167172
}
168-
}
169-
}, "full", listOf("resources"))
173+
}, "full", listOf("resources"))
174+
}
170175
}
171176

172177
private suspend fun updateServerIfNecessary(mapping: ServerUrlMapper.UrlMapping) {

app/src/main/java/org/ole/planet/myplanet/ui/survey/SurveyFragment.kt

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -109,46 +109,48 @@ class SurveyFragment : BaseRecyclerFragment<RealmStepExam?>(), SurveyAdoptListen
109109
}
110110

111111
private fun startSyncManager() {
112-
syncManager.start(object : SyncListener {
113-
override fun onSyncStarted() {
114-
launchWhenViewIsReady {
115-
if (!requireActivity().isFinishing) {
116-
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
117-
customProgressDialog?.setText("Syncing surveys...")
118-
customProgressDialog?.show()
112+
lifecycleScope.launch {
113+
syncManager.start(object : SyncListener {
114+
override fun onSyncStarted() {
115+
launchWhenViewIsReady {
116+
if (!requireActivity().isFinishing) {
117+
customProgressDialog = DialogUtils.CustomProgressDialog(requireContext())
118+
customProgressDialog?.setText("Syncing surveys...")
119+
customProgressDialog?.show()
120+
}
119121
}
120122
}
121-
}
122123

123-
override fun onSyncComplete() {
124-
prefManager.setExamsSynced(true)
125-
val job = launchWhenViewIsReady {
126-
customProgressDialog?.dismiss()
127-
customProgressDialog = null
128-
updateAdapterData(isTeamShareAllowed = false)
129-
}
130-
if (job == null) {
131-
customProgressDialog?.dismiss()
132-
customProgressDialog = null
124+
override fun onSyncComplete() {
125+
prefManager.setExamsSynced(true)
126+
val job = launchWhenViewIsReady {
127+
customProgressDialog?.dismiss()
128+
customProgressDialog = null
129+
updateAdapterData(isTeamShareAllowed = false)
130+
}
131+
if (job == null) {
132+
customProgressDialog?.dismiss()
133+
customProgressDialog = null
134+
}
133135
}
134-
}
135136

136-
override fun onSyncFailed(msg: String?) {
137-
val job = launchWhenViewIsReady {
138-
customProgressDialog?.dismiss()
139-
customProgressDialog = null
140-
_binding?.let { binding ->
141-
Snackbar.make(binding.root, "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG)
142-
.setAction("Retry") { startExamSync() }
143-
.show()
137+
override fun onSyncFailed(msg: String?) {
138+
val job = launchWhenViewIsReady {
139+
customProgressDialog?.dismiss()
140+
customProgressDialog = null
141+
_binding?.let { binding ->
142+
Snackbar.make(binding.root, "Sync failed: ${msg ?: "Unknown error"}", Snackbar.LENGTH_LONG)
143+
.setAction("Retry") { startExamSync() }
144+
.show()
145+
}
146+
}
147+
if (job == null) {
148+
customProgressDialog?.dismiss()
149+
customProgressDialog = null
144150
}
145151
}
146-
if (job == null) {
147-
customProgressDialog?.dismiss()
148-
customProgressDialog = null
149-
}
150-
}
151-
}, "full", listOf("exams"))
152+
}, "full", listOf("exams"))
153+
}
152154
}
153155

154156
private suspend fun updateServerIfNecessary(mapping: ServerUrlMapper.UrlMapping) {

0 commit comments

Comments
 (0)