Android仿微信无网络保存图片到相册,基于Glide4.x与Rxjava2.x

1,630 阅读3分钟

今天在微信预览大图时,发现其在无网络的情况下也可以保存图片,我觉得这是个常见的需求,所以动手实现类似的需求,中间也踩了挺多坑,所以跟大家一起分享我踩过的坑。

知识点概览

1、Glide如何获取缓存的图片

2、Android文件的拷贝

3、保存的图片如何通知系统更新图库

=============分割线============

1、Glide如何获取缓存的图片

Glide4.x以下,加载图片的方式为

Glide.with(context)
        .load(obj)
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .into(imageView);

Glide4.x以上时,为

RequestOptions requestOptions = new RequestOptions()
        .centerCrop()
        .dontAnimate()
        .diskCacheStrategy(DiskCacheStrategy.ALL);
Glide.with(context)
        .load(obj)
        .apply(requestOptions)
        .into(iv);

这里有个注意点,diskCacheStrategy方法的缓存策略需要指定为DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,如果指定的缓存策略为DiskCacheStrategy.NONE,是获取不到缓存图的。

我们首先需要获取缓存的File,Glide因为版本不同,获取File的方式也不同

Glide4.x以下获取的方法为

File file =  Glide.with(context)
        .load(imgUrl)
        .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
        .get();

Glide4.x以上时,为

File file = Glide.with(context)
        .download(imgUrl)
        .submit()
        .get();

2、Android文件的拷贝

获得File后,这只是个文件,存在系统data目录下,我们需要将File复制到sd卡的目录下,并重命名为.jpg文件。

String fileParentPath =
        Environment.getExternalStorageDirectory().getAbsolutePath() + "/XingTu/Image";
File appDir = new File(fileParentPath);
if (!appDir.exists()) {
    appDir.mkdirs();
}
//获得原文件流
FileInputStream fis = new FileInputStream(file);
//保存的文件名
String fileName = "xt" + System.currentTimeMillis() + ".jpg";
//目标文件
File targetFile = new File(appDir, fileName);
//输出文件流
FileOutputStream fos = new FileOutputStream(targetFile);
// 缓冲数组
byte[] b = new byte[1024 * 8];
while (fis.read(b) != -1) {
    fos.write(b);
}
fos.flush();
fis.close();
fos.close();

首先创建要保存路径的文件appDir,然后创建目标文件targetFile,接着创建输入输出流,通过while循环输出文件,最后别忘了调用flush和close方法释放资源。

3、保存的图片如何通知系统更新图库

我们保存玩图片后,需要通知系统相册刷新,否则是找不到保存的图片的。

关于通知系统相册更新,有两种方法。

第一,直接发送广播

Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(file);
intent.setData(uri);
context.sendBroadcast(intent);

第二,使用MediaScannerConnection

//扫描媒体库
String extension = MimeTypeMap.getFileExtensionFromUrl(targetFile.getAbsolutePath());
String mimeTypes = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
MediaScannerConnection.scanFile(context, new String[]{targetFile.getAbsolutePath()},
        new String[]{mimeTypes},null);

上面两种方法都能达到更新效果,但是查看了其他博客,有人说第一种方法某些情况下无效,具体我没有深究,这里推荐使用第二种方法更新。

完整代码

public void saveImgToGallery(final Context context, final String imgUrl){
    Observable.create((ObservableOnSubscribe<File>) emitter -> {
        //Glide提供了一个download() 接口来获取缓存的图片文件,
        // 但是前提必须要设置diskCacheStrategy方法的缓存策略为
        // DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,
        // 还有download()方法需要在子线程里进行
        File file = Glide.with(context)
                .download(imgUrl)
                .submit()
                .get();
        String fileParentPath = 
                Environment.getExternalStorageDirectory().getAbsolutePath() + "/XingTu/Image";
        File appDir = new File(fileParentPath);
        if (!appDir.exists()) {
            appDir.mkdirs();
        }
        //获得原文件流
        FileInputStream fis = new FileInputStream(file);
        //保存的文件名
        String fileName = "xt" + System.currentTimeMillis() + ".jpg";
        //目标文件
        File targetFile = new File(appDir, fileName);
        //输出文件流
        FileOutputStream fos = new FileOutputStream(targetFile);
        // 缓冲数组
        byte[] b = new byte[1024 * 8];
        while (fis.read(b) != -1) {
            fos.write(b);
        }
        fos.flush();
        fis.close();
        fos.close();
        //扫描媒体库
        String extension = MimeTypeMap.getFileExtensionFromUrl(targetFile.getAbsolutePath());
        String mimeTypes = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
        MediaScannerConnection.scanFile(context, new String[]{targetFile.getAbsolutePath()},
                new String[]{mimeTypes},null);
        emitter.onNext(targetFile);
    })
            .subscribeOn(Schedulers.io()) //发送事件在io线程
            .observeOn(AndroidSchedulers.mainThread())//最后切换主线程提示结果
            .subscribe(file -> ToastUtil.showToast("保存图片成功"),
                    throwable -> ToastUtil.showToast("保存失败"));
}

总结:这是一个常见的需求,虽然代码很短,但是涉及到三个大的知识点,需要注重平常的积累,在遇到问题时首先冷静思考,而不是心里mmp,这样才能有效解决问题。

第一次写博客,如果有不对的地方欢迎一起交流。

最后放一个操作的gif视频,压的不好,见谅!