Android引进高速缓存的异步加载全分辨率

为什么要缓存

通过图像缩放,我们这样做是对的异步加载优化的大图,但现在的App这不仅是一款高清大图。图。动不动就是图文混排。以图代文,假设这些图片都载入到内存中。必然会OOM。因此,在用户浏览完图像后。应当马上将这些废弃的图像回收,可是。这又带来了另一个问题。也就是当用户在浏览完一次图片后,假设还要返回去再进行又一次浏览,那么这些回收掉的图像又要又一次进行载入,保不准就要那些无聊到蛋疼的人在那一边看你回收GC。一边看你又一次载入。这两件事情,肯定是互相矛盾的。也是影响性能的一个非常重要的原因。

内存缓存

针对这样一个非常须要找到一个彼此平衡点的问题,Google提供了一套内存缓存技术。

内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了高速訪问的方法。当中最核心的类是LruCache 。这个类非常适合用来缓存图片,它的主要算法原理是把近期使用的对象用强引用存储在 LinkedHashMap 中,并且把近期最少使用的对象在缓存值达到预设定值之前从内存中移除。LruCache 是在support-v4中才引入的,在引入LruCache 之前。Google建议的是使用软引用或弱引用 (SoftReference or WeakReference)来进行内存缓存。可是从Android 2.3開始,GC算法改动,软引用与弱引用相同会优先被GC回收。所以这样的方法也就没有太高的使用价值了,如今网上非常多还在继续使用SoftReference 和WeakReference的文章。大多都是过时的文章,建议大家跟上党的步伐,与时俱进。

LruCache使用

内存缓存LruCache所使用的内存缓存大小是由开发人员决定的。开发人员须要依据图像的使用率、分辨率、訪问频率、设备性能等非常多因素进行考虑。

这个平衡点常常须要非常多经验和測试来决定。

使用LruCache非常easy:

private LruCache<String, Bitmap> mMemoryCaches;

// 获取应用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
// 分配cache
int cacheSize = maxMemory / 10;
mMemoryCaches = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
}; // 从LruCache获取中获取缓存对象
public Bitmap getBitmapFromMemoryCaches(String url) {
return mMemoryCaches.get(url);
} // 添加缓存对象到LruCache
public void addBitmapToMemoryCaches(String url,Bitmap bitmap) {
if (getBitmapFromMemoryCaches(url) == null) {
mMemoryCaches.put(url, bitmap);
}
}

首先,我们须要声明LruCache。接着。通过LruCache的构造方法创建缓存对象,并为其分配cacheSize,这个cacheSize通常我们须要通过Runtime来获取,获取当前系统分给App的可用内存,并将这些内存的一部分用做LruCache缓存。LruCache中必须重写sizeOf方法。通过这种方法,LruCache能够获取每一个缓存对象的大小。子类必须重写。由于默认的LruCache获取的是缓存的个数。

。。尼玛。

最后,我们提供两个方法getBitmapFromMemoryCaches和addBitmapToMemoryCaches分别用来获取和添加内存缓存到LruCache。
等等,我们好像还没写释放内存的方法,对,不用你写了,Lru算法能够保证cacheSize不会OOM,一旦超过这个大小,GC就会回收时间最长的对象,释放空间。

为异步处理添加一级缓存

OK。在了解了关于缓存的基础信息后,我们回到如今这个样例,想想怎么利用缓存来进行异步处理的优化。首先,ListView、GridView这些娇生惯养的玩意儿。碰不得摔不得,更不能在它滚的开心的时候,你还在后面拼命玩载入。所以,第一个重点,滚的时候就让它开心的滚,滚完了再開始载入。

滚完再载入

