关于数据的存储
sp,datastore,内部存储,外部存储,数据库等对比,大概的一些概念
关于内部存储和外部存储的一些区别,以及API29与API30平台上的区别PL这个视频大概有说到
https://www.youtube.com/watch?v=TkOzcyzH1hU&list=PLQkwcJG4YTCR9jZq8O19nUL2hLqmLYX4M
内部存储只有自己的应用可以访问,而且应用卸载后,可一起删除
外部存储的的话,有个问题,就是应用卸载之后,文件还遗留着,然后Android就出了storage scope来解决这个问题,但是这个在Android 10之后引入的storagescope,但是在Andorid 11之后才强制;
内部存储
读取内部文件
private suspend fun loadPhotosFromInternalStorage(): List<InternalStoragePhoto> {
return withContext(Dispatchers.IO) {
val files = filesDir.listFiles()
files?.filter { it.canRead() && it.isFile && it.name.endsWith(".jpg") }?.map {
val bytes = it.readBytes()
val bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
InternalStoragePhoto(it.name, bmp)
} ?: listOf()
}
}
写入内部文件
private suspend fun savePhotoToInternalStorage(filename: String, bmp: Bitmap): Boolean {
return withContext(Dispatchers.IO) {
try {
openFileOutput("$filename.jpg", MODE_PRIVATE).use { stream ->
if(!bmp.compress(Bitmap.CompressFormat.JPEG, 95, stream)) {
throw IOException("Couldn't save bitmap.")
}
}
true
} catch(e: IOException) {
e.printStackTrace()
false
}
}
}
删除内部文件
private suspend fun deletePhotoFromInternalStorage(filename: String): Boolean {
return withContext(Dispatchers.IO) {
try {
deleteFile(filename) //context就有的方法
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
外部存储
读取外部文件
首先要有权限READ_EXTERNAL_STORAGE
//通过contentprovider来查询外部共享媒体文件
private suspend fun loadPhotosFromExternalStorage(): List<SharedStoragePhoto> {
return withContext(Dispatchers.IO) {
val collection = sdk29AndUp { //api29以上
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} ?: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.WIDTH,
MediaStore.Images.Media.HEIGHT,
)
val photos = mutableListOf<SharedStoragePhoto>()
contentResolver.query(
collection,
projection,
null,
null,
"${MediaStore.Images.Media.DISPLAY_NAME} ASC"
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val displayNameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val widthColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH)
val heightColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT)
while(cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val displayName = cursor.getString(displayNameColumn)
val width = cursor.getInt(widthColumn)
val height = cursor.getInt(heightColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
photos.add(SharedStoragePhoto(id, displayName, width, height, contentUri))
}
photos.toList()
} ?: listOf()
}
}
写入外部文件
权限WRITE_EXTERNAL_STORAGE
//通过contentprovider向媒体库写入图片数据
private suspend fun savePhotoToExternalStorage(displayName: String, bmp: Bitmap): Boolean {
return withContext(Dispatchers.IO) {
val imageCollection = sdk29AndUp {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} ?: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$displayName.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.WIDTH, bmp.width)
put(MediaStore.Images.Media.HEIGHT, bmp.height)
}
try {
contentResolver.insert(imageCollection, contentValues)?.also { uri ->
//前面只是加了数据库的值,返回了UIR,
//然后我们需要将返回的URI中,写入图片数据
contentResolver.openOutputStream(uri).use { outputStream ->
if(!bmp.compress(Bitmap.CompressFormat.JPEG, 95, outputStream)) {
throw IOException("Couldn't save bitmap")
}
}
} ?: throw IOException("Couldn't create MediaStore entry")
true
} catch(e: IOException) {
e.printStackTrace()
false
}
}
}
删除外部文件
//这里会比较不一样,早期的版本可以直接删除 //Android10之后需要有权限 //没有权限,则需要创建删除请求,系统会有弹窗,让用户来进行确认
private suspend fun deletePhotoFromExternalStorage(photoUri: Uri) {
withContext(Dispatchers.IO) {
try {
contentResolver.delete(photoUri, null, null)
} catch (e: SecurityException) {
val intentSender = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
MediaStore.createDeleteRequest(contentResolver, listOf(photoUri)).intentSender
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
val recoverableSecurityException = e as? RecoverableSecurityException
recoverableSecurityException?.userAction?.actionIntent?.intentSender
}
else -> null
}
intentSender?.let { sender ->
intentSenderLauncher.launch(
IntentSenderRequest.Builder(sender).build()
)
}
}
}
}