Skip to content

Commit f82f81f

Browse files
fix(anr): Resolve ANR in AutoSyncWorker by making uploads asynchronous
Refactored `uploadExamResult`, `uploadFeedback`, `uploadAchievement`, and `uploadResourceActivities` in `UploadManager` to be `suspend` functions. This moves the blocking database, serialization, and network operations to a background thread, resolving the ANR. Updated the call sites in `AutoSyncWorker` to launch these functions within a coroutine on the `applicationScope`. Also restored error handling to `uploadExamResult` that was removed in a previous refactoring.
1 parent bbb8822 commit f82f81f

File tree

2 files changed

+58
-82
lines changed

2 files changed

+58
-82
lines changed

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import org.ole.planet.myplanet.di.AutoSyncEntryPoint
1818
import org.ole.planet.myplanet.model.MyPlanet
1919
import org.ole.planet.myplanet.ui.sync.LoginActivity
2020
import org.ole.planet.myplanet.utilities.Constants.PREFS_NAME
21+
import kotlinx.coroutines.launch
2122
import org.ole.planet.myplanet.utilities.DialogUtils.startDownloadUpdate
2223
import org.ole.planet.myplanet.utilities.UrlUtils
2324
import org.ole.planet.myplanet.utilities.Utilities
@@ -78,10 +79,12 @@ class AutoSyncWorker(
7879
}
7980
if (!MainApplication.isSyncRunning) {
8081
MainApplication.isSyncRunning = true
81-
uploadManager.uploadExamResult(this)
82-
uploadManager.uploadFeedback(this)
83-
uploadManager.uploadAchievement()
84-
uploadManager.uploadResourceActivities("")
82+
MainApplication.applicationScope.launch {
83+
uploadManager.uploadExamResult()
84+
uploadManager.uploadFeedback()
85+
uploadManager.uploadAchievement()
86+
uploadManager.uploadResourceActivities("")
87+
}
8588
uploadManager.uploadUserActivities(this)
8689
uploadManager.uploadCourseActivities()
8790
uploadManager.uploadSearchActivity()

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

Lines changed: 51 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import com.google.gson.Gson
88
import com.google.gson.JsonObject
99
import dagger.hilt.android.qualifiers.ApplicationContext
1010
import io.realm.Realm
11+
import kotlinx.coroutines.Dispatchers
12+
import kotlinx.coroutines.withContext
1113
import io.realm.RealmResults
1214
import java.io.File
1315
import java.io.IOException
@@ -162,63 +164,44 @@ class UploadManager @Inject constructor(
162164
}
163165
}
164166

