如何解决写入权限不起作用-范围存储Android SDK 30又名Android 11
还有其他人发现范围存储几乎不可能开始工作吗?大声笑。
我一直在尝试了解如何允许用户向我的应用授予对应用文件夹之外的文本文件的写权限。 (假设允许用户编辑其“文档”文件夹中的文件文本)。我已经设置了MANAGE_EXTERNAL_STORAGE
权限,并且可以确认该应用程序具有该权限。但还是每次我尝试
val fileDescriptor = context.contentResolver.openFileDescriptor(uri,"rwt")?.fileDescriptor
我收到Illegal Argument: Media is read-only
错误。
我的清单请求这三个权限:
<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:requestLegacyExternalStorage="true"
但仍然遇到此只读问题。
我想念什么?
更多说明
我如何获取URI:
view?.selectFileButton?.setonClickListener {
val intent =
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
}
startActivityForResult(Intent.createChooser(intent,"Select a file"),111)
}
然后
override fun onActivityResult(requestCode: Int,resultCode: Int,data: Intent?) {
super.onActivityResult(requestCode,resultCode,data)
if (requestCode == 111 && resultCode == AppCompatActivity.RESULT_OK && data != null) {
val selectedFileUri = data.data;
if (selectedFileUri != null) {
viewmodel.saveFilename(selectedFileUri.toString())
val contentResolver = context!!.contentResolver
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableuriPermission(selectedFileUri,takeFlags)
view?.fileName?.text = viewmodel.filename
//Todo("if we didn't get the permissions we needed,ask for permission or have the user select a different file")
}
}
}
解决方法
根据您的代码:
- 您列出的所有权限都与
ACTION_OPEN_DOCUMENT
无关 - 您的
Intent
上的两个标志都不存在
但是,您真正的问题是您似乎正在选择媒体,例如从“音频”类别中选择媒体。 ACTION_OPEN_DOCUMENT
保证我们可以从Uri
所标识的内容中读取内容,但不保证可写位置。不幸的是,MediaProvider
阻止了所有写访问,并抛出了您引用其消息的异常。
用the issue that I filed last year引述自己:
问题在于我们无法在
ACTION_OPEN_DOCUMENT
Intent
上指定要编写的内容,因此希望将用户限制在可写位置。鉴于Android Q / R特别重视我们向Storage Access Framework的迁移,因此需要这种功能。否则,我们所能做的就是检测到我们没有写访问权限(例如DocumentFile
和canWrite()
),然后告诉用户“对不起,我不能在那儿写”,这导致糟糕的用户体验。
我在this blog post中写了更多有关此问题的内容。
因此,使用DocumentFile
和canWrite()
来查看是否允许您写入Uri
所标识的位置,并要求用户选择其他文档。
在Android 11上,并使用API 30模拟器进行测试,我发现了类似的公用文件夹
Download,Documents,DCIM,Alarms,Pictures and such
可用于使用经典文件系统路径的应用程序。
仅限于应用程序自己的文件。
另外,我发现一个应用程序以这种方式创建的文件可被使用SAF的其他应用程序写入。
,您可以尝试以下代码。它对我有用。
class MainActivity : AppCompatActivity() {
private lateinit var theTextOfFile: TextView
private lateinit var inputText: EditText
private lateinit var saveBtn: Button
private lateinit var readBtn: Button
private lateinit var deleteBtn: Button
private lateinit var someText: String
private val filename = "theFile.txt"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isPermissionGranted()) {
val permissions = arrayOf(WRITE_EXTERNAL_STORAGE)
for (i in permissions.indices) {
requestPermission(permissions[i],i)
}
}
theTextOfFile = findViewById(R.id.theTextOfFile)
inputText = findViewById(R.id.inputText)
saveBtn = findViewById(R.id.saveBtn)
readBtn = findViewById(R.id.readBtn)
deleteBtn = findViewById(R.id.deleteBtn)
saveBtn.setOnClickListener { savingFunction() }
deleteBtn.setOnClickListener { deleteFunction() }
readBtn.setOnClickListener {
theTextOfFile.text = readFile()
}
}
private fun readFile() : String{
val rootPath = "/storage/emulated/0/Download/"
val myFile = File(rootPath,filename)
return if (myFile.exists()) {
FileInputStream(myFile).bufferedReader().use { it.readText() }
}
else "no file"
}
private fun deleteFunction(){
val rootPath = "/storage/emulated/0/Download/"
val myFile = File(rootPath,filename)
if (myFile.exists()) {
myFile.delete()
}
}
private fun savingFunction(){
deleteFunction()
someText = inputText.text.toString()
val resolver = applicationContext.contentResolver
val values = ContentValues()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.DISPLAY_NAME,filename)
values.put(MediaStore.MediaColumns.MIME_TYPE,"text/plain")
values.put(MediaStore.MediaColumns.RELATIVE_PATH,Environment.DIRECTORY_DOWNLOADS)
val uri = resolver.insert(MediaStore.Files.getContentUri("external"),values)
uri?.let { it ->
resolver.openOutputStream(it).use {
// Write file
it?.write(someText.toByteArray(Charset.defaultCharset()))
it?.close()
}
}
} else {
val rootPath = "/storage/emulated/0/Download/"
val myFile = File(rootPath,filename)
val outputStream: FileOutputStream
try {
if (myFile.createNewFile()) {
outputStream = FileOutputStream(myFile,true)
outputStream.write(someText.toByteArray())
outputStream.flush()
outputStream.close()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun isPermissionGranted(): Boolean {
val permissionCheck = ActivityCompat.checkSelfPermission(this,WRITE_EXTERNAL_STORAGE)
return permissionCheck == PackageManager.PERMISSION_GRANTED
}
private fun requestPermission(permission: String,requestCode: Int) {
ActivityCompat.requestPermissions(this,arrayOf(permission),requestCode)
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。