标签:
经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目 Android-Universal-Image-Loader 或者 ignition 都是个很好的选择。
在这里把原来 写过的优化的代码直接拿出来,经过测试千张图片效果还是不错的。
免费培训课:http://www.jinhusns.com/Products/Curriculum/?type=xcj
工程目录
至于 Activity 就是加载了 1个网格布局
01./**02.* 实现 异步加载 和 2级缓存03.*/04.public class ImagedownActivity extends Activity {05. 06.public static String filepath;07.@Override08.public void onCreate(Bundle savedInstanceState) {09.super.onCreate(savedInstanceState);10.setContentView(R.layout.main);11.filepath = this.getCacheDir().getAbsolutePath();12.GridView gv=(GridView)findViewById(R.id.gridview01);13.//设置列数14.gv.setNumColumns(3);15.//配置适配器16.gv.setAdapter(new Myadapter(this));17.}18. 19.@Override20.protected void onDestroy() {21.// TODO Auto-generated method stub22.//activity 销毁时,清除缓存23.MyImageLoader.removeCache(filepath);24.super.onDestroy();25.}26. 27.}接下来 Myadapter.java(给网格每个item塞入图片 )在生成每个 item 异步请求网络获取image
01.public class Myadapter extends BaseAdapter {02.private Context context;03.private String root ="http://192.168.0.100:8080/Android_list/";04.private String[] URLS;05.private final MyImageLoader myImageLoader = new MyImageLoader(context);;06. 07./**08.* adapter 初始化的时候早一堆数据09.* 这里我请求的是自己搭的服务器10.* @param context11.*/12.public Myadapter(Context context){13.this.context =context;14.URLS = new String[999];15.for (int i = 0; i < 999; i++) {16.URLS[i] = root + (i+1)+".jpg";17.} 18.}19. 20. 21.@Override22.public int getCount() {23.return URLS.length;24.}25. 26.@Override27.public Object getItem(int position) {28.return URLS[position];29.}30. 31.@Override32.public long getItemId(int position) {33.return URLS[position].hashCode();34.}35. 36. 37. 38.@Override39.public View getView(int position, View view, ViewGroup parent) {40.ImageView imageView;41.if(view==null){42.imageView=new ImageView(context);43.imageView.setLayoutParams(new GridView.LayoutParams(200,190));44.imageView.setAdjustViewBounds(false);45.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);46.imageView.setPadding(5, 5, 5, 5);47.}else{48.imageView=(ImageView)view;49.}50.myImageLoader.downLoad(URLS[position], (ImageView)imageView , context);51.return imageView;52.}53.}MyImageLoader.java
001.public class MyImageLoader { 002. 003.//最大内存 004.final static int memClass = (int) Runtime.getRuntime().maxMemory(); 005.private Context context; 006. 007.// 是否缓存到硬盘 008.private boolean diskcache = true; 009. 010.// 定义一级 缓存的图片数 011.private static final int catch_num = 10; 012. 013.// 定义二级缓存 容器 软引用 014.private static ConcurrentHashMap<String, SoftReference<Bitmap>> current_hashmap = new ConcurrentHashMap<String, SoftReference<Bitmap>>(); 015. 016.// 定义一级缓存容器 强引用 (catch_num ,0.75f,true) 默认参 数 2.加载因子默认 3.排序模式 true 017.private static LinkedHashMap<String, Bitmap> link_hashmap = new LinkedHashMap<String, Bitmap>(catch_num ,0.75f,true) { 018. 019.// 必须实现的方法 020.protected boolean removeEldestEntry(java.util.Map.Entry<String, Bitmap> eldest) { 021./** 当一级缓存中 图片数量大于 定义的数量 放入二级缓存中 022.*/ 023.if (this.size() > catch_num) { 024.// 软连接的方法 存进二级缓存中 025.current_hashmap.put(eldest.getKey(), new SoftReference<Bitmap>( 026.eldest.getValue())); 027.//缓存到本地 028.cancheToDisk(eldest.getKey(),eldest.getValue() ); 029. 030.return true; 031.} 032.return false; 033.}; 034.}; 035. 036.public MyImageLoader(Context context) { 037. 038.} 039. 040. 041./** 042.* 外部调用此方法 进行下载图片 043.*/ 044.public void downLoad(String key , ImageView imageView,Context context){ 045.// 先从缓存中找 。 046.context = this.context; 047. 048.Bitmap bitmap = getBitmapFromCache(key); 049.if( null!= bitmap){ 050.imageView.setImageBitmap(bitmap); 051.cancleDownload(key, imageView); //取消下载 052.return ; 053.} 054. 055.// 缓存中 没有 把当前的 imageView 给他 得到 task 056.if(cancleDownload(key, imageView)){ //没有任务进行。,。。开始下载 057.ImageDownloadTask task = new ImageDownloadTask(imageView); 058.Zhanwei_Image zhanwei_image = new Zhanwei_Image(task); 059.//先把占位的图片放进去 060.imageView.setImageDrawable(zhanwei_image); 061.// task执行任务 062.task.execute(key); 063.} 064.} 065. 066. 067./** 此方法 用于优化 : 用户直接 翻到 哪个 就先加载 哪个、 068.* @param key - URL 069.* @param imageView - imageView 070.* core: 给当前的 imageView 得到给他下载的 task 071.*/ 072. 073.private boolean cancleDownload(String key,ImageView imageView){ 074.// 给当前的 imageView 得到给他下载的 task 075.ImageDownloadTask task = getImageDownloadTask(imageView); 076.if(null != task){ 077.String down_key = task.key; 078.if( null == down_key || !down_key.equals(key)){ 079.task.cancel(true); // imageview 和 url 的key不一样 取消下载 080.}else{ 081.return false; //正在下载: 082.} 083.} 084.return true; //没有正在下载 085.} 086. 087. 088. 089.// public void getThisProcessMemeryInfo() { 090.// int pid = android.os.Process.myPid(); 091.// android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(new int[] {pid}); 092.// System.out.println("本应用当前使用了" + (float)memoryInfoArray[0].getTotalPrivateDirty() / 1024 + "mb的内存"); 093.// } 094. 095. 096. 097./** 098.* 从缓存中得到 图片的方法 1.先从一级 缓存找 linkhashmap 不是线程安全的 必须要加同步 099.*/ 100.public Bitmap getBitmapFromCache(String key) { 101.//1.先在一级缓存中找 102.synchronized (link_hashmap) { 103.Bitmap bitmap = link_hashmap.get(key); 104.if (null != bitmap) { 105.link_hashmap.remove(key); 106.// 按照 LRU是Least Recently Used 近期最少使用算法 内存算法 就近 就 原则 放到首位 107.link_hashmap.put(key, bitmap); 108.System.out.println(" 在缓存1中找图片了 =" +key); 109.return bitmap; 110.} 111.} 112. 113.// 2. 到二级 缓存找 114.SoftReference<Bitmap> soft = current_hashmap.get(key); 115.if (soft != null) { 116.//得到 软连接 中的图片 117.Bitmap soft_bitmap = soft.get(); 118.if (null != soft_bitmap) { 119.System.out.println(" 在缓存2中找图片了 =" +key); 120.return soft_bitmap; 121.} 122.} else { 123.// 没有图片的话 把这个key删除 124.current_hashmap.remove(key); 125.} 126. 127. 128.//3.都没有的话去从外部缓存文件读取 129.if(diskcache){ 130.Bitmap bitmap = getBitmapFromFile(key); 131.if(bitmap!= null){ 132.link_hashmap.put(key, bitmap); //将图片放到一级缓存首位 133.return bitmap; 134.} 135.} 136. 137.return null; 138.} 139. 140. 141./** 142.* 缓存到本地文件 143.* @param key 144.* @param bitmap 145.*/ 146.public static void cancheToDisk(String key ,Bitmap bitmap ){ 147.//2.缓存bitmap至/data/data/packageName/cache/文件夹中 148.try { 149.String fileName = getMD5Str(key); 150.String filePath = ImagedownActivity.filepath + "/" + fileName; 151.System.out.println("缓存到本地===" + filePath); 152.FileOutputStream fos = new FileOutputStream(filePath); 153.bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); 154. 155.} catch (Exception e) { 156. 157.} 158.} 159. 160. 161./** 162.* 从外部文件缓存中获取bitmap 163.* @param url 164.* @return 165.*/ 166.private Bitmap getBitmapFromFile(String url){ 167.Bitmap bitmap = null; 168.String fileName = getMD5Str(url); 169.if(fileName == null){ 170.return null; 171.} 172.String filePath = ImagedownActivity.filepath + "/" + fileName; 173.try { 174.FileInputStream fis = new FileInputStream(filePath); 175.bitmap = BitmapFactory.decodeStream(fis); 176.System.out.println("在本地缓存中找到图片==="+ filePath); 177.} catch (FileNotFoundException e) { 178.System.out.println("getBitmapFromFile==="+ e.toString()); 179.e.printStackTrace(); 180.bitmap = null; 181.} 182.return bitmap; 183.} 184. 185. 186. 187./** 188.* 清理文件缓存 189.* @param dirPath 190.* @return 191.*/ 192.public static boolean removeCache(String dirPath) { 193.File dir = new File(dirPath); 194.File[] files = dir.listFiles(); 195.if(files == null || files.length == 0) { 196.return true; 197.} 198.int dirSize = 0; 199.//这里删除所有的缓存 200.int all_ = (int) ( 1 * files.length + 1); 201.//对files 进行排序 202.Arrays.sort(files, new FileLastModifiedSort()); 203.for (int i = 0; i < all_ ; i++) { 204.files[i].delete(); 205.} 206.return true; 207.} 208. 209. 210./** 211.* 根据文件最后修改时间进行排序 212.*/ 213.private static class FileLastModifiedSort implements Comparator<File> { 214.@Override 215.public int compare(File lhs, File rhs) { 216.if(lhs.lastModified() > rhs.lastModified()) { 217.return 1; 218.} else if(lhs.lastModified() == rhs.lastModified()) { 219.return 0; 220.} else { 221.return -1; 222.} 223.} 224.} 225. 226. 227./** 228.* MD5 加密 229.*/ 230.private static String getMD5Str(String str) { 231.MessageDigest messageDigest = null; 232.try { 233.messageDigest = MessageDigest.getInstance("MD5"); 234.messageDigest.reset(); 235.messageDigest.update(str.getBytes("UTF-8")); 236.} catch (NoSuchAlgorithmException e) { 237.System.out.println("NoSuchAlgorithmException caught!"); 238.return null; 239.} catch (UnsupportedEncodingException e) { 240.e.printStackTrace(); 241.return null; 242.} 243. 244.byte[] byteArray = messageDigest.digest(); 245.StringBuffer md5StrBuff = new StringBuffer(); 246.for (int i = 0; i < byteArray.length; i++) { 247.if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) 248.md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i])); 249.else 250.md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); 251.} 252.return md5StrBuff.toString(); 253.} 254. 255. 256.// ------------------------ 异步加载---------------------------- 257./** 258.* 占位的 图片 或者 颜色 用来绑定 相应的图片 259.*/ 260.class Zhanwei_Image extends ColorDrawable{ 261.//里面存放 相应 的异步 处理时加载好的图片 ----- 相应的 task 262.private final WeakReference<ImageDownloadTask> taskReference; 263.public Zhanwei_Image(ImageDownloadTask task){ 264.super(Color.BLUE); 265.taskReference = new WeakReference<MyImageLoader.ImageDownloadTask>(task); 266.} 267.// 返回去这个 task 用于比较 268.public ImageDownloadTask getImageDownloadTask(){ 269.return taskReference.get(); 270.} 271.} 272. 273. 274.// 根据 给 的 iamgeView、 得到里面的 task 用于和当前的 task比较是不是同1个 275.private ImageDownloadTask getImageDownloadTask(ImageView imageView){ 276.if( null != imageView){ 277.Drawable drawable = imageView.getDrawable(); 278.if( drawable instanceof Zhanwei_Image) 279.return ((Zhanwei_Image)drawable).getImageDownloadTask(); 280. 281.} 282.return null; 283.} 284. 285. 286. 287./** 288.* 把图片 添加到缓存中 289.*/ 290.public void addBitmap(String key, Bitmap bitmap) { 291.if (null != bitmap) { 292.synchronized (link_hashmap) { // 添加到一级 缓存中 293.link_hashmap.put(key, bitmap); 294.} 295.} 296.} 297. 298. 299./** 在后台 加载每个图片 300.* 第一个参数 第2个要进度条不 第三个返回结果 bitmap 301.*/ 302.class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> { 303. 304.private String key; 305.private WeakReference<ImageView> imgViReference; 306. 307.public ImageDownloadTask(ImageView imageView) { 308.//imageView 传进来 。。要给哪个iamgeView加载图片 309.imgViReference = new WeakReference<ImageView>( 310.imageView); 311.} 312. 313.@Override 314.protected Bitmap doInBackground(String... params){ 315.key = params[0]; 316.//调用下载函数 根据 url 下载 317.return downloadBitmap(key); 318.} 319. 320.@Override 321.protected void onPostExecute(Bitmap result) { 322.if(isCancelled()){ 323.result = null; 324.} 325. 326.System.out.println("result=="+ result.getByteCount()+"---memClassmemery="+memClass); 327. 328.if(null!= result){ 329.//保存到缓存中 330.addBitmap(key, result); 331.ImageView imageView = imgViReference.get(); 332.if( null != imageView){ 333.//向 imageView 里面放入 bitmap 334.ImageDownloadTask task = getImageDownloadTask(imageView); 335. 336./** 337.* 判断 是不是 同一个 task( ) 338.* 如果当前这个 task == imageView 里面的那个 task 就是同1个 339.*/ 340.if( this == task ){ 341.imageView.setImageBitmap(result); 342. 343.} 344.} 345.} 346.} 347.} 348. 349. 350./** 351.* 连接网络 客户端 下载图片 352.*/ 353.private Bitmap downloadBitmap(String url) { 354. 355.final HttpClient client = AndroidHttpClient.newInstance("Android"); 356.final HttpGet getRequest = new HttpGet(url); 357.try { 358.HttpResponse response = client.execute(getRequest); 359.final int statusCode = response.getStatusLine().getStatusCode(); 360. 361.if (statusCode != HttpStatus.SC_OK) { 362. 363.Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url); 364.return null; 365.} 366. 367.final HttpEntity entity = response.getEntity(); 368.if (entity != null) { 369.InputStream inputStream = null; 370.try { 371. 372.inputStream = entity.getContent(); 373./** 374.* 1.没有压缩直接将生成的bitmap返回去 375.*/ 376.// return BitmapFactory.decodeStream(inputStream); 377. 378./** 379.* 2.得到data后在这里把图片进行压缩 380.*/ 381.byte[] data = StreamTool.read(inputStream); 382.return BitmapManager.scaleBitmap(context, data, 0.3f); 383.// return BitmapFactory.decodeStream(new FlushedInputStream(inputStream)); 384.} finally { 385.if (inputStream != null) { 386.inputStream.close(); 387.} 388.entity.consumeContent(); 389.} 390.} 391.} catch (IOException e) { 392.getRequest.abort(); 393.} catch (IllegalStateException e) { 394.getRequest.abort(); 395.} catch (Exception e) { 396.getRequest.abort(); 397.} finally { 398.if ((client instanceof AndroidHttpClient)) { 399.((AndroidHttpClient) client).close(); 400.} 401.} 402.return null; 403.} 404. 405.}StreamTool.java
01.public class StreamTool { 02. 03.public static byte[] read(InputStream in) throws Exception{ 04.ByteArrayOutputStream out_byte = new ByteArrayOutputStream(); 05.byte[] buff = new byte[1024]; 06.int len=0; 07.while((len = in.read(buff))!= -1){ 08.//写到内存中 字节流 09.out_byte.write( buff, 0 , len); 10.} 11.out_byte.close(); 12.// 把内存数据返回 13.return out_byte.toByteArray(); 14.} 15.}BitmapManager.java ( 这个类里面对 网络资源的图片 进行了优化)
001.public class BitmapManager {002. 003./**004.* 按屏幕适配Bitmap005.*/006.public static Bitmap scaleBitmap(Context context, byte[] data , float percent) {007. 008.//这里我不获取了,假设是下面这个分辨率009.int screenWidth = 540;010.int screenrHeight = 950;011.//设置 options012.BitmapFactory.Options options = new BitmapFactory.Options();013./**014.* BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds.SDK中对这个成员的说明是这样的:015.* If set to true, the decoder will return null (no bitmap), but the out…016.* 也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你,017.* 它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。018.*/019.options.inJustDecodeBounds = true;020. 021.//读取022.Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);023. 024.int imgWidth = options.outWidth;025.int imgHeight = options.outHeight;026. 027.//如果比你设置的宽高大 就进行缩放,028.if(imgWidth > screenWidth * percent || imgHeight > screenrHeight * percent) {029.options.inSampleSize = calculateInSampleSize(options, screenWidth, screenrHeight, percent);030.}031. 032. 033./**034.* If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller 035.* to query the bitmap without having to allocate the memory for its pixels.036.* 037.* 如果设置成 true,这个编码将会返回1个null , 但是那个区域仍将被设置(也就是存在),允许(调用者)去查询那个没有分配 内存的像素 bitmap 038.*/039.options.inJustDecodeBounds = false;040. 041./**042.* Android的Bitmap.Config给出了bitmap的一个像素所对应的存储方式,043.* 有RGB_565,ARGB_8888,ARGB_4444,ALPHA_8四种。RGB_565表示的是红绿蓝三色分别用5,6,5个比特来存储,044.* 一个像素占用了5+6+5=16个比特。ARGB_8888表示红绿蓝和半透明分别用8,8,8,8个比特来存储,045.* 一个像素占用了8+8+8+8=32个比特。这样的话如果图片是以RGB_8888读入的,那么占用内存的大小将是RGB_565读入方式的2倍。046.* 通常我们给Imagview加载图片是通过setDrawable或者在xml文件中用android:src来设置047.* 默认的加载图片大小的方式是以RGB_8888读入的。048.* 049.*/050.options.inPreferredConfig = Bitmap.Config.RGB_565;051. 052./**053.* If this is set to true, then the resulting bitmap will allocate its pixels such that they can be purged 054.* if the system needs to reclaim memory.055.* 056.* 如果设置成 true, 这个结果bitmap 将会被分配像素,这样他们就能被 系统回收了,当系统需要回收内存的时候057.*/058.options.inPurgeable = true;059. 060./**061.* This field works in conjuction with inPurgeable.062.* 这个方法是在 inPurgeable 的基础上工作的063.*/064.options.inInputShareable = true;065. 066. 067.bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);068. 069.System.out.println("data==="+ data.length +" change == bitmap byte "+ bitmap.getByteCount());070.return bitmap;071.}072. 073. 074. 075.// options reqWidth 屏幕宽 reqHeight屏幕高 你的view是屏幕的多大076.public static int calculateInSampleSize(BitmapFactory.Options options, int screenWidth, int screenHeight ,float percent) {077. 078.// 原始图片宽高079.final int height = options.outHeight;080.final int width = options.outWidth;081.// 倍数082.int inSampleSize = 1;083. 084.if (height > screenHeight * percent || width > screenWidth * percent) {085. 086.// 计算目标宽高与原始宽高的比值087.final int inSampleSize_h = Math.round((float) height / (float)( screenHeight * percent));088. 089.final int inSampleSize_w = Math.round((float) width / (float)( screenWidth * percent));090. 091.// 选择两个比值中较小的作为inSampleSize的092.inSampleSize = inSampleSize_h < inSampleSize_w ? inSampleSize_h : inSampleSize_w;093. 094.System.out.println("inSampleSize===="+ inSampleSize);095.// 096.if(inSampleSize < 1) {097.inSampleSize = 1;098.}099.}100.//简单说这个数字就是 缩小为原来的几倍,根据你的image需要占屏幕多大动态算的(比如你用的权重设置layout)101.return inSampleSize;102.}103.}这个是代码输出的最多给这个进程分配的内存 128M
可以看到我上面的bitmapManager 里面有个 options.inPreferredConfig 注释写的很清楚,可以上去看一下,接下来贴几种格式的效果图
rgb565 和 argb_444 所占的内存 (54000)
看一下 argb_8888 ( 108000)
当然可能仔细看的人会看到我一开始截的 鸣人的效果图 上半部分 和 下半部分的颜色会有点问题。上面的rgb_565 生成的,和原图色彩可能会有点出入。
但是内存真心少了一半,所以各种取舍就看个人了,代码注释都谢的很清楚了。
至于 : MyImageLoaderLru.java 其实就是 MyImageLoader.java
先贴出代码不同地方的代码 : 就是在强引用的地方 把 LinkedHashMap 换成了 LruCache
01.// 获取单个进程可用内存的最大值 02.// 方式一:使用ActivityManager服务(计量单位为M) 03./*int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();*/ 04.// 方式二:使用Runtime类(计量单位为Byte) 05.final static int memClass = (int) Runtime.getRuntime().maxMemory(); 06.// 3. 定义一级缓存容器 强引用 (catch_num /2,0.75f,true) 默认参 数 2.加载因子默认 3.排序模式 true07.final static int max = memClass/5;08. 09.// LruCache 用强引用将 图片放入 LinkedHashMap 10.private static LruCache<String, Bitmap> lrucache = new LruCache<String, Bitmap>(max) {11.protected int sizeOf(String key, Bitmap value) { 12.if(value != null) { 13.// 计算存储bitmap所占用的字节数 14.return value.getRowBytes() * value.getHeight(); 15.} else { 16.return 0; 17.} 18.} 19. 20.@Override 21.protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { 22.if(oldValue != null) { 23.// 当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存 24.current_hashmap.put(key, new SoftReference<Bitmap>(oldValue)); 25.} 26.} 27.};1. 强引用:LruCache 后面再说,其实他内的内部封装的就是1个 LinkedHashMap 。LinkedHashMap 是线程不安全的,所以上面都会用到同步。
2. 软引用:ConcurrentHashMap 是线程安全的,并且支持高并发很有效率,这个后面也会说到,为什么要用 软引用 SoftReference,这个是在系统将要oom时,就会回收
软引用的对象资源,所以才会用到他,防止程序出异常 。
3. 磁盘缓存: 这个经常会看到网易新闻等,应用有些界面你看了很多图片,往上翻很多, 其实没有再次访问网络,会将部分image缓存在sdcard里。
4. 其中1个优化: 当比如用户快速滑动到 最底部,其实是最先加载显示给用户的部分的内容的,这样就是用户看到哪加载哪,1个是快,1个是避免资源浪费。
原理: 当用户进入界面加载图片 ,首先会从1级缓存强引用中找,找不到回去2级缓存软引用中找,找不到再去sdcard中找,再找不到才会去请求网络加载资源。
当然sdcard的缓存 看个人需求是否需要。
注: android 4.0 后 对 SoftReference 的回收机制进行了改变,所以你是可以不用 2级缓存的,直接去掉就好了。
只要控制好你的 lrucache 或者 linkedhashmap就好了。
免费培训课:http://www.jinhusns.com/Products/Curriculum/?type=xcj
android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存
标签:
原文地址:http://www.cnblogs.com/xiaoxiaojia/p/5481085.html