Android 网络图片保存方案

主要解决有两个问题

一、Android 调用MediaStore.Images.Media.insertImage保存图片时生成两张图片的问题

解决方案:

/*// MediaStore.Images.Media.insertImage支持我们传递进去一个图片文件路径,不过我并不推荐使用这个方法,因为它会将原本的图片,再 Copy 一份,

// 到 Picture 目录下,也就是说你最终在磁盘上会得到两张相同的图片。

MediaStore.Images.Media.insertImage(contentResolver,

file.absolutePath, file.name, null)*/

// 保存图片替换方式

var values = ContentValues()

values.put(MediaStore.Images.Media.DATA, file.absolutePath)

values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")

var uri =contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

二、三种方法,刷新 Android 的 MediaStore!让你保存的图片立即出现在相册里!

Android扫描多媒体文件剖析

我推荐的方法,是使用 MediaScannerConnection 来实现。通过scanFile()方法,我们只需要制定一个待刷新的文件路径和对应的 MimeType 即可,它支持传递多个路径,也可就是支持批量扫描。

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

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

三、保存图片到相册

1、// 保存图片到相册方式一: 不支持我们指定路径,自动将图片保存至【Picture】目录下,更新MediaStore

private fun downloadPic1(pic: String?) {

Flowable.just(pic)

.map{ s: String?-> saveImage1(s)}

            .subscribeOn(Schedulers.io())//在io线程执行订阅内容

            .unsubscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())//在Android主线程执行观察

            .subscribe(Consumer {

                if (it.isNullOrBlank()) {

Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()

}else {

/*var fileUri = Uri.fromFile(File(it))

sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, fileUri))*/

                    MediaScannerConnection.scanFile(mContext, arrayOf(it), arrayOf("image/jpeg"), null)

Toast.makeText(this, "已保存在 ".plus(it), Toast.LENGTH_SHORT).show()

}

}, Consumer {

                Log.e(TAG, "onLongClick: 保存图片到相册 e = " +it.message)

Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()

})

}

@Throws(IOException::class, ExecutionException::class, InterruptedException::class)

private fun saveImage1(data: String?): String? {

val target: FutureTarget = Glide.with(this)

.asBitmap()

.load(data)

.submit()

var bitmap = target.get()

if (bitmap !=null) {

// 使用 Media.inserImage() 方法,不需要我们指定路径,会自动将图片保存至 Picture 目录下。

// 它也不支持我们指定路径。如果我们对图片保存的路径没有要求,并且保存的是一个 Bitmap 对象,此方法是非常的方便的。

        var stringUrl = MediaStore.Images.Media.insertImage(contentResolver,

                bitmap, data, data)

bitmap?.recycle()

/**

* 取保存的图片地址

*/

        var url = Uri.parse(stringUrl)

var result: String?

var cursor = MediaStore.Images.Media.query(contentResolver, url, arrayOf(MediaStore.Images.ImageColumns.DATA),//

                null, null, null)

return if (cursor ==null)

url.path

        else {

cursor.moveToFirst()

var index = cursor?.getColumnIndex(MediaStore.Images.ImageColumns.DATA)

result = cursor.getString(index)

cursor.close()

result

}

}

return ""

}

2、// 保存图片到相册方式二: 支持指定路径,更新MediaStore

private fun downloadPic2(pic: String?) {

Flowable.just(pic)

.map{ s: String?-> saveImage2(s)}

            .map{ file: File?->

                return@map file?.let {

                    /*// MediaStore.Images.Media.insertImage支持我们传递进去一个图片文件路径,不过我并不推荐使用这个方法,因为它会将原本的图片,再 Copy 一份,

// 到 Picture 目录下,也就是说你最终在磁盘上会得到两张相同的图片。

MediaStore.Images.Media.insertImage(contentResolver,

file.absolutePath, file.name, null)*/

// 保存图片替换方式

                    var values = ContentValues()

values.put(MediaStore.Images.Media.DATA, file.absolutePath)

values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")

contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

/*// 正常情况下,它是没有问题的,不过假如你发现它不生效,就需要检查一下你文件的路径是否传递正确。通过查看 MediaScannerReceiver 的源码,

// 可以发现 onReceive() 方法中,针对 ACTION_MEDIA_SCANNER_SCAN_FILE 还有一个限制条件,那就是传递进去的文件绝对路径,

// 必须是以 Environment.getExternalStorageDirectory() 方法的返回值开头。

sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)))*/

// 刷新 MediaStore 替换方式:

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

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

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

// 这里我们主要是利用 MediaScannerConnection 类的 scanFile() 方法进行触发扫描

                    MediaScannerConnection.scanFile(mContext, arrayOf(file.absolutePath), arrayOf("image/jpeg"), null)

file

} ?:null

            }

            .subscribeOn(Schedulers.io())//在io线程执行订阅内容

            .unsubscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())//在Android主线程执行观察

            .subscribe({ file: File?->

                file?.let {

                    Toast.makeText(this, "已保存在 ".plus(file.absoluteFile), Toast.LENGTH_SHORT).show()

} ?: Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()

}){ throwable: Throwable->

                Log.e(TAG, "onLongClick: 保存图片到相册 e = " + throwable.message)

Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()

}

}

@Throws(IOException::class, ExecutionException::class, InterruptedException::class)

private fun saveImage2(data: String?): File? {

val fos: FileOutputStream

var file: File? =null

    val target: FutureTarget = Glide.with(this)

.asBitmap()

.load(data)

.submit()

var bitmap = target.get()

if (bitmap !=null) {

file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),

                SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date()) +".jpg")

fos = FileOutputStream(file)

bitmap?.compress(Bitmap.CompressFormat.JPEG, 80, fos)

fos.flush()

fos.close()

bitmap?.recycle()

}

return file

}

你可能感兴趣的:(Android 网络图片保存方案)