Android保存多张图片到本地的实现方法
01.实际开发保存图片遇到的问题
业务需求
在素材list页面的九宫格素材中,展示网络请求加载的图片。如果用户点击保存按钮,则保存若干张图片到本地。具体做法是,使用glide加载图片,然后设置listener监听,在图片请求成功onresourceready后,将图片资源resource保存到集合中。这个时候,如果点击保存控件,则循环遍历图片资源集合保存到本地文件夹。
具体做法代码展示
这个时候直接将请求网络的图片转化成bitmap,然后存储到集合中。然后当点击保存按钮的时候,将会保存该组集合中的多张图片到本地文件夹中。
//bitmap图片集合 private arraylist<bitmap> bitmaparraylist = new arraylist<>(); requestoptions requestoptions = new requestoptions() .transform(new glideroundtransform(mcontext, radius, cornertype)); glideapp.with(mivimg.getcontext()) .asbitmap() .load(url) .listener(new requestlistener<bitmap>() { @override public boolean onloadfailed(@nullable glideexception e, object model, target<bitmap> target, boolean isfirstresource) { return true; } @override public boolean onresourceready(bitmap resource, object model, target<bitmap> target, datasource datasource, boolean isfirstresource) { bitmaparraylist.add(resource); return false; } }) .apply(requestoptions) .placeholder(imageutils.getdefaultimage()) .into(mivimg); //循环遍历图片资源集合,然后开始保存图片到本地文件夹 mbitmap = bitmaparraylist.get(i); savepath = filesaveutils.getlocalimgsavepath(); fileoutputstream fos = null; try { file filepic = new file(savepath); if (!filepic.exists()) { filepic.getparentfile().mkdirs(); filepic.createnewfile(); } fos = new fileoutputstream(filepic); // 100 图片品质为满 mbitmap.compress(bitmap.compressformat.jpeg, 100, fos); } catch (ioexception e) { e.printstacktrace(); return null; } finally { if (fos != null) { try { fos.flush(); fos.close(); } catch (ioexception e) { e.printstacktrace(); } } //刷新相册 if (isscanner) { scanner(context, savepath); } }
遇到的问题
保存图片到本地后,发现图片并不是原始的图片,而是展现在view控件上被裁切的图片,也就是imageview的尺寸大小图片。
为什么会遇到这种问题
如果你传递一个imageview作为.into()的参数,glide会使用imageview的大小来限制图片的大小。例如如果要加载的图片是1000x1000像素,但是imageview的尺寸只有250x250像素,glide会降低图片到小尺寸,以节省处理时间和内存。
在设置into控件后,也就是说,在onresourceready方法中返回的图片资源resource,实质上不是你加载的原图片,而是imageview设定尺寸大小的图片。所以保存之后,你会发现图片变小了。
那么如何解决问题呢?
第一种做法:九宫格图片控件展示的时候会加载网络资源,然后加载图片成功后,则将资源保存到集合中,点击保存则循环存储集合中的资源。这种做法只会请求一个网络。由于开始
第二种做法:九宫格图片控件展示的时候会加载网络资源,点击保存九宫格图片的时候,则依次循环请求网络图片资源然后保存图片到本地,这种做法会请求两次网络。
02.直接用http请求图片并保存本地
http请求图片
/** * 请求网络图片 * @param url url */ private static long time = 0; public static inputstream httpimage(string url) { long l1 = system.currenttimemillis(); url myfileurl = null; bitmap bitmap = null; httpurlconnection conn = null; inputstream is = null; try { myfileurl = new url(url); } catch (malformedurlexception e) { e.printstacktrace(); } try { conn = (httpurlconnection) myfileurl.openconnection(); conn.setconnecttimeout(10000); conn.setreadtimeout(5000); conn.setdoinput(true); conn.connect(); is = conn.getinputstream(); } catch (ioexception e) { e.printstacktrace(); } finally { try { if (is != null) { is.close(); conn.disconnect(); } } catch (ioexception e) { e.printstacktrace(); } long l2 = system.currenttimemillis(); time = (l2-l1) + time; logutils.e("毫秒值"+time); //保存 } return is; } ```
保存到本地
inputstream inputstream = httpimage( "https://img1.haowmc.com/hwmc/material/2019061079934131.jpg"); string localimgsavepath = filesaveutils.getlocalimgsavepath(); file imagefile = new file(localimgsavepath); if (!imagefile.exists()) { imagefile.getparentfile().mkdirs(); try { imagefile.createnewfile(); } catch (ioexception e) { e.printstacktrace(); } } fileoutputstream fos = null; bufferedinputstream bis = null; try { fos = new fileoutputstream(imagefile); bis = new bufferedinputstream(inputstream); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (exception e) { e.printstacktrace(); } finally { try { if (bis != null) { bis.close(); } if (fos != null) { fos.close(); } } catch (ioexception e) { e.printstacktrace(); } }
03.用glide下载图片保存本地
glide下载图片
file file = glide.with(reflexactivity.this) .load(url.get(0)) .downloadonly(500, 500) .get();
保存到本地
string localimgsavepath = filesaveutils.getlocalimgsavepath(); file imagefile = new file(localimgsavepath); if (!imagefile.exists()) { imagefile.getparentfile().mkdirs(); imagefile.createnewfile(); } copy(file,imagefile); /** * * @param source 输入文件 * @param target 输出文件 */ public static void copy(file source, file target) { fileinputstream fileinputstream = null; fileoutputstream fileoutputstream = null; try { fileinputstream = new fileinputstream(source); fileoutputstream = new fileoutputstream(target); byte[] buffer = new byte[1024]; while (fileinputstream.read(buffer) > 0) { fileoutputstream.write(buffer); } } catch (exception e) { e.printstacktrace(); } finally { try { if (fileinputstream != null) { fileinputstream.close(); } if (fileoutputstream != null) { fileoutputstream.close(); } } catch (ioexception e) { e.printstacktrace(); } } } ```
04.如何实现连续保存多张图片
思路:循环子线程
- 可行(不推荐), 如果我要下载9个图片,将子线程加入for循环内,并最终呈现。
- 有严重缺陷,线程延时,图片顺序不能做保证。如果是线程套线程的话,第一个子线程结束了,嵌套在该子线程f的or循环内的子线程还没结束,从而主线程获取不到子线程里获取的图片。
- 还有就是如何判断所有线程执行完毕,比如所有图片下载完成后,吐司下载完成。
不建议的方案
创建一个线程池来管理线程,关于线程池封装库,可以看线程池简单封装
这种方案不知道所有线程中请求图片是否全部完成,且不能保证顺序。
arraylist<string> images = new arraylist<>(); for (string image : images){ //使用该线程池,及时run方法中执行异常也不会崩溃 poolthread executor = baseapplication.getapplication().getexecutor(); executor.setname("getimage"); executor.execute(new runnable() { @override public void run() { //请求网络图片并保存到本地操作 } }); }
推荐解决方案
arraylist<string> images = new arraylist<>(); apiservice apiservice = retrofitservice.getinstance().getapiservice(); //注意:此处是保存多张图片,可以采用异步线程 arraylist<observable<boolean>> observables = new arraylist<>(); final atomicinteger count = new atomicinteger(); for (string image : images){ observables.add(apiservice.downloadimage(image) .subscribeon(schedulers.io()) .map(new function<responsebody, boolean>() { @override public boolean apply(responsebody responsebody) throws exception { saveio(responsebody.bytestream()); return true; } })); } // observable的merge 将所有的observable合成一个observable,所有的observable同时发射数据 disposable subscribe = observable.merge(observables).observeon(androidschedulers.mainthread()) .subscribe(new consumer<boolean>() { @override public void accept(boolean b) throws exception { if (b) { count.addandget(1); log.e("yc", "download is succcess"); } } }, new consumer<throwable>() { @override public void accept(throwable throwable) throws exception { log.e("yc", "download error"); } }, new action() { @override public void run() throws exception { log.e("yc", "download complete"); // 下载成功的数量 和 图片集合的数量一致,说明全部下载成功了 if (images.size() == count.get()) { toastutils.showroundrecttoast("保存成功"); } else { if (count.get() == 0) { toastutils.showroundrecttoast("保存失败"); } else { toastutils.showroundrecttoast("因网络问题 保存成功" + count + ",保存失败" + (images.size() - count.get())); } } } }, new consumer<disposable>() { @override public void accept(disposable disposable) throws exception { log.e("yc","disposable"); } }); private void saveio(inputstream inputstream){ string localimgsavepath = filesaveutils.getlocalimgsavepath(); file imagefile = new file(localimgsavepath); if (!imagefile.exists()) { imagefile.getparentfile().mkdirs(); try { imagefile.createnewfile(); } catch (ioexception e) { e.printstacktrace(); } } fileoutputstream fos = null; bufferedinputstream bis = null; try { fos = new fileoutputstream(imagefile); bis = new bufferedinputstream(inputstream); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (exception e) { e.printstacktrace(); } finally { try { if (bis != null) { bis.close(); } if (fos != null) { fos.close(); } } catch (ioexception e) { e.printstacktrace(); } //刷新相册代码省略…… } }
链接地址:https://github.com/yangchong2...
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。
上一篇: 唐朝著名酷吏,来俊臣有什么特殊爱好?