正确将图片保存到相册的方法

而如果这个文件变成了一张图片,那你涉及到的就不仅仅是一个 I/O 操作了,还需要考虑如何更新 MediaStore,这样才可以在系统相册中。

刷新系统 Media 通常有如下几种方式:

  • 通过操作 MediaStore 类。
  • 发送广播更新 MediaStore。
  • 通过操作 MediaScannerConnection 类。

操作 MediaStore

MediaStore.Images.Media它提供了几个 inserImage () 方法,供我们向 MediaStore 中插入图片数据,并产生一个缩略图。

  /**
             * Insert an image and create a thumbnail for it.
             *
             * @param cr The content resolver to use
             * @param source The stream to use for the image
             * @param title The name of the image
             * @param description The description of the image
             * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
             *              for any reason.
             */
            public static final String insertImage(ContentResolver cr, Bitmap source,
                                                   String title, String description) {
                ContentValues values = new ContentValues();
                values.put(Images.Media.TITLE, title);
                values.put(Images.Media.DESCRIPTION, description);
                values.put(Images.Media.MIME_TYPE, "image/jpeg");

                Uri url = null;
                String stringUrl = null;    /* value to be returned */

                try {
                    url = cr.insert(EXTERNAL_CONTENT_URI, values);

                    if (source != null) {
                        OutputStream imageOut = cr.openOutputStream(url);
                        try {
                            source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
                        } finally {
                            imageOut.close();
                        }

                        long id = ContentUris.parseId(url);
                        // Wait until MINI_KIND thumbnail is generated.
                        Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
                                Images.Thumbnails.MINI_KIND, null);
                        // This is for backward compatibility.
                        Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
                                Images.Thumbnails.MICRO_KIND);
                    } else {
                        Log.e(TAG, "Failed to create thumbnail, removing original");
                        cr.delete(url, null, null);
                        url = null;
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Failed to insert image", e);
                    if (url != null) {
                        cr.delete(url, null, null);
                        url = null;
                    }
                }

                if (url != null) {
                    stringUrl = url.toString();
                }

                return stringUrl;
            }

这个方法传递进去的是一个 Bitmap 对象,其余的 title 和 description 分别是图片文件的名称和一段描述。

广播可以更新 MediaStore

说到广播,在 Android 4.4 之前,是可以通过 ACTION_MEDIA_MOUNTED 广播,来通知系统刷新 MediaStore 的,不过假如你现在还在依赖这条广播,你会得到一个错误信息。

E/AndroidRuntime(23718): java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED from pid=23718, uid=10097

在 Android 4.4 之后,这个广播只能由系统进行广播,App 只能对该广播进行监听,在当前的系统分布环境下,这条路已经走不通了。
这样设计也很好理解,毕竟扫描全盘是非常的耗资源,所以系统肯定要把全盘扫描的权限拿在自己手里不开放出来,避免被第三方 App 滥用。
不过 Android 依然给我们提供了替代方案,那就是用 MediaScannerConnection 或者发送 ACTION_MEDIA_SCANNER_SCAN_FILE 广播。
接下来就来说说 ACTION_MEDIA_SCANNER_SCAN_FILE 这个广播。
通过广播刷新 MediaStore 的方式非常的简单,只需要指定文件路径和 Action 就好了。

val saveAs = "Your_Created_Image_File_Path"
val contentUri = Uri.fromFile(File(saveAs))
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri)
sendBroadcast(mediaScanIntent)

正常情况下,它是没有问题的,不过假如你发现它不生效,就需要检查一下你文件的路径是否传递正确。
通过查看 MediaScannerReceiver 的源码,可以发现 onReceive() 方法中,针对 ACTION_MEDIA_SCANNER_SCAN_FILE 还有一个限制条件,那就是传递进去的文件绝对路径,必须是以 Environment.getExternalStorageDirectory() 方法的返回值开头。

操作 MediaScannerConnection 类

刷新 MediaStore 还有一个最通用也是我推荐的一个方法,那就是使用 MediaScannerConnection 进行操作。

不同于 MediaStore.Image.Media 和广播的方式,使用 MediaScannerConnection 不仅可以保存文件,还可以指定文件路径,最好的就是,它还支持刷新完成的回调。

如果我们对时序有要求,并且需要制定文件保存路径的话,最好的方式就是直接使用 MediaScannerConnection 类进行操作,并且这也应该是兼容最好的方式。

这里我们主要是利用 MediaScannerConnection 类的 scanFile() 方法进行触发扫描。
通过 scanFile() 方法,我们只需要制定一个待刷新的文件路径和对应的 MimeType 即可,它支持传递多个路径,也可就是支持批量扫描。

注意这里的 MimeType 是一定要填写的,并且不能写通配符 / 或 null,否则会导致刷新失败,通常我们保存的是一个图片的话,只需要传递 image/jpeg 即可。

最后一个参数, onScanCompletedListener 中可以监听我们扫描的结果,需要注意的是,假如这里扫描的是多个文件路径,它也会被回调多次。所以如果有什么在刷新之后的后续操作,就需要特殊处理一下(原因后面是说)。

MediaScannerConnection.scanFile(this
        , arrayOf(picFile.absolutePath)
        , arrayOf("image/jpeg"), { path, uri ->
    Log.i("cxmyDev", "onScanCompleted : " + path)
})

测试:“系统相册里怎么看不到我刚保存的图片,是我操作不对吗?”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值