android 10 分区储存适配

搜索文档

以下代码段使用 `[ACTION_OPEN_DOCUMENT]来搜索包含图片文件的文档提供程序:

{".3gp", "video/3gpp"},

  {".apk", "application/vnd.android.package-archive"},

  {".asf", "video/x-ms-asf"},

  {".avi", "video/x-msvideo"},

  {".bin", "application/octet-stream"},

  {".bmp", "image/bmp"},

  {".c", "text/plain"},

  {".class", "application/octet-stream"},

  {".conf", "text/plain"},

  {".cpp", "text/plain"},

  {".doc", "application/msword"},

  {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},

  {".xls", "application/vnd.ms-excel"},

  {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},

  {".exe", "application/octet-stream"},

  {".gif", "image/gif"},

  {".gtar", "application/x-gtar"},

  {".gz", "application/x-gzip"},

  {".h", "text/plain"},

  {".htm", "text/html"},

  {".html", "text/html"},

  {".jar", "application/java-archive"},

  {".java", "text/plain"},

  {".jpeg", "image/jpeg"},

  {".jpg", "image/jpeg"},

  {".js", "application/x-javascript"},

  {".log", "text/plain"},

  {".m3u", "audio/x-mpegurl"},

  {".m4a", "audio/mp4a-latm"},

  {".m4b", "audio/mp4a-latm"},

  {".m4p", "audio/mp4a-latm"},

  {".m4u", "video/vnd.mpegurl"},

  {".m4v", "video/x-m4v"},

  {".mov", "video/quicktime"},

  {".mp2", "audio/x-mpeg"},

  {".mp3", "audio/x-mpeg"},

  {".mp4", "video/mp4"},

  {".mpc", "application/vnd.mpohun.certificate"},

  {".mpe", "video/mpeg"},

  {".mpeg", "video/mpeg"},

  {".mpg", "video/mpeg"},

  {".mpg4", "video/mp4"},

  {".mpga", "audio/mpeg"},

  {".msg", "application/vnd.ms-outlook"},

  {".ogg", "audio/ogg"},

  {".pdf", "application/pdf"},

  {".png", "image/png"},

  {".pps", "application/vnd.ms-powerpoint"},

  {".ppt", "application/vnd.ms-powerpoint"},

  {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},

  {".prop", "text/plain"},

  {".rc", "text/plain"},

  {".rmvb", "audio/x-pn-realaudio"},

  {".rtf", "application/rtf"},

  {".sh", "text/plain"},

  {".tar", "application/x-tar"},

  {".tgz", "application/x-compressed"},

  {".txt", "text/plain"},

  {".wav", "audio/x-wav"},

  {".wma", "audio/x-ms-wma"},

  {".wmv", "audio/x-ms-wmv"},

  {".wps", "application/vnd.ms-works"},

  {".xml", "text/plain"},

  {".z", "application/x-compress"},

  {".zip", "application/x-zip-compressed"},

  {"", "*/*"}

  };
————————————————
版权声明:本文为CSDN博主「Work_Times」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34099401/article/details/64439869
private const val READ_REQUEST_CODE: Int = 42
...
/**
 * Fires an intent to spin up the "file chooser" UI and select an image.
 */
fun performFileSearch() {

    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    // browser.
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        // Filter to only show results that can be "opened", such as a
        // file (as opposed to a list of contacts or timezones)
        addCategory(Intent.CATEGORY_OPENABLE)

        // Filter to show only images, using the image MIME data type.
        // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
        // To search for all documents available via installed storage providers,
        // it would be "*/*".
        type = "image/*"
    }

    startActivityForResult(intent, READ_REQUEST_CODE)
}
  • 当应用触发 `[ACTION_OPEN_DOCUMENT] Intent 时,该 Intent 会启动选择器,以显示所有匹配的文档提供程序。
  • 在 Intent 中添加 `[CATEGORY_OPENABLE]类别可对结果进行过滤,从而只显示可打开的文档(如图片文件)。
  • intent.setType("image/*") 语句可做进一步过滤,从而只显示 MIME 数据类型为图像的文档。

处理结果

当用户在选择器中选择文档后,系统会调用 [onActivityResult()]。resultData参数包含指向所选文档的 URI。您可以使用getData()` 提取该 URI。获得 URI 后,您可以用它来检索用户所需文档。例如:

override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {

    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    // response to some other intent, and the code below shouldn't run at all.

    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        // The document selected by the user won't be returned in the intent.
        // Instead, a URI to that document will be contained in the return intent
        // provided to this method as a parameter.
        // Pull that URI using resultData.getData().
        resultData?.data?.also { uri ->
            Log.i(TAG, "Uri: $uri")
            showImage(uri)
        }
    }
}
fun dumpImageMetaData(uri: Uri) {

    // The query, since it only applies to a single document, will only return
    // one row. There's no need to filter, sort, or select fields, since we want
    // all fields for one document.
    val cursor: Cursor? = contentResolver.query( uri, null, null, null, null, null)

    cursor?.use {
        // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (it.moveToFirst()) {

            // Note it's called "Display Name".  This is
            // provider-specific, and might not necessarily be the file name.
            val displayName: String =
                    it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
            Log.i(TAG, "Display Name: $displayName")

            val sizeIndex: Int = it.getColumnIndex(OpenableColumns.SIZE)
            // If the size is unknown, the value stored is null.  But since an
            // int can't be null in Java, the behavior is implementation-specific,
            // which is just a fancy term for "unpredictable".  So as
            // a rule, check if it's null before assigning to an int.  This will
            // happen often:  The storage API allows for remote files, whose
            // size might not be locally known.
            val size: String = if (!it.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                it.getString(sizeIndex)
            } else {
                "Unknown"
            }
            Log.i(TAG, "Size: $size")
        }
    }
}

