如何解决实施范围存储后应用程序没有响应
我在一个示例项目中实现了 Scope Storage。我在本地存储和范围存储中保存、加载和修改图像的位置> 还有。 下面是我的主要活动类,我在其中保存、加载和修改本地和范围存储中的图像。下面的代码在 API 级别 28 或 Android 10 中运行。但是当我在 Android 11 中运行此应用程序时,它会挂起并显示应用程序无响应。我在 Logcat 中找不到任何错误。下面是我的代码清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.scopestorage">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxsdkVersion="28" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Scopestorage">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
现在下面是我的主要活动代码:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var internalStoragePhotoAdapter: InternalStoragePhotoAdapter
private lateinit var externalStoragePhotoAdapter: SharedPhotoAdapter
private var readPermissionGranted = false
private var writePermissionGranted = false
private lateinit var permissionsLauncher: ActivityResultLauncher<Array<String>>
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var contentObserver: ContentObserver
private var deletedImageUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
internalStoragePhotoAdapter = InternalStoragePhotoAdapter {
lifecycleScope.launch {
val isDeletionSuccessful = deletePhotoFromInternalStorage(it.name)
if(isDeletionSuccessful) {
loadPhotosFromInternalStorageIntoRecyclerView()
Toast.makeText(this@MainActivity,"Photo successfully deleted",Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity,"Failed to delete photo",Toast.LENGTH_SHORT).show()
}
}
}
externalStoragePhotoAdapter = SharedPhotoAdapter {
lifecycleScope.launch {
deletePhotoFromExternalStorage(it.contentUri)
deletedImageUri = it.contentUri
}
}
setupExternalStorageRecyclerView()
initContentObserver()
permissionsLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
readPermissionGranted = permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: readPermissionGranted
writePermissionGranted = permissions[Manifest.permission.WRITE_EXTERNAL_STORAGE] ?: writePermissionGranted
if(readPermissionGranted) {
loadPhotosFromExternalStorageIntoRecyclerView()
} else {
Toast.makeText(this,"Can't read files without permission.",Toast.LENGTH_LONG).show()
}
}
updateOrRequestPermissions()
intentSenderLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if(it.resultCode == RESULT_OK) {
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
lifecycleScope.launch {
deletePhotoFromExternalStorage(deletedImageUri ?: return@launch)
}
}
Toast.makeText(this@MainActivity,"Photo deleted successfully",Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity,"Photo Couldn't be deleted",Toast.LENGTH_SHORT).show()
}
}
val takePhoto = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) {
lifecycleScope.launch {
val isPrivate = binding.switchPrivate.isChecked
val isSavedSuccessfully = when {
isPrivate -> savePhotoToInternalStorage(UUID.randomUUID().toString(),it)
writePermissionGranted -> savePhotoToExternalStorage(UUID.randomUUID().toString(),it)
else -> false
}
if(isPrivate) {
loadPhotosFromInternalStorageIntoRecyclerView()
}
if(isSavedSuccessfully) {
Toast.makeText(this@MainActivity,"Photo saved successfully","Failed to save photo",Toast.LENGTH_SHORT).show()
}
}
}
binding.btnTakePhoto.setonClickListener {
takePhoto.launch()
}
setupInternalStorageRecyclerView()
loadPhotosFromInternalStorageIntoRecyclerView()
loadPhotosFromExternalStorageIntoRecyclerView()
}
private fun initContentObserver() {
contentObserver = object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
if(readPermissionGranted) {
loadPhotosFromExternalStorageIntoRecyclerView()
}
}
}
contentResolver.registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,true,contentObserver
)
}
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()
)
}
}
}
}
private suspend fun loadPhotosFromExternalStorage(): List<SharedStoragePhoto> {
return withContext(dispatchers.IO) {
val collection = sdk29AndUp {
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,"${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()
}
}
private fun updateOrRequestPermissions() {
val hasReadPermission = ContextCompat.checkSelfPermission(
this,Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
val hasWritePermission = ContextCompat.checkSelfPermission(
this,Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
val minSdk29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
readPermissionGranted = hasReadPermission
writePermissionGranted = hasWritePermission || minSdk29
val permissionsToRequest = mutablelistof<String>()
if(!writePermissionGranted) {
permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
if(!readPermissionGranted) {
permissionsToRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE)
}
if(permissionsToRequest.isNotEmpty()) {
permissionsLauncher.launch(permissionsToRequest.toTypedArray())
}
}
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 ->
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
}
}
}
private fun setupInternalStorageRecyclerView() = binding.rvPrivatePhotos.apply {
adapter = internalStoragePhotoAdapter
layoutManager = StaggeredGridLayoutManager(3,RecyclerView.VERTICAL)
}
private fun setupExternalStorageRecyclerView() = binding.rvPublicPhotos.apply {
adapter = externalStoragePhotoAdapter
layoutManager = StaggeredGridLayoutManager(3,RecyclerView.VERTICAL)
}
private fun loadPhotosFromInternalStorageIntoRecyclerView() {
lifecycleScope.launch {
val photos = loadPhotosFromInternalStorage()
internalStoragePhotoAdapter.submitList(photos)
}
}
private fun loadPhotosFromExternalStorageIntoRecyclerView() {
lifecycleScope.launch {
val photos = loadPhotosFromExternalStorage()
externalStoragePhotoAdapter.submitList(photos)
}
}
private suspend fun deletePhotoFromInternalStorage(filename: String): Boolean {
return withContext(dispatchers.IO) {
try {
deleteFile(filename)
} catch (e: Exception) {
e.printstacktrace()
false
}
}
}
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,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,stream)) {
throw IOException("Couldn't save bitmap.")
}
}
true
} catch(e: IOException) {
e.printstacktrace()
false
}
}
}
override fun onDestroy() {
super.onDestroy()
contentResolver.unregisterContentObserver(contentObserver)
}
}
任何形式的帮助将不胜感激。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。