要实现这一点,我们能够通过给Adapter添加AbsListView.OnScrollListener接口来实现。
当然,另一点须要注意,第一次初始化的时候,一定要手动来载入图片,不然系统推断你没滚,仅仅能调用onScroll方法,不会调用onScrollStateChanged方法。并且我们也须要在onScroll方法中来不断获取可见的Item。特别要注意的是visibleItemCount,仅仅要大于0的时候,才觉得是開始显示图片了。

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
mImageLoader.loadImages(mStart, mEnd);
} else {
mImageLoader.cancelAllTasks();
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mStart = firstVisibleItem;
mEnd = firstVisibleItem + visibleItemCount;
if (mFirstFlag  && visibleItemCount > 0) {
mImageLoader.loadImages(mStart, mEnd);
mFirstFlag = false;
}
}

载入显示的项目

载入数据的时候,获取第一个能显示的Item和最后一个可见的Item,仅仅载入这一部分。所以我们创建一个方法——loadImages(int start, int end)。这种方法用来载入从start到end之间的Item数据。
载入的时候,先从内存缓存中去取,假设有,那说明近期已经载入过了,那直接载入就好了,假设没有取到,那就开启synctask去下载。
public void loadImages(int start, int end) {
for (int i = start; i < end; i++) {
String url = Images.IMAGE_URLS[i];
Bitmap bitmap = getBitmapFromMemoryCaches(url);
if (bitmap == null) {
ASyncDownloadImage task = new ASyncDownloadImage(url);
mTasks.add(task);
task.execute(url);
} else {
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
}
}
}

这里我们在设置图片的时候,直接通过findViewWithTag。通过url来找到相应的Imageview,这里与之前不同是由于我们这里是依照start到end来进行载入,直接从ListView对象中获取相应的Imageview比較简单。

下载与Asynctask

下载依旧是使用老方法:
private static Bitmap getBitmapFromUrl(String urlString) {
Bitmap bitmap;
InputStream is = null;
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(conn.getInputStream());
bitmap = BitmapFactory.decodeStream(is);
conn.disconnect();
return bitmap;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
return null;
}

Asynctask也与之前基本相似:

class ASyncDownloadImage extends AsyncTask<String, Void, Bitmap> {

    private String url;

    public ASyncDownloadImage(String url) {
this.url = url;
} @Override
protected Bitmap doInBackground(String... params) {
url = params[0];
Bitmap bitmap = getBitmapFromUrl(url);
if (bitmap != null) {
addBitmapToMemoryCaches(url, bitmap);
}
return bitmap;
} @Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
mTasks.remove(this);
}
}

唯一不同的是。我们在下载好图像之后。会将图像载入到Lrucache。

组装

OK,万事具备。准备刷代码。在刷之前,我们先来又一次整理下思路,首先,在Adapter中,一载入ListView,就開始下载显示范围内的Item的图像,这时候缓存中当然没有。所以都去下载了,下完了就显示在Item中,并缓存起来。假设还没下完,你就迫不及待的滚起来了,那么马上取消全部task。让ListView欢快的滚。滚完之后,继续载入。
OK,该讲的都讲了,以下我们開始刷代码了。一切尽在不言中,仅仅有代码最懂你。
package com.imooc.listviewacyncloader;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView; import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set; public class ImageLoaderWithCaches { private Set<ASyncDownloadImage> mTasks;
private LruCache<String, Bitmap> mMemoryCaches;
private ListView mListView; public ImageLoaderWithCaches(ListView listview) {
this.mListView = listview;
mTasks = new HashSet<>();
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 10;
mMemoryCaches = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
} public void showImage(String url, ImageView imageView) {
Bitmap bitmap = getBitmapFromMemoryCaches(url);
if (bitmap == null) {
imageView.setImageResource(R.drawable.ic_launcher);
} else {
imageView.setImageBitmap(bitmap);
}
} public Bitmap getBitmapFromMemoryCaches(String url) {
return mMemoryCaches.get(url);
} public void addBitmapToMemoryCaches(String url,Bitmap bitmap) {
if (getBitmapFromMemoryCaches(url) == null) {
mMemoryCaches.put(url, bitmap);
}
} public void loadImages(int start, int end) {
for (int i = start; i < end; i++) {
String url = Images.IMAGE_URLS[i];
Bitmap bitmap = getBitmapFromMemoryCaches(url);
if (bitmap == null) {
ASyncDownloadImage task = new ASyncDownloadImage(url);
mTasks.add(task);
task.execute(url);
} else {
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
}
}
} private static Bitmap getBitmapFromUrl(String urlString) {
Bitmap bitmap;
InputStream is = null;
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(conn.getInputStream());
bitmap = BitmapFactory.decodeStream(is);
conn.disconnect();
return bitmap;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
return null;
} public void cancelAllTasks() {
if (mTasks != null) {
for (ASyncDownloadImage task : mTasks) {
task.cancel(false);
}
}
} class ASyncDownloadImage extends AsyncTask<String, Void, Bitmap> { private String url; public ASyncDownloadImage(String url) {
this.url = url;
} @Override
protected Bitmap doInBackground(String... params) {
url = params[0];
Bitmap bitmap = getBitmapFromUrl(url);
if (bitmap != null) {
addBitmapToMemoryCaches(url, bitmap);
}
return bitmap;
} @Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
mTasks.remove(this);
}
}
}

