图片三级缓存(一)

本文介绍了Android中图片的三级缓存机制,包括内存缓存LruCache、磁盘缓存DiskLruCache以及从网络直接下载。在MainActivity中使用AsyncTask获取图片URL,NewsAdapter作为列表适配器,实现OnScrollListener接口,确保滑动时预加载图片,避免卡顿。同时,文章探讨了可能的内存泄漏问题,并提供了源码下载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请注明出处:https://blog.youkuaiyun.com/michael1112/article/details/81265683

图片三级缓存的简单应用:

这是一个简单的demo。图片从http://www.imooc.com/api/teacher?type=4&num=30 这里获取。通过三种方式来加载显示图片(LruCache,DiskLruCache,和多线程从网上直接下载)。

-- 应用入口:MainActivity.java

这个类用AnsyTask来异步获取图片的url 列表。(这是一个非静态内部类,是否会引起内存泄漏呢? 同时在GetJsonTask中使用了mainContext,它的声明如下,那么是否也会引起内存泄漏呢? 这两个问题将在一篇文章中作出解释。)

private Context mainContext = MainActivity.this;
/**
 * 自定义异步任务解析json数据
 */
class GetJsonTask extends AsyncTask<String,Void,List<NewsBean>>{

    @Override
    protected List<NewsBean> doInBackground(String... params) {
        return GetJsonUtil.getJson(params[0]);
    }

    @Override
    protected void onPostExecute(List<NewsBean> newsBeen) {
        super.onPostExecute(newsBeen);
        NewsAdapter newsAdapter = new NewsAdapter(mainContext, newsBeen,mainListView);
        mainListView.setAdapter(newsAdapter);
    }
}

-- NewsAdapter.java 

absListView 是ListView的父类. 

    

    NewsAdapter 要实现AbsListView 的接口OnScrollListener。这里的作用是当listview在滑动时,只显示预加载的图片。当滑动停止时,从网上download或从内存或从硬盘缓存中加载图片。这样做的目的是为了避免在滑动时出现卡顿现象。

 

onScroll的后三个参数的意义:

      firstVisibleItem--》当第一次进入listview时这个值是0.当向上滑一个item时这个值是1. 明白其意思了吧。

      visibleItemCount--》当前手机屏幕课件的cell。

       totalItemCount--》一次加载的item的数量。例如totalItemCount=30,当前的visibleItemCount=12

public abstract void onScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)

Added in API level 1

Callback method to be invoked when the list or grid has been scrolled. This will be called after the scroll has completed

Parameters

viewThe view whose scroll state is being reported
firstVisibleItemthe index of the first visible cell (ignore if visibleItemCount == 0)
visibleItemCountthe number of visible cells
totalItemCNewsAdapter ountthe number of items in the list adaptor

NewsAdapter .java:  第一次进入listview的时候先调用getView,然后根据url去读取硬盘缓存中的图片。这是缓存中是没有图片的,所以设置为默认图片。然后系统调用onScroll,应为是第一次进入所以调用DiskCacheUtil类的loadImages。然后用NewsAsyncTask异步获取图片。将获取的图片写入缓存然后读取显示到界面上。

                                  当滑动屏幕时,onScrollStateChanged被调用,详细流程和上面的相似,就留给小伙伴们自个儿分析了。下面是详细代码。

 

package com.jdjz.lrucachedemo.adapter;

import android.content.Context;
import android.util.Log;
import android.util.LruCache;
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 android.widget.TextView;

import com.jdjz.lrucachedemo.R;
import com.jdjz.lrucachedemo.beans.NewsBean;
import com.jdjz.lrucachedemo.utils.DiskCacheUtil;
import com.jdjz.lrucachedemo.utils.LruCacheUtil;
import com.jdjz.lrucachedemo.utils.ThreadUtil;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by happen on 2018-07-19.
 */
//AbsListView 是ListView的父类
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {

    private Context context;
    private List<NewsBean> list;
    private LruCacheUtil lruCacheUtil;
    private DiskCacheUtil mDiskCacheUtil;
    private ThreadUtil mThreadUtil;
    private int mStart,mEnd;  //滑动的起始位置
    public static String[] urls; //用来保存当前获取到的所有图片的Url地址

    //是否是第一次进入  (优化AtomicBoolean)
    private AtomicBoolean mFirstIn = new AtomicBoolean();

    private int type = 1;//1表示disklrucache,2表示lrucache,3表示ThreadUtil


    public NewsAdapter(Context context,List<NewsBean> list,ListView lv){
        this.context = context;
        this.list = list;
        lruCacheUtil = new LruCacheUtil(lv);
        mDiskCacheUtil = new DiskCacheUtil(context,lv);
        mThreadUtil = new ThreadUtil();
        //存入url地址
        urls = new String[list.size()];
        for(int i=0; i<list.size();i++){
            urls[i] = list.get(i).newsIconUrl;
        }
        mFirstIn.set(true);
        //注册监听事件
        lv.setOnScrollListener(this);
    }

    /**
     * 滑动状态改变的时候才会去调用此方法
     *
     * @param view        滚动的View
     * @param scrollState 滚动的状态
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

        if(scrollState == SCROLL_STATE_IDLE){
            Log.i("jdjz","onScrollStateChanged SCROLL_STATE_IDLE ");
            //加载可预见
            try{
                switch (type){
                    case 1:
                        mDiskCacheUtil.loadImages(mStart,mEnd);
                        break;
                    case 2:
                        lruCacheUtil.loadImages(mStart,mEnd);
                        break;
                    default:
                        Log.i("jdjz","onScrollStateChanged SCROLL_STATE_IDLE不是前两种方式(disklrucache and lrucache)加载图片");
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }else{
            //停止加载任务
            mDiskCacheUtil.cancelAllTask();
        }
    }

    /**
     * 滑动过程中 一直会调用此方法
     *
     * @param view             滚动的View
     * @param firstVisibleItem 第一个可见的item
     * @param visibleItemCount 可见的item的长度
     * @param totalItemCount   总共item的个数
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = firstVisibleItem + visibleItemCount;
        //如果是第一次进入 且可见item大于0 预加载
        if (mFirstIn.get() && visibleItemCount > 0) {
            try {
                Log.i("jdjz","onScroll 如果是第一次进入 且可见item大于0 预加载 ");
                switch(type){
                    case 1:
                        mDiskCacheUtil.loadImages(mStart, mEnd);
                        break;
                    case 2:
                        lruCacheUtil.loadImages(mStart,mEnd);
                        break;
                    default:
                        Log.i("jdjz","第一次进入: 不是前两种方式(disklrucache and lrucache)加载图片") ;
                        break;
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
            mFirstIn.set(false);
        }
    }

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

    @Override
    public Object getItem(int i) {
        Log.i("tchl","getItem:"+i);
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        Log.i("tchl","getItemId:"+i);
        return i;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.i("jdjz","getView");
        ViewHolder viewHolder;
        if(convertView == null){
            convertView = View.inflate(context, R.layout.item_news,null);
        }
        //得到一个ViewHolder
        viewHolder = ViewHolder.getViewHolder(convertView);
        //先加载默认图片 防止有的没有图片
        viewHolder.iconImage.setImageResource(R.mipmap.ic_launcher);
        String iconUrl = list.get(position).newsIconUrl;
        //当前位置的ImageView与URL中的图片绑定
        viewHolder.iconImage.setTag(iconUrl);

        Log.i("jdjz","iconUrl:"+iconUrl);
        switch (type){
            case 1:
                //第1种方式 通过异步任务方式设置 且利用DiskLruCache存储到磁盘缓存中
                try {
                    mDiskCacheUtil.showImageByAsyncTask(viewHolder.iconImage, iconUrl);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            case 2:

                //第2种方式 通过异步任务方式设置 且利用LruCache存储到内存缓存中
                lruCacheUtil.showImageByAsyncTask(viewHolder.iconImage, iconUrl);
                break;
            case 3:
                //第3种方式 通过子线程设置
                mThreadUtil.showImageByThread(viewHolder.iconImage, iconUrl);
                break;
            default:
                Log.i("jdjz","getView  非前3种方式显示图片");
                break;
        }

        viewHolder.titleText.setText(list.get(position).newsTitle);
        viewHolder.contentText.setText(list.get(position).newsContent);

        return convertView;
    }


    static class ViewHolder {
        ImageView iconImage;
        TextView titleText;
        TextView contentText;

        // 构造函数中就初始化View
        public ViewHolder(View convertView) {
            iconImage = (ImageView) convertView.findViewById(R.id.iv_icon);
            titleText = (TextView) convertView.findViewById(R.id.tv_title);
            contentText = (TextView) convertView.findViewById(R.id.tv_content);
        }

        // 得到一个ViewHolder
        public static ViewHolder getViewHolder(View convertView) {
            ViewHolder viewHolder = (ViewHolder) convertView.getTag();
            if (viewHolder == null) {
                viewHolder = new ViewHolder(convertView);
                convertView.setTag(viewHolder);
            }
            return viewHolder;
        }
    }
}

===============================================================================

private int type = 1;//1表示disklrucache,2表示lrucache,3表示ThreadUtil

上面分析是type=1的情况,也就是disklrucache的情况。

当type=2 时,也就是lrucache的情况,图片加载流程和disklrucache一样。这里就不做分析了。

当type=3时,就是开启一个线程,去网络上下载图片。

 

===========================
源码下载

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值