165-
fun uploadExamResult(listener: SuccessListener) {
167+
suspend fun uploadExamResult(): Boolean = withContext(Dispatchers.IO) {
166168
val apiInterface = client.create(ApiInterface::class.java)
167-
168-
try {
169+
return@withContext try {
169170
data class SubmissionData(val id: String?, val serialized: JsonObject, val _id: String?, val _rev: String?)
170-
171-
val submissionsToUpload = databaseService.withRealm { realm ->
171+
val submissionsToUpload = databaseService.withRealmAsync { realm ->
172172
realm.where(RealmSubmission::class.java).findAll()
173173
.filter { (it.answers?.size ?: 0) > 0 && it.userId?.startsWith("guest") != true }
174174
.map { sub ->
175-
val serialized = if (!TextUtils.isEmpty(sub._id)) {
176-
RealmSubmission.serializeExamResult(realm, sub, context)
177-
} else {
178-
RealmSubmission.serializeExamResult(realm, sub, context)
179-
}
175+
val serialized = RealmSubmission.serializeExamResult(realm, sub, context)
180176
SubmissionData(sub.id, serialized, sub._id, sub._rev)
181177
}
182178
}
183-
184-
var processedCount = 0
185-
var errorCount = 0
186-
187-
submissionsToUpload.processInBatches { data ->
179+
submissionsToUpload.forEach { data ->
188180
try {
189181
val response: JsonObject? = if (TextUtils.isEmpty(data._id)) {
190182
apiInterface?.postDoc(UrlUtils.header, "application/json", "${UrlUtils.getUrl()}/submissions", data.serialized)?.execute()?.body()
191183
} else {
192184
apiInterface?.putDoc(UrlUtils.header, "application/json", "${UrlUtils.getUrl()}/submissions/${data._id}", data.serialized)?.execute()?.body()
193185
}
194-
195186
if (response != null && data.id != null) {
196-
databaseService.withRealm { realm ->
187+
databaseService.withRealmAsync { realm ->
197188
realm.executeTransaction { transactionRealm ->
198189
transactionRealm.where(RealmSubmission::class.java).equalTo("id", data.id).findFirst()?.let { sub ->
199190
sub._id = getString("id", response)
200191
sub._rev = getString("rev", response)
201192
}
202193
}
203194
}
204-
processedCount++
205-
} else {
206-
errorCount++
207195
}
208-
} catch (e: IOException) {
209-
errorCount++
210-
e.printStackTrace()
211196
} catch (e: Exception) {
212-
errorCount++
213197
e.printStackTrace()
214198
}
215199
}
216-
217200
uploadCourseProgress()
218-
listener.onSuccess("Result sync completed successfully ($processedCount processed, $errorCount errors)")
201+
true
219202
} catch (e: Exception) {
220203
e.printStackTrace()
221-
listener.onSuccess("Error during result sync: ${e.message}")
204+
false
222205
}
223206
}
224207

@@ -240,11 +223,11 @@ class UploadManager @Inject constructor(
240223
return `object`
241224
}
242225

243-
fun uploadAchievement() {
244-
databaseService.withRealm { realm ->
245-
realm.executeTransactionAsync { transactionRealm: Realm ->
226+
suspend fun uploadAchievement() = withContext(Dispatchers.IO) {
227+
try {
228+
databaseService.withRealmAsync { realm ->
246229
val list: List<RealmAchievement> =
247-
transactionRealm.where(RealmAchievement::class.java).findAll()
230+
realm.where(RealmAchievement::class.java).findAll()
248231
list.processInBatches { sub ->
249232
try {
250233
if (sub._id?.startsWith("guest") == true) {
@@ -255,8 +238,9 @@ class UploadManager @Inject constructor(
255238
}
256239
}
257240
}
241+
} catch (e: Exception) {
242+
e.printStackTrace()
258243
}
259-
260244
}
261245

262246
private fun uploadCourseProgress() {
@@ -298,20 +282,11 @@ class UploadManager @Inject constructor(
298282
}
299283
}
300284

301-
fun uploadFeedback(listener: SuccessListener) {
285+
suspend fun uploadFeedback() = withContext(Dispatchers.IO) {
302286
val apiInterface = client.create(ApiInterface::class.java)
303-
databaseService.withRealm { realm ->
304-
realm.executeTransactionAsync(Realm.Transaction { transactionRealm: Realm ->
305-
val feedbacks: List<RealmFeedback> =
306-
transactionRealm.where(RealmFeedback::class.java).findAll()
307-
308-
if (feedbacks.isEmpty()) {
309-
return@Transaction
310-
}
311-
312-
var successCount = 0
313-
var errorCount = 0
314-
287+
try {
288+
databaseService.withRealmAsync { realm ->
289+
val feedbacks: List<RealmFeedback> = realm.where(RealmFeedback::class.java).findAll()
315290
feedbacks.processInBatches { feedback ->
316291
try {
317292
val res: Response<JsonObject>? = apiInterface?.postDoc(
@@ -326,26 +301,19 @@ class UploadManager @Inject constructor(
326301
val revElement = r["rev"]
327302
val idElement = r["id"]
328303
if (revElement != null && idElement != null) {
329-
feedback._rev = revElement.asString
330-
feedback._id = idElement.asString
331-
successCount++
332-
} else {
333-
errorCount++
304+
realm.executeTransaction {
305+
feedback._rev = revElement.asString
306+
feedback._id = idElement.asString
307+
}
334308
}
335-
} else {
336-
errorCount++
337309
}
338310
} catch (e: IOException) {
339-
errorCount++
340311
e.printStackTrace()
341312
}
342313
}
343-
}, {
344-
listener.onSuccess("Feedback sync completed successfully")
345-
}, { error ->
346-
listener.onSuccess("Feedback sync failed: ${error.message}")
347-
error.printStackTrace()
348-
})
314+
}
315+
} catch (e: Exception) {
316+
e.printStackTrace()
349317
}
350318
}
351319

@@ -846,36 +814,41 @@ class UploadManager @Inject constructor(
846814

847815
}
848816

849-
fun uploadResourceActivities(type: String) {
817+
suspend fun uploadResourceActivities(type: String) = withContext(Dispatchers.IO) {
850818
val apiInterface = client?.create(ApiInterface::class.java)
819+
val db = if (type == "sync") "admin_activities" else "resource_activities"
851820

852-
val db = if (type == "sync") {
853-
"admin_activities"
854-
} else {
855-
"resource_activities"
856-
}
821+
try {
822+
databaseService.withRealmAsync { realm ->
823+
val activities: RealmResults<RealmResourceActivity> =
824+
if (type == "sync") {
825+
realm.where(RealmResourceActivity::class.java).isNull("_rev").equalTo("type", "sync").findAll()
826+
} else {
827+
realm.where(RealmResourceActivity::class.java).isNull("_rev").notEqualTo("type", "sync").findAll()
828+
}
857829

858-
databaseService.withRealm { realm ->
859-
realm.executeTransactionAsync { transactionRealm: Realm ->
860-
val activities: RealmResults<RealmResourceActivity> =
861-
if (type == "sync") {
862-
transactionRealm.where(RealmResourceActivity::class.java).isNull("_rev").equalTo("type", "sync").findAll()
863-
} else {
864-
transactionRealm.where(RealmResourceActivity::class.java).isNull("_rev").notEqualTo("type", "sync").findAll()
865-
}
866-
activities.processInBatches { act ->
830+
activities.processInBatches { act ->
867831
try {
868-
val `object` = apiInterface?.postDoc(UrlUtils.header, "application/json", "${UrlUtils.getUrl()}/" + db, RealmResourceActivity.serializeResourceActivities(act))?.execute()?.body()
832+
val `object` = apiInterface?.postDoc(
833+
UrlUtils.header,
834+
"application/json",
835+
"${UrlUtils.getUrl()}/$db",
836+
RealmResourceActivity.serializeResourceActivities(act)
837+
)?.execute()?.body()
869838

870839
if (`object` != null) {
871-
act._rev = getString("rev", `object`)
872-
act._id = getString("id", `object`)
840+
realm.executeTransaction {
841+
act._rev = getString("rev", `object`)
842+
act._id = getString("id", `object`)
843+
}
873844
}
874845
} catch (e: IOException) {
875846
e.printStackTrace()
876847
}
848+
}
877849
}
878-
}
850+
} catch (e: Exception) {
851+
e.printStackTrace()
879852
}
880853
}
881854

0 commit comments

Comments
 (0)