情景一:在开发过程中经常会遇到将网络图片存储到本地,
目的:为了避免图片多次下载
实现策略:使图片的URL与存储文件文件名一一对应。
如果将文件的URL存储为文件名是行不通的,因为URL会包涵很多特殊字符,文件存储命名是不允许的。
所以我们需要对图片的URL与文件名关联切符合文件名命名规则。
我的实现方式是采用MD5算法,
MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。将经过算法处理过的字节数组转换成一个BigInteger类型,再输出为String类型,这样在网络图片URL不变的情况下,图片存储的文件名就唯一。
public static String generateImageId(String imageUrl) { final String HASH_ALGORITHM = "MD5"; final int RADIX = 10 + 26; // 10 digits + 26 letters byte[] md5 = null; try { MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM); digest.update(imageUrl.getBytes()); md5 = digest.digest(); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } BigInteger bi = new BigInteger(md5).abs(); return bi.toString(RADIX); }
情景二:用户说我们的应用很费流量总是看到在下载东西,且内存被大量的图片占用
目的:减少流量的消耗,减少内存的占用
原因:平凡重复多次下载,安卓手机的RAM存储空间是有限的
实现策略:图片存储在SD开中,首先检测是否有SDcard.下载之前判断本地是否已经存在文件
首先要判断SDcard是否可以正常使用
/** * 判断SD卡是否存在 */ public static boolean checkHasSdcard() { String status = Environment.getExternalStorageState(); return status.equals(Environment.MEDIA_MOUNTED); }
通过上述情景一的方法,我们已经将网络图片URL与本地文件唯一关联起来了,接下来在下载图片时判断本地是否已存在,不存在则下载,减少重复下载次数。
<span style="font-size:18px;"> /** * 判断SD卡上是否存在此文件 * * @param fileName * @return isExists * @throws IOException */ public static boolean hasSDFile(String fileName) throws IOException { return new File(fileName).exists(); }</span>
目的:通过URL下载至本地
实现策略:利用HttpURLConnection读取流将输入流转换为Bitmap对象再
1.读取输入流
/** * 使用HttpURLConnection从url获取InputStream * * @param httpUrl * @return inputStream * @throws IOException */ public static InputStream getInputStreamFromUrl(String httpUrl) { try { URL url = new URL(httpUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(10 * 1000); if (conn.getResponseCode() == 200) { return conn.getInputStream(); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
/** * 通过url得到Bitmap * * @param url * @return * @throws Exception */ public static Bitmap getBitmapFromUrl(String url) throws Exception { return BitmapFactory.decodeStream(getInputStreamFromUrl(url)); }
/** * 保存图片文件 * @param filePath 本地存储路径 * @param bm * @param fileName 参考generateId * @throws IOException */ public static void write2SDFromBitmap(String filePath, Bitmap bm, String fileName) throws IOException { if (bm == null) return; File dirFile = new File(filePath); if (!dirFile.exists()) { dirFile.mkdirs(); } File myCaptureFile = new File(filePath + File.separator + fileName); if (!myCaptureFile.exists()) { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(myCaptureFile)); bm.compress(Bitmap.CompressFormat.JPEG, 80, bos); bos.flush(); bos.close(); } }我们在存储时可以对图片进行格式处理。
情景四:我们下载了若干大图,没显示几张就OOM崩溃了
目的:减少图片占用内存
策略:手机上显示的图不需要非常详细,这么理解,有一张10M的图片需要显示而我们真正在手机上看到的或许只有50K,那么加载那些根本就用不到的信息是无用的也是非常消耗资源。而且如果一张图10M普通的加载方式占用的内存是10M+,一个应用分配的栈没几张就被占满,程序的崩溃就不在话下了。
这里呢网上有非常多的示例讲到关于“网络异步加载双缓存图片的机制”可以搜一搜,三缓存再加上额外存储卡。本篇文章咱不介绍这种机制,看下文。
1.通过文件路径获取Bitmap
BitmapFactory.decodeFile(imgPath);注:这里提示一点,手机图片的尺寸要求是比较特殊的,一般服务器后台返回的图片分辨率也是针对手机特殊规定,一般不超过100K,在开发过程中需要对网络图片的分辨率严格规范。阿里,京东等应用中的图片很大一部分在50k以下。
2.正题,情景四1中你只告诉我怎么将文件路径转化为Bitmap,你还是没有告诉我怎么做处理。不急下面就是你需要的内容。
2.1 opts.inSampleSize
public static Bitmap readBitMap(Context context, int resId) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; opt.inSampleSize = 4;//缩小为原始的1/16 // 获取资源图片 InputStream is = context.getResources().openRawResource(resId); return BitmapFactory.decodeStream(is, null, opt);
2.2 opt.inJustDecodeBounds
public static Bitmap readBitMap(Context context, int resId) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; opt.inJustDecodeBounds = true;//设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出/ //原始图片的长度和宽度 // 获取资源图片 InputStream is = context.getResources().openRawResource(resId); BitmapFactory.decodeStream(is, null, opt); int width_tmp = opt.outWidth, height_tmp = opt.outHeight; opt.outWidth = width_tmp/2; opt.outHeight = height_tmp/2; opt.inJustDecodeBounds = false; return BitmapFactory.decodeStream(is, null, opt); }