Android Worker Class Not Restoring Media Files from ZIP After Successful Database Restoration

  Kiến thức lập trình

I’m working on an Android project that involves a Worker class designed to process a ZIP file input. This ZIP file is structured to include a database file located at /databases/GazeApp.db and various media files under multiple subfolders like /contacts/1/gallery/*.jpg among others.

The goal within the restoreDataFromZip() method is to unpack the ZIP file, iterating over its contents to restore a Room database from the .db file, which is currently functioning as expected. However, I’m encountering an issue where the media files within the /contacts/ subfolder are not being restored to the application’s private storage as intended. Below is a snippet of the current implementation:

@HiltWorker
class DatabaseRestoreWorker @AssistedInject constructor(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    private val channelId = "restore_channel"
    private val channelName = "Database Restore"
    private val ongoingNotificationId = 3

    override suspend fun doWork(): Result {
        val context = applicationContext

        // Retrieve the ZIP file URI from the input data
        val zipFileUriString = inputData.getString(Const.ZIP_BACKUP_FILE_KEY)
        val zipFileUri = zipFileUriString?.let { Uri.parse(it) }

        // Check if the URI is not null
        if (zipFileUri != null) {

            // Step 2: Restore data from the ZIP file
            val success = restoreDataFromZip(context, zipFileUri)

            // Step 2: Notify user
            // showRestoreCompleteNotification(context, success, notificationManager)

            // Step 3: If restoration is successful, reset the database
            if (success) {
                GazeDatabase.resetDatabase()
                // Optionally, perform any additional cleanup or reinitialization here

                return Result.success()
            }
        }

        return Result.failure()
    }

    private fun restoreDataFromZip(context: Context, zipFileUri: Uri): Boolean {
        val contentResolver = context.contentResolver
        var dbRestorationSuccess = false
        var mediaFilesRestored = false

        contentResolver.openInputStream(zipFileUri)?.use { inputStream ->
            ZipInputStream(inputStream).use { zipInputStream ->
                var entry: ZipEntry?
                while (zipInputStream.nextEntry.also { entry = it } != null) {
                    val entryName = entry?.name

                    // Skip processing if entryName is null
                    if (entryName == null) {
                        zipInputStream.closeEntry()
                        continue
                    }

                    // Process media files before the database file
                    if (entryName.startsWith("contacts/")) {
                        try {
                            restoreContactsFile(context, entryName, zipInputStream)
                            mediaFilesRestored = true
                        } catch (e: Exception) {
                            // Decide whether to fail the entire process or just log the error
                        } finally {
                            zipInputStream.closeEntry()  // Ensure the stream for this entry is fully consumed
                        }
                    }

                    // Process the database file
                    if (entryName == "databases/GazeApp.db") {
                        try {
                            restoreDatabaseFile(context, zipInputStream)
                            dbRestorationSuccess = true
                            // Break after processing the database to avoid further processing
                            break
                        } catch (e: Exception) {
                            Log.e("DatabaseRestoreWorker", "Failed to restore database", e)
                            return false  // Abort the restoration process if database restoration fails
                        } finally {
                            zipInputStream.closeEntry()
                        }
                    }
                }
            }
        }

        // Restoration is considered successful if the database was successfully restored
        return dbRestorationSuccess
    }

    private fun restoreDatabaseFile(context: Context, zipInputStream: ZipInputStream) {
        // Get the path to the current Room database file
        val dbPath = context.getDatabasePath(Const.DB_NAME).absolutePath

        // Ensure any existing database is closed before overwriting
        GazeDatabase.getDatabase(context).close()
        GazeDatabase.resetDatabase()

        // Replace the current database file with the extracted one
        File(dbPath).outputStream().use { fileOutputStream ->
            zipInputStream.copyTo(fileOutputStream)
        }
    }

    private fun restoreContactsFile(
        context: Context,
        entryName: String,
        zipInputStream: ZipInputStream
    ) {
        // Construct the output file path within the app's private storage
        val outputFile = File(context.filesDir, entryName)

        // Ensure the parent directories exist
        outputFile.parentFile?.mkdirs()

        // Write the ZIP entry content to the output file
        outputFile.outputStream().use { fileOutputStream ->
            zipInputStream.copyTo(fileOutputStream)
        }
    }
}

I’ve verified the ZIP file’s integrity and confirmed the presence of .jpg files within the specified subfolders. Attempting various strategies, I observed that directly invoking restoreContactsFile(context, entryName, zipInputStream) immediately after checking for a null entryName does indeed restore the images from the contacts/1/gallery/ subfolder. However, this approach contradicts my requirement to prioritize the database file restoration before proceeding with the media files in the /contacts subfolders.

Despite experimenting with multiple passes through the ZIP file and even attempting to duplicate the ZIP for a sequential restoration process, the only successful restoration occurs with the immediate call to restoreContactsFile, which misaligns with my intended logic of database-first restoration followed by media file restoration.

I would greatly appreciate any insights or suggestions on how to achieve the desired sequential restoration process effectively.

LEAVE A COMMENT