当前位置:网站首页>Backup and restore of Android local SQLite database

Backup and restore of Android local SQLite database

2022-07-05 13:13:00 Vaccae

Learn better people ,

Be a better person .

——《 Smart enjoyment of wechat 》

0db1c82043453a1d1087606d9fa303bf.jpeg

The length of this paper is 3024 word , Expected reading 6 minute

Preface

Internet Android APP In fact, a lot of development is Android End writing UI, Business logic passed API Callback display data , And I mainly deal with hardware equipment , At ordinary times, we should also consider the normal use of a single machine when the network is not available , So all business logic is implemented in the program , Data localization requirements are also high , It needs to be used Sqlite database , So this article is devoted to Sqlite Database backup and restore .

619a561a612bf07a04ee8e352f3d2cd2.png

How to achieve Sqlite Database backup and restore ?

A

Actually realize Sqlite The backup and restore principle of is still relatively simple , Will be App Generated in the Sqlite Copy the database file of to the storage area , Restore is to copy the copied database files back to the database directory specified by the program . But here is a key problem , The storage directory must be the external directory specified by yourself , If you are backing up the database file or the directory under the package , The installation upgrade signature is incorrect , Or manually open the application and click clear data , The local database and backup database files will also be emptied , The consequences can be imagined ....

Realization effect

fd82ccd211662996edf2abe536ea50ac.gif

d4dbe4b87debfa89fa99f0cac51d7a20.png

1. Three local database files

df32ac705f55f89f8ec2fb6457abce75.png

2.SD There is no backup file in the card directory

e8130d9893b27433185449854a700dc5.png

3. Click backup database

688f97c6f84d7113c1cd6f98a6b4deff.png

4.SD The database has been copied from the card directory

1953e8415c80d6aca5b8752d6d7ad724.png

5. Delete the original databases Directory database

eb6c2429973d39d585bbd2dca955d4ce.png

6. After re inquiry, nothing is displayed

276ee2b8b262804e8f11f52fdedb3748.png

7. Click restore database databases The files in the directory have been copied back

46953c18ed9f0f850a5b88a82e99b879.png

8. Click again to query data , You can see the displayed data

Core code

d35018e328ea1def14d2bd79a1acbfb3.png

Smart enjoyment of wechat

Backup and restore classes (DbBackupUtil‍)

package com.vaccae.roomdemo


import android.annotation.SuppressLint
import android.content.Context
import android.os.Environment
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException




/**
 *  author :Vaccae
 *  mailbox :[email protected]
 *  Creation time :18:22
 *  Function module description :
 */


class DbBackupUtil {


    private var mContext: Context? = null


    val path =
        Environment.getExternalStorageDirectory().absolutePath + File.separator + "RoomBackup" + File.separator


    private fun getInstance(context: Context) {
        mContext ?: run {
            synchronized(DbBackupUtil::class.java) {
                mContext = context
            }
        }
    }


    private fun createPath() {
        // Installation package path 
        val updateDir = File(path)
        // Create folder 
        if (!updateDir.exists()) {
            updateDir.mkdirs()
        }
    }


    suspend fun backup(context: Context): Flow<String> = flow {
        getInstance(context)
        createPath()
        mContext?.let {
            val strs = it.databaseList()
            emit(" common ${strs.size} A database file , Start backup ")


            for (str in strs) {
                emit(" Backup now ${str} database ...")


                // Find the path to the file   /data/data/ Package name /databases/ Database name 
                val dbFile = it.getDatabasePath(str)
                //val dstFile = it.getExternalFilesDir("db").toString() + "/" + str


                val dstFile = path + str;


                var fis: FileInputStream? = null
                var fos: FileOutputStream? = null
                try {
                    // File copy to sd In the card 
                    fis = FileInputStream(dbFile)
                    fos = FileOutputStream(dstFile)
                    var len = 0
                    val buffer = ByteArray(2048)
                    while (-1 != fis.read(buffer).also({ len = it })) {
                        fos.write(buffer, 0, len)
                    }
                    fos.flush()
                    emit("${str} Database backup complete ...")
                } catch (e: Exception) {
                    throw e
                } finally {
                    // Turn off data flow 
                    try {
                        fos?.close()
                        fis?.close()
                    } catch (e: IOException) {
                        throw e
                    }
                }
            }
            emit(" All database backups are complete ")
        } ?: kotlin.run { throw Exception(" Undefined Context") }
    }


