升级Android Q之路遇到的坑-本地相册图片加载

从Android 10开始,我们访问应用程序外部文件的方式发生了轻微的变化。这种行为的改变来自于作用域存储的概念,它旨在提高用户文件的保密性,在android系统中添加更多的文件访问控制。

在安卓Q以前版本选取相册后会获取到照片的Uri和绝对路径(以下简称Path),然后用Glide去加载图片就可以显示在Imageview上面,但是在Android 10(API 级别 29)及更高版本为目标平台的应用在默认情况下被赋予了对外部存储设备的分区访问权限(即分区存储)。此类应用只能看到本应用专有的目录(通过 Context.getExternalFilesDir() 访问)以及特定类型的媒体。通俗的讲就是本应用访问本应用的文件不需要权限可自由访问,在访问其他应用的文件时比如手机相册,不仅需要READ_EXTERNAL_STORAGE权限还要使ContentResolver.openFile()

从相册获取到图片的Uri后要获取到图片的路径,在Android Q以前获取地址的方式为

  Cursor cursor = null;
  final String column = MediaStore.MediaColumns.DATA;
  final String[] projection = {column};
  String path = "";
  cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int columnIndex = cursor.getColumnIndexOrThrow(column);
                path = cursor.getString(columnIndex);
            }

path就是图片的地址。
但是在安卓Q上面虽说能获取到图片的地址,但是用Glide去加载那就是空白的图片,因为从Q开始MediaStore.MediaColumns.DATA被弃用。
所以在获取图片地址的时候要分版本。代码如下

if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                return path;
            } else {//安卓10
                ParcelFileDescriptor descriptor = null;
                try {
                    descriptor = context.getContentResolver().openFile(uri, "r", null);
                    FileInputStream inputStream = new FileInputStream(descriptor.getFileDescriptor());
                    byte[] byteArray = FileUtilsToQ.Companion.readBinaryStream(inputStream, (int) descriptor.getStatSize());
                    String[] split = path.split("/");
                    if (split.length != 0) {
                        File cachedFile = new File(context.getCacheDir(), split[split.length - 1]);
                        boolean fileSaved = FileUtilsToQ.Companion.writeFile(cachedFile, byteArray);
                        if (fileSaved) {
                            return cachedFile.getAbsolutePath();
                        } else {
                            return null;
                        }
                    }
                } catch (FileNotFoundException e) {
                    return null;
                } finally {
                    if (descriptor != null) {
                        try {
                            descriptor.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

其中FileUtilsToQ的代码:

import java.io.*

class FileUtilsToQ {
    companion object {
        fun readBinaryStream(
                stream: InputStream,
                byteCount: Int
        ): ByteArray {
            val output = ByteArrayOutputStream()
            try {
                val buffer = ByteArray(if (byteCount > 0) byteCount else 4096)
                var read: Int
                while (stream.read(buffer).also { read = it } >= 0) {
                    output.write(buffer, 0, read)
                }
            } catch (e: IOException) {
                e.printStackTrace()
            } finally {
                try {
                    stream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
            return output.toByteArray()
        }

        fun writeFile(cachedFile: File, data: ByteArray): Boolean {
            return try {
                var output: BufferedOutputStream? = null
                try {
                    output = BufferedOutputStream(FileOutputStream(cachedFile))
                    output.write(data)
                    output.flush()
                    true
                } finally {
                    output?.close()
                }
            } catch (ex: Exception) {
                false
            }
        }
    }
}

然后就可以获取到图片的地址。

你可能感兴趣的:(AndroidQ)