当前位置:网站首页>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 》
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 .
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
1. Three local database files
2.SD There is no backup file in the card directory
3. Click backup database
4.SD The database has been copied from the card directory
5. Delete the original databases Directory database
6. After re inquiry, nothing is displayed
7. Click restore database databases The files in the directory have been copied back
8. Click again to query data , You can see the displayed data
Core code
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 .
Send current progress
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 .
AndroidManifest Add list permissions to
There is also the directory of storage
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
StartActivityforResult It's abandoned , So use registerForActivityResult To achieve
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
Past highlights
To make a Android Sqlite Remote operation and maintenance gadget
Realization Android Local Sqlite Database network transfer to PC End
Android Room Actual combat of database version migration
边栏推荐
- My colleague didn't understand selenium for half a month, so I figured it out for him in half an hour! Easily showed a wave of operations of climbing Taobao [easy to understand]
- Rocky basic command 3
- #从源头解决# 自定义头文件在VS上出现“无法打开源文件“XX.h“的问题
- Notion 类笔记软件如何选择?Notion 、FlowUs 、Wolai 对比评测
- Rocky基础命令3
- RHCSA9
- Halcon template matching actual code (I)
- MSTP and eth trunk
- Yyds dry inventory JS intercept file suffix
- SAP SEGW 事物码里的 ABAP Editor
猜你喜欢
Principle and performance analysis of lepton lossless compression
Changing JS code has no effect
【服务器数据恢复】某品牌服务器存储raid5数据恢复案例
Flutter 绘制波浪移动动画效果,曲线和折线图
碎片化知识管理工具Memos
Pandora IOT development board learning (HAL Library) - Experiment 7 window watchdog experiment (learning notes)
How can non-technical departments participate in Devops?
RHCSA10
使用 jMeter 对 SAP Spartacus 进行并发性能测试
聊聊异步编程的 7 种实现方式
随机推荐
It's too convenient. You can complete the code release and approval by nailing it!
LB10S-ASEMI整流桥LB10S
Apicloud studio3 API management and debugging tutorial
Fragmented knowledge management tool memos
ABAP editor in SAP segw transaction code
从外卖点单浅谈伪需求
155. Minimum stack
#yyds干货盘点# 解决名企真题:搬圆桌
Talking about fake demand from takeout order
APICloud Studio3 WiFi真机同步和WiFi真机预览使用说明
JPA规范总结和整理
Solve Unicode decodeerror: 'GBK' codec can't decode byte 0xa2 in position 107
Introduction aux contrôles de la page dynamique SAP ui5
Shi Zhenzhen's 2021 summary and 2022 outlook | colorful eggs at the end of the article
How to realize batch sending when fishing
百日完成国产数据库opengausss的开源任务--openGuass极简版3.0.0安装教程
946. 验证栈序列
DataPipeline双料入选中国信通院2022数智化图谱、数据库发展报告
The Research Report "2022 RPA supplier strength matrix analysis of China's banking industry" was officially launched
How can non-technical departments participate in Devops?