    suspend fun restore(context: Context): Flow<String> {
        return flow {
            getInstance(context)
            createPath()


            mContext?.let {
                //var dbfiles = it.getExternalFilesDir("db")
                var dbfiles = File(path)


                dbfiles.let { dbs ->
                    var files = dbs.listFiles()
                    if (files.isNotEmpty()) {
                        emit(" common ${files.size} A database file , Start restoring ")


                        for (str in files) {
                            var dbFile = it.getDatabasePath(str.name)
                            dbFile.delete()


                            var fis: FileInputStream? = null
                            var fos: FileOutputStream? = null
                            try {
                                // File copy to sd In the card 
                                fis = FileInputStream(str)
                                fos = FileOutputStream(dbFile)
                                var len = 0
                                val buffer = ByteArray(2048)
                                while (-1 != fis.read(buffer).also({ len = it })) {
                                    fos.write(buffer, 0, len)
                                }
                                fos.flush()
                                emit("${str} Database restore completed ...")
                            } catch (e: Exception) {
                                throw e
                            } finally {
                                // Turn off data flow 
                                try {
                                    fos?.close()
                                    fis?.close()
                                } catch (e: IOException) {
                                    throw e
                                }
                            }
                        }
                        emit(" All databases are restored ")
                    }
                }
            } ?: kotlin.run { throw Exception(" Undefined Context") }
        }
    }
}

Activity In the call

// Backup call 
        btnbackup.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                DbBackupUtil().backup(applicationContext)
                    .flowOn(Dispatchers.IO)
                    .collect(collector = FlowCollector { t ->
                        tvshow.text = t
                    })
            }
        }


        // Restore call 
        btnrestore.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                DbBackupUtil().restore(applicationContext)
                    .flowOn(Dispatchers.IO)
                    .collect(collector = FlowCollector { t ->
                        tvshow.text = t
                    })
            }
        }

Smart enjoyment of wechat

Key points

1. Backup and restore adopt return flow In the form of , Because there are three databases , This can be done in UI The interface shows the restore progress .

22046300583b9e82728aed8b749aaa0a.png

Send current progress

d07e8a0e4a95089118dbf1707c80f7e7.png

UI Show current progress

2. The backed up database files are stored in SD Card custom directory , Prevent the application from clicking to clear data , If the backup file is also copied to the package directory, it will also be deleted . However, for storage permissions , stay Android 6.0 Then it becomes dangerous authority , And by the Android 11 It becomes a special permission , You must authorize all file management permissions .

ed2f349fd39e0e3eeb09a7a828f7ff20.png

AndroidManifest Add list permissions to

0fa42a448811787f8b23eb2a48aecb57.png

There is also the directory of storage

05c525c05ad7b2a282a1dfd8b427640f.png

Corresponding xml/file_pahts.xml In the definition of external-path

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.vaccae.roomdemo">


    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:exported="true"
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    </application>


</manifest>

file_path.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The final setting inside NAME and PATH It should be the same as the name of the folder , The corresponding file will be found -->
    <external-path name="RoomBackup" path="RoomBackup"/>
</paths>

Activity Dynamically apply for permission in  

3cfdee6879c47aa1ee86615103c5f53f.png

StartActivityforResult It's abandoned , So use registerForActivityResult To achieve

80c2a6c594f11585acb26086f9b26c1c.png

Apply for permission function

MainActivity Application permission code

class MainActivity : AppCompatActivity() {


    companion object {
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.ACCESS_NETWORK_STATE,
            Manifest.permission.CHANGE_NETWORK_STATE,
            Manifest.permission.INTERNET
        )
    }


    //StartActivity After abandonment , Use registerForActivityResult To achieve 
    private val requestDataLauncher =
        registerForActivityResult(object : ActivityResultContract<Int, String>() {
            override fun createIntent(context: Context, input: Int?): Intent {
                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
                intent.data = Uri.parse("package:$packageName")
                return intent
            }


            override fun parseResult(resultCode: Int, intent: Intent?): String {
                TODO("Not yet implemented")
            }
        }
        ) {
            Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
        }




    private fun allPermissionsGranted() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            //  First judge whether you have permission 
            if (Environment.isExternalStorageManager()) {
                REQUIRED_PERMISSIONS.all {
                    ContextCompat.checkSelfPermission(
                        baseContext,
                        it
                    ) == PackageManager.PERMISSION_GRANTED
                }
            } else {
                requestDataLauncher.launch(REQUEST_CODE_PERMISSIONS)
            }
        } else {
            REQUIRED_PERMISSIONS.all {
                ContextCompat.checkSelfPermission(
                    baseContext,
                    it
                ) == PackageManager.PERMISSION_GRANTED
            }
        }
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        // Application authority 
        allPermissionsGranted()
    }
}

So the whole Android The backup and restore of the local database is completed ,Demo I am the one who directly added to the original remote query analysis gadget Demo in 《 To make a Android Sqlite Remote operation and maintenance gadget 》, The complete source code is linked below :

Source code address

https://github.com/Vaccae/TransAndroidSqliteDBDemo.git

Click to read the original text and you can see “ Code cloud ” The address of

End

b39b623baf39d76fa076de86a03d63cd.png

758a7f91f04137dbbb608afca210b63f.png

Past highlights

a6fd5c3c576afcdce7260cce72969b72.jpeg

To make a Android Sqlite Remote operation and maintenance gadget


94f9e0a9be7debba422ba7ba38e23665.jpeg

Realization Android Local Sqlite Database network transfer to PC End


1bf0908fdc6c6fc76840a0d0ba99d6e1.jpeg

Android Room Actual combat of database version migration


原网站

版权声明
本文为[Vaccae]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207051256497983.html