检查文档元数据

获得文档的 URI 后,您可以访问该文档的元数据。以下代码段用于获取 URI 所指定文档的元数据,并将其记入日志:

@Throws(IOException::class)
private fun getBitmapFromUri(uri: Uri): Bitmap {
    val parcelFileDescriptor: ParcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r")
    val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
    val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
    parcelFileDescriptor.close()
    return image
}

请注意,您不应在界面线程上执行此操作。请使用 [AsyncTask] 在后台执行此操作。打开位图后,您可以在ImageView` 中显示该位图。

获取 InputStream

以下示例展示了如何从 URI 中获取 [InputStream](https://developer.android.google.cn/reference/java/io/InputStream)。在此代码段中,系统会将文件行读取到字符串中:

@Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
    val stringBuilder = StringBuilder()
    contentResolver.openInputStream(uri)?.use { inputStream ->
        BufferedReader(InputStreamReader(inputStream)).use { reader ->
            var line: String? = reader.readLine()
            while (line != null) {
                stringBuilder.append(line)
                line = reader.readLine()
            }
        }
    }
    return stringBuilder.toString()
}

创建文档

您的应用可通过使用 `[ACTION_CREATE_DOCUMENT]Intent,在文档提供程序中创建新文档。如要创建文件,请为您的 Intent 提供 MIME 类型和文件名,然后使用唯一的请求代码启动该 Intent。系统会为您执行其余操作:

private const val WRITE_REQUEST_CODE: Int = 43
private fun createFile(mimeType: String, fileName: String) {
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        // Filter to only show results that can be "opened", such as
        // a file (as opposed to a list of contacts or timezones).
        addCategory(Intent.CATEGORY_OPENABLE)

        // Create a file with the requested MIME type.
        type = mimeType
        putExtra(Intent.EXTRA_TITLE, fileName)
    }

    startActivityForResult(intent, WRITE_REQUEST_CODE)
}

创建新文档后,您可以在 `[onActivityResult()] 中获取该文档的 URI,以便继续向其写入内容。

删除文档

如果您获得了文档的 URI,并且文档的 [Document.COLUMN_FLAGS] 包含[SUPPORTS_DELETE],则便可删除该文档。例如

DocumentsContract.deleteDocument(contentResolver, uri)

编辑文档

您可以随时使用 SAF 编辑文本文档。以下代码段会触发 [ACTION_OPEN_DOCUMENT]Intent 并使用CATEGORY_OPENABLE` 类别,从而只显示可打开的文档。它会进一步过滤,从而只显示文本文件:

private const val EDIT_REQUEST_CODE: Int = 44
/**
 * Open a file for writing and append some text to it.
 */
private fun editDocument() {
    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    // file browser.
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        // Filter to only show results that can be "opened", such as a
        // file (as opposed to a list of contacts or timezones).
        addCategory(Intent.CATEGORY_OPENABLE)

        // Filter to show only text files.
        type = "text/plain"
    }

    startActivityForResult(intent, EDIT_REQUEST_CODE)
}

接下来,您可以从 onActivityResult()(请参阅[处理结果](https://developer.android.google.cn/guide/topics/providers/document-provider#results))调用代码,以执行编辑操作。以下代码段将从ContentResolver获取FileOutputStream`。其默认使用写入模式。最佳做法是请求获得最少的所需访问权限,因此如果您只需要写入权限,请勿请求获得读取/写入权限:

private fun alterDocument(uri: Uri) {
    try {
        contentResolver.openFileDescriptor(uri, "w")?.use {
            // use{} lets the document provider know you're done by automatically closing the stream
            FileOutputStream(it.fileDescriptor).use {
                it.write(
                    ("Overwritten by MyCloud at ${System.currentTimeMillis()}\n").toByteArray()
                )
            }
        }
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

保留权限
当应用打开文件进行读取或写入时,系统会为其提供针对该文件的 URI 授权,有效期直至用户设备重启。但假定您的应用是图像编辑应用,而且您希望用户能直接从应用中访问其编辑的最后 5 张图像。如果用户的设备已重启,则您必须让用户回到系统选择器以查找这些文件,而这显然不是理想的做法。

为防止出现此情况,您可以保留系统向应用授予的权限。实际上,您的应用是“获取”了系统提供的 URI 持久授权。如此一来,用户便可通过您的应用持续访问文件,即使设备已重启也不受影响:

val takeFlags: Int = intent.flags and
        (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

官网

你可能感兴趣的:(android 10 分区储存适配)