以下是Adapter的代码:

package com.imooc.listviewacyncloader;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView; import java.util.List; public class MyAdapterUseCaches extends BaseAdapter implements
AbsListView.OnScrollListener { private LayoutInflater mInflater;
private List<String> mData;
private ImageLoaderWithCaches mImageLoader;
private int mStart = 0, mEnd = 0;
private boolean mFirstFlag; public MyAdapterUseCaches(Context context, List<String> data, ListView listView) {
this.mData = data;
mInflater = LayoutInflater.from(context);
mImageLoader = new ImageLoaderWithCaches(listView);
mImageLoader.loadImages(mStart, mEnd);
mFirstFlag = true;
listView.setOnScrollListener(this);
} @Override
public int getCount() {
return mData.size();
} @Override
public Object getItem(int position) {
return mData.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
String url = mData.get(position);
ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.listview_item, null);
viewHolder.imageView =
(ImageView) convertView.findViewById(R.id.iv_lv_item);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setTag(url);
viewHolder.imageView.setImageResource(R.drawable.ic_launcher);
mImageLoader.showImage(url, viewHolder.imageView);
return convertView;
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
mImageLoader.loadImages(mStart, mEnd);
} else {
mImageLoader.cancelAllTasks();
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mStart = firstVisibleItem;
mEnd = firstVisibleItem + visibleItemCount;
if (mFirstFlag  && visibleItemCount > 0) {
mImageLoader.loadImages(mStart, mEnd);
mFirstFlag = false;
}
} public class ViewHolder {
public ImageView imageView;
}
}

是不是非常easy,如今引入缓存了,下载过的图片会临时保存在内存中。妈妈再也不用操心你OOM啦。

我们下拉试试,下载完的图片再次出现也能够马上载入了,除非滑动太多导致GC。
能够就看见。我们的这次利用缓存进行载入有这样几个特点:
1、初始化的时候载入
2、滑动的时候才载入
3、载入的内容暂存缓存中
4、仅仅载入显示的区域
后面我们将继续优化缓存。未完待续~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我的Github 我的视频 亩类网络

版权声明:本文博主原创文章。博客,未经同意不得转载。

