【缓存图片】网络图片本地存储策略

情景一:在开发过程中经常会遇到将网络图片存储到本地,

目的:为了避免图片多次下载

实现策略:使图片的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,怎么将图片存至本地

目的:通过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;
	}

2.通过BitmapFactocy.decodeStream方法将刘转换为Bitmap对象

/**
	 * 通过url得到Bitmap
	 * 
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static Bitmap getBitmapFromUrl(String url) throws Exception {
		return BitmapFactory.decodeStream(getInputStreamFromUrl(url));
	}

3.将Bitmap对象存储至本地

    /**
     * 保存图片文件
     * @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);
	}

这就是一个非常简单的网络图片的存储策略,从图片下载到图片显示,是不是又领悟了些什么呢。


你可能感兴趣的:(网络图片存储策略)