您的位置:首页 > 其它

使用LRUCACHE解决加载本地大量图片卡顿及OOM问题

2017-05-10 15:12 531 查看
今天测试了一下之前写的一个从本地加载图片并显示的功能,因为以前测试的时候一次性显示的图片比较少,所以就没发现有卡顿以及进入界面比较慢的问题,但是今天想看一下传入很多图片之后的界面效果,所以就加载了很多图片,传入图片之后一开始进入界面,发现出错,出错运用是因为我往Adapter里面传的参数有一项是ArrayList<Bitmap>,因为一次性传入太多的BitMap,所以越界了,导致程序崩溃,然后改成传路径,这个问题得到解决,但是又发现进入界面的时候特别慢,而且往下滑动的时候也是特别的卡顿,我在重写Adapter的时候用到了优化,但是还是特别卡顿,然后想到我每加载一张图片都是去根据路径去读文件,所以导致卡顿现象特别严重,而且进入的时候也是因为每张图片都去读一下文件,所以进入特别慢,有时候甚至出现OOM的问题。这时突然间感觉自己以前写的时候脑子估计是抽风了,竟然犯了这么弱智的问题,每次读文件是很耗时的,所以程序不崩溃才怪。因此感觉使用缓存的技术来修改自己的程序。因为这次又用到了LruCache技术,所以顺便写一下使用过程吧。
其实方法跟网上大部分讲解的代码都类似,只是把从网络下载图片部分改成从本地加载,而且因为这个程序的特殊性,所以加载的图片不能有相同路径的图片,因为所有的键值对设置我都是以图片的路径做的,所以如果路径相同的话有可能会使图片显示不出来。
public class WineListAdapter extends BaseAdapter implements AbsListView.OnScrollListener {

// 从本地加载图片的线程集合
private List<ImageDownloadTask> mDownloadTaskList;
private LruCache<String, Bitmap> mLruCache;
// 引用外部的变量
private WeakReference<GridView> mGridView;
private WeakReference<List<String>> urls;
private WeakReference<List<String>> names;
private WeakReference<Context> mContext;

// 可见项的第一项的index
private int mFirstVisibleIndex;

// 可见项的个数
private int mVisibleItemCount;

// 是不是第一次打开Activity
private boolean isFirstOpen = true;

public WineListAdapter(Context context, GridView mGridView, List<String> urls, List<String> names) {
this.mContext = new WeakReference<Context>(context);
this.urls = new WeakReference<List<String>>(urls);
this.names = new WeakReference<List<String>>(names);
this.mGridView = new WeakReference<GridView>(mGridView);
this.mGridView.get().setOnScrollListener(this);
mDownloadTaskList = new ArrayList<>();
initCache();
}

private void initCache() {
// 获取应用的max heap size
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Android官方教学文档推荐LruCache的size为heap size的1/8
int cacheSize = maxMemory / 8;

mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
if (bitmap != null) {
return bitmap.getByteCount() / 1024;
}
return 0;
}
};
}

@Override
public int getCount() {
return urls.get().size();
}

@Override
public Object getItem(int position) {
return urls.get().get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext.get()).inflate(R.layout.winelist_item, null);
viewHolder = new ViewHolder();
viewHolder.im = (ImageView) convertView.findViewById(R.id.iv_winelist);
viewHolder.tv = (TextView) convertView.findViewById(R.id.tv_winelist);
convertView.setTag(viewHolder);
} else {
//view = convertView;
viewHolder = (ViewHolder) convertView.getTag();
}

String url = urls.get().get(position);
viewHolder.im.setTag(url);
viewHolder.tv.setText(names.get().get(position));

if (!viewHolder.im.getTag().equals(url)) {
showImageView(viewHolder.im, url);
}

return convertView;
}

