Android _ 资源、知识总结链接目录 + 框架简化开发

混乱的 Android 存储

2021-08-25  本文已影响0人  小强开学前

Android 10(API 29,代号 Q)开始启用分区存储。
禁止一切应用访问除本应用文件夹外的所有文件,自己创建的媒体文件可以访问。

直接通过路径访问图片,Android 6.0 (API 23,M)及以上

确保/sdcard/Download/1.webp存在

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"1.webp");
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());

没有申请权限时,皆会抛出错误

com.android.providers.media.module E/MediaProvider: Permission to access file: /storage/emulated/0/Download/1.webp is denied
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Download/1.webp: open failed: EACCES (Permission denied)

通过这篇文章的Intent方式返回URI获取图片

创建Contract

    public static final class PickImage extends ActivityResultContract<Void, Uri> {

        @NonNull
        @Override
        public Intent createIntent(@NonNull Context context, @Nullable Void input) {
            return new Intent(Intent.ACTION_PICK).setType("image/*");
        }

        @Nullable
        @Override
        public Uri parseResult(int resultCode, @Nullable Intent intent) {
            if (intent == null || resultCode != Activity.RESULT_OK) return null;
            return intent.getData();
        }
    }

创建申请权限的Launch

private final ActivityResultLauncher<Void> imageLauncher = registerForActivityResult(new PickImage(), resultUri -> {
    if (resultUri != null) {
        handleResult(resultUri);
    } else {
        Toast.makeText(MainActivity.this, "未做处理", Toast.LENGTH_SHORT).show();
    }
});

URI转Bitmap使用代码如下

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    ImageDecoder.Source source = ImageDecoder.createSource(MainActivity.this.getContentResolver(), resultUri);
    bm = ImageDecoder.decodeBitmap(source);
} else {
    bm = MediaStore.Images.Media.getBitmap(MainActivity.this.getContentResolver(), resultUri);
}

测试结果

机型 版本 结果
Pixel 3 Android 12 真机 成功读取无需权限
Pixel 3 Android 12 模拟器 成功读取无需权限
Pixel 2 Android 9 模拟器 成功读取无需权限
Nut Pro 2s Android 8.1 真机 返回URI但报错SecurityException
Samsung S8 Android 7 真机 成功读取无需权限

目测一般情况是不需要任何权限的,国产机器魔改太多,为了做好兼容,还是需要捕获SecurityException然后弹框提示用户此种情况。

存储图片到媒体文件夹sdcard/Pictures/Example/

fun saveBitmap2Gallery(context: Context, bm: Bitmap) {
    val currentTime = System.currentTimeMillis()

    // name the file
    val imageFileName = "IMG_TEST_$currentTime.jpg"
    val imageFilePath =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Environment.DIRECTORY_PICTURES + File.separator + "Example" + File.separator
        else Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath + File.separator + "Example" + File.separator

    // write to file
    val resolver: ContentResolver = context.contentResolver
    val newScreenshot = ContentValues().apply {
        put(MediaStore.Images.ImageColumns.DATE_ADDED, currentTime)
        put(MediaStore.Images.ImageColumns.DISPLAY_NAME, imageFileName)
        put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpg")
        put(MediaStore.Images.ImageColumns.WIDTH, bm.width)
        put(MediaStore.Images.ImageColumns.HEIGHT, bm.height)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            put(MediaStore.Images.ImageColumns.RELATIVE_PATH, imageFilePath)
        } else {
            // make sure the path is existed
            val imageFileDir = File(imageFilePath)
            if (!imageFileDir.exists()) {
                val mkdir = imageFileDir.mkdirs()
                if (!mkdir) {
                    logD("save failed, error: cannot create folder. Make sure app has the permission.")
                    return
                }
            }
            put(MediaStore.Images.ImageColumns.DATA, imageFilePath + imageFileName)
            put(MediaStore.Images.ImageColumns.TITLE, imageFileName)
        }

    }
    // insert a new image
    resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, newScreenshot)?.let { uri ->
        resolver.openOutputStream(uri)?.let { os ->
            bm.compress(Bitmap.CompressFormat.PNG, 80, os)
            os.flush()
            os.close()
            newScreenshot.clear()
            newScreenshot.put(MediaStore.Images.ImageColumns.SIZE, File(imageFilePath).length())
            resolver.update(uri, newScreenshot, null, null)
            logD("save success, you can view it in gallery")
        }
    }
}

机型 版本 结果
Pixel 3 Android 12 真机 保存成功
Pixel 3 Android 12 模拟器 保存成功
Pixel 2 Android 9 模拟器 创建文件夹这一步失败
Nut Pro 2s Android 8.1 真机 创建文件夹这一步失败
上一篇下一篇

猜你喜欢

热点阅读