Android引入高速缓存的异步加载全分辨率的更多相关文章

  1. [Android Pro] 使用CursorLoader异步加载数据 from 3.0

    Android 3.0引入了CursorLoader实现异步加载数据,为了避免同步查询数据库时阻塞UI线程的问题.在API 11之前可以通过下载支持库,来使之前的系统支持此功能,下载页面为 http: ...

  2. Android之使用Android-AQuery异步加载图片(一)

    第一节:转载地址(http://www.cnblogs.com/lee0oo0/archive/2012/10/25/2738299.html) // 必须实现AQuery这个类 AQuery aq ...

  3. (转)Android技术积累:图片异步加载

    当在ListView或GridView中要加载很多图片时,很容易出现滑动时的卡顿现象,以及出现OOM导致FC(Force Close). 会出现卡顿现象主要是因为加载数据慢,要等数据加载完才能显示出来 ...

  4. Android中图片的异步加载

    转: 1.  为什么要异步加载图片 下载图片比较费时,先显示文字部分,让加载图片的过程在后台,以提升用户体验 2.  SoftReference的作用 栈内存—引用 堆内存—对象 Eg: Object ...

  5. Android异步加载图像(含线程池,缓存方法)

    研究了android从网络上异步加载图像: (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法. 在主线程中new 一个Han ...

  6. 转: Android异步加载图像小结

    转:http://blog.csdn.net/sgl870927/article/details/6285535 研究了android从网络上异步加载图像,现总结如下: (1)由于android UI ...

  7. Android图片异步加载之Android-Universal-Image-Loader

    将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就 ...

  8. [置顶] Android图片异步加载之Android-Universal-Image-Loader

    将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就 ...

  9. ListView与GridView异步加载图片

    原理很简单,主要是用到了回调方法,下面是异步加载图片的类 <span style="font-size:16px;">package com.xxx.xxx; impo ...

随机推荐

  1. HUST 1017(DLX)

    题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=65998#problem/A 题意:求01矩阵的精确覆盖. DLX学习资料:ht ...

  2. 图片预览插件 fancyBox

    今天给大家分享一款优秀的 jquery 弹出层展示插件 fancybox. 它除了能够展示图片之外,还能够展示 flash.iframe 内容.html 文本以及 ajax 调用.我们能够通过 css ...

  3. MySQL数据转移至MSSQL详解

    一.安装MySQL ODBC驱动 为MySQL安装Connector/ODBC驱动.在此需要注意的一点是Connector/ODBC驱动与MySQL Server的版本对应问题.   二.创建系统DS ...

  4. 用 Apache James 搭建邮件服务器来收发邮件实践(一)(转)

    Apache James 简称 James, 是 Java Apache Mail Enterprise Server的缩写.James 是100%基于Java的电子邮件服务器.它是一种独立的邮件服务 ...

  5. HDU 4998 Rotate

    题意: n次旋转  每次平面绕ai点旋转pi弧度  问  最后状态相当于初始状态绕A点旋转P弧度  A和P是多少 思路: 如果初始X点的最后状态为X'点  则圆心一定在X和X'连线的垂直平分线上  那 ...

  6. windows phone (17) ManipulationDelta事件

    原文:windows phone (17) ManipulationDelta事件 ManipulationDelta事件会是在触摸位置发生变化是引发,比如可以根据用户在触摸屏中移动的位置,图片发生相 ...

  7. PHP+Mysql————表单数据插入数据库及数据提取

    站点在进行新用户注冊时,都会将用户的注冊信息存入数据库中,须要的时候再进行提取.今天写了一个简单的实例. 主要完毕下面几点功能: (1)用户进行注冊,实现password反复确认,验证码校对功能. ( ...

  8. 懒人模式Singleton模式Meyers版本号

    直接看代码: /* Singleton模式保证:在一个程序,,一个类有且只有一个实例.并提供一个访问 它的全局访问点 在编程其中.很多情况下,需要确保有一类的一个实例 比如: windopws系统中仅 ...

  9. 解决SMARTFORMS 中table 控件单行跨页的问题

    在CX项目中,MM模块做了大量的的单据打印的工作,一个问题困扰了我好久,一直不能解决.当物料描述很长时,table控件在单元格中能自动换行,这样就有可能在换页处出现一行记录的一部分打在上一页,一部分记 ...

  10. MVC EF 增 删 改 查

    using System;using System.Collections.Generic;using System.Linq;using System.Web;//using System.Data ...