/**
* 给ImageView设置Bitmap
*/
private void showImageView(ImageView imageView, String url) {

// 先从cache中找bitmap缓存
Bitmap bitmap = get(url);

if (url.equals("1")) {
imageView.setImageBitmap(BitmapFactory.decodeResource(mContext.get().getResources(), R.mipmap.ic_launcher));
} else if (bitmap != null) {
// 如果缓存命中
imageView.setImageBitmap(bitmap);
} else {
// 如果cache miss
imageView.setImageBitmap(BitmapFactory.decodeResource(mContext.get().getResources(), R.mipmap.ic_launcher));
}
}

/**
* 将Bitmap put 到 cache中
*/
private void put(String key, Bitmap bitmap) {

if (get(key) == null) {
mLruCache.put(key, bitmap);
}
}

/**
* 在Cache中查找bitmap,如果miss则返回null
*/
private Bitmap get(String key) {
return mLruCache.get(key);
}

/**
* 从本地加载图片
*/
private Bitmap loadBitmap(String urlStr) {
Bitmap bitmap = null;
File file = new File(urlStr);
if (!file.exists()) {
//Toast.makeText(mContext.get(), "文件损坏,请查看", Toast.LENGTH_SHORT).show();
Log.i("---LK---", "no file");
bitmap = BitmapFactory.decodeResource(mContext.get().getResources(), R.mipmap.ic_launcher);
} else {
bitmap = BitmapFactory.decodeFile(urlStr);
}
return bitmap;
}

/**
* 加载可见项的图片
*/
private void loadVisibleBitmap(int mFirstVisibleItem, int mVisibleItemCount) {

for (int i = mFirstVisibleItem; i < mFirstVisibleItem + mVisibleItemCount; i++) {
final String url = urls.get().get(i);
Bitmap bitmap = get(url);
ImageView mImageView;
if (bitmap != null) {
//缓存中存在该图片的话就设置给ImageView
mImageView = (ImageView) mGridView.get().findViewWithTag(url);
if (mImageView != null) {
mImageView.setImageBitmap(bitmap);
}
} else {
//不存在的话就开启一个异步线程去下载
ImageDownloadTask task = new ImageDownloadTask(this);
mDownloadTaskList.add(task);
task.execute(url);
}
}
}

/**
* 取消所有的下载任务
*/
public void cancelAllTask() {

if (mDownloadTaskList != null) {
for (int i = 0; i < mDownloadTaskList.size(); i++) {
mDownloadTaskList.get(i).cancel(true);
}
}
}

/**
* 从本地加载图片的异步task
*/
static class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {

private String url;
private WeakReference<WineListAdapter> photoAdapter;

public ImageDownloadTask(WineListAdapter photoAdapter) {
this.photoAdapter = new WeakReference<WineListAdapter>(photoAdapter);
}

@Override
protected Bitmap doInBackground(String... params) {
//在后台开始下载图片
url = params[0];
Bitmap bitmap = photoAdapter.get().loadBitmap(url);
if (bitmap != null) {
//把下载好的图片放入LruCache中
String key = url;
photoAdapter.get().put(key, bitmap);
}
return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//把下载好的图片显示出来
ImageView mImageView = (ImageView) photoAdapter.get().mGridView.get().findViewWithTag(url);
if (mImageView != null && bitmap != null) {
mImageView.setImageBitmap(bitmap);
photoAdapter.get().mDownloadTaskList.remove(this);//把下载好的任务移除
}
}
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//GridView停止滑动时,去加载可见项的图片
if (scrollState == SCROLL_STATE_IDLE) {
loadVisibleBitmap(mFirstVisibleIndex, mVisibleItemCount);
} else {
//GridView开始滑动时,取消所有加载任务
cancelAllTask();
}

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mFirstVisibleIndex = firstVisibleItem;
mVisibleItemCount = visibleItemCount;

// 第一次打开,加载可见项
if (isFirstOpen && visibleItemCount > 0) {
loadVisibleBitmap(mFirstVisibleIndex, mVisibleItemCount);
isFirstOpen = false;
}

}

private class ViewHolder {
public ImageView im;
public TextView tv;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: