加载图片,图片如果达到一定的上限,如果没有一种合理的机制对图片进行释放必然会引起程序的崩溃.

为了避免这种情况,我们可以使用Android中LruCache来缓存下载的图片,防止程序出现OOM.
 

 
 
打开activity_main.xml作为程序的主布局,加入如下代码:
 
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
    <GridView 
        android:id="@+id/photo_wall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:columnWidth="90dip"
        android:stretchMode="columnWidth"
        android:numColumns="auto_fit"
        android:verticalSpacing="10dip"
        android:gravity="center"
        ></GridView>

</LinearLayout>

 
 
    接着我们定义GridView中每一个子View的布局,新建一个photo_layout.xml布局,加入如下代码:
 
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
 
    <ImageView 
        android:id="@+id/photo"
        android:layout_width="90dip"
        android:layout_height="90dip"
        android:src="@drawable/empty_photo"
        android:layout_centerInParent="true"
        />
 

</RelativeLayout>

在每一个子View中使用了ImageView来显示一张图片.
接下来就是适配器
    **
 * GridView的适配器,负责异步从网络上下载图片展示在照片墙上。
 * 
 * @author guolin
 */
public class PhotoWallAdapter extends ArrayAdapter<String> implements OnScrollListener {
 
    /**
     * 记录所有正在下载或等待下载的任务。
     */
    private Set<BitmapWorkerTask> taskCollection;
 
    /**
     * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
     */
    private LruCache<String, Bitmap> mMemoryCache;
 
    /**
     * GridView的实例
     */
    private GridView mPhotoWall;
 
    /**
     * 第一张可见图片的下标
     */
    private int mFirstVisibleItem;
 
    /**
     * 一屏有多少张图片可见
     */
    private int mVisibleItemCount;
 
    /**
     * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
     */
    private boolean isFirstEnter = true;
 
    public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,
            GridView photoWall) {
        super(context, textViewResourceId, objects);
        mPhotoWall = photoWall;
        taskCollection = new HashSet<BitmapWorkerTask>();
        // 获取应用程序最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        Log.e("info","可用缓存..."+cacheSize);
        // 设置图片缓存大小为程序最大可用内存的1/8
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        mPhotoWall.setOnScrollListener(this);
    }
 
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final String url = getItem(position);
        View view;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);
        } else {
            view = convertView;
        }
        final ImageView photo = (ImageView) view.findViewById(R.id.photo);
        // 给ImageView设置一个Tag,保证异步加载图片时不会乱序
        photo.setTag(url);
        setImageView(url, photo);
        return view;
    }
 
    /**
     * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。如果LruCache中没有该图片的缓存,
     * 就给ImageView设置一张默认图片。
     * 
     * @param imageUrl
     *            图片的URL地址,用于作为LruCache的键。
     * @param imageView
     *            用于显示图片的控件。
     */
    private void setImageView(String imageUrl, ImageView imageView) {
        Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
        if (bitmap != null) {
            Log.e("info","从缓存中取....");
            imageView.setImageBitmap(bitmap);
        } else {
            Log.e("info","没有图片....");
            imageView.setImageResource(R.drawable.empty_photo);
        }
    }
 
    /**
     * 将一张图片存储到LruCache中。
     * 
     * @param key
     *            LruCache的键,这里传入图片的URL地址。
     * @param bitmap
     *            LruCache的键,这里传入从网络上下载的Bitmap对象。
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }
 
    /**
     * 从LruCache中获取一张图片,如果不存在就返回null。
     * 
     * @param key
     *            LruCache的键,这里传入图片的URL地址。
     * @return 对应传入键的Bitmap对象,或者null。
     */
    public Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }
 
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
        if (scrollState == SCROLL_STATE_IDLE) {
            loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
        } else {
            cancelAllTasks();
        }
    }
 
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
            int totalItemCount) {
        mFirstVisibleItem = firstVisibleItem;
        mVisibleItemCount = visibleItemCount;
        // 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,
        // 因此在这里为首次进入程序开启下载任务。
        if (isFirstEnter && visibleItemCount > 0) {
            loadBitmaps(firstVisibleItem, visibleItemCount);
            isFirstEnter = false;
        }
    }
 
    /**
     * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
     * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
     * 
     * @param firstVisibleItem
     *            第一个可见的ImageView的下标
     * @param visibleItemCount
     *            屏幕中总共可见的元素数
     */
    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
        try {
            for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
                String imageUrl = Images.imageThumbUrls[i];
                Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
                if (bitmap == null) {
                    BitmapWorkerTask task = new BitmapWorkerTask();
                    taskCollection.add(task);
                    task.execute(imageUrl);
                } else {
                    ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
                    if (imageView != null && bitmap != null) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 取消所有正在下载或等待下载的任务。
     */
    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }
 
    /**
     * 异步下载图片的任务。
     * 
     * @author guolin
     */
    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
 
        /**
         * 图片的URL地址
         */
        private String imageUrl;
 
        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            // 在后台开始下载图片
            Bitmap bitmap = downloadBitmap(params[0]);
            if (bitmap != null) {
                // 图片下载完成后缓存到LrcCache中
                addBitmapToMemoryCache(params[0], bitmap);
            }
            return bitmap;
        }
 
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
            ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            taskCollection.remove(this);
        }
 
        /**
         * 建立HTTP请求,并获取Bitmap对象。
         * 
         * @param imageUrl
         *            图片的URL地址
         * @return 解析后的Bitmap对象
         */
        private Bitmap downloadBitmap(String imageUrl) {
            Bitmap bitmap = null;
            HttpURLConnection con = null;
            try {
                URL url = new URL(imageUrl);
                con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(5 * 1000);
                con.setReadTimeout(10 * 1000);
                con.setDoInput(true);
                con.setDoOutput(true);
                bitmap = BitmapFactory.decodeStream(con.getInputStream());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (con != null) {
                    con.disconnect();
                }
            }
            return bitmap;
        }
 
    }
 
}
MainActivity:
    **
 * 照片墙主活动,使用GridView展示照片墙。
 * 
 * @author guolin
 */
public class MainActivity extends Activity {
 
    /**
     * 用于展示照片墙的GridView
     */
    private GridView mPhotoWall;
 
    /**
     * GridView的适配器
     */
    private PhotoWallAdapter adapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPhotoWall = (GridView) findViewById(R.id.photo_wall);
        adapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls, mPhotoWall);
        mPhotoWall.setAdapter(adapter);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出程序时结束所有的下载任务
        adapter.cancelAllTasks();
    }
 
}
MainActivity中的代码非常简单,没什么需要说明的了,在Activity被销毁时取消掉了所有的下载任务,避免程序在后台耗费流量。另外由于我们使用了网络功能,别忘了在AndroidManifest.xml中加入网络权限的声明。
 
 

android 使用LruCache缓存网络图片的更多相关文章

  1. Android 使用 LruCache 缓存图片

    在你应用程序的 UI 界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来.在很多情况下,(比如使用 ListView, GridView 或者 ViewP ...

  2. Android使用 LruCache 缓存图片

    摘要:在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来. 使用图片缓存技术 在 你应用程序的UI界面加载一张图片是一件很简单的事情,但 ...

  3. Android中用双缓存技术,加载网络图片

    最近在学校参加一个比赛,写的一个Android应用,里面要加载大量的网络图片,可是用传统的方法图片一多就会造成程序出现内存溢出而崩溃.因为自己也在学习中,所以看了很多博客和视频,然后参照这些大神的写源 ...

  4. Android内存管理(3)缓存不要用SoftReference, 用android.util.LruCache

    A reference that is cleared when its referent is not strongly reachable and there is memory pressure ...

  5. picasso_强大的Android图片下载缓存库

    tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...

  6. 利用LruCache载入网络图片实现图片瀑布流效果(改进版)

    PS: 2015年1月20日21:37:27 关于LoadImageAsyncTask和checkAllImageViewVisibility可能有点小bug 改动后的代码请參见升级版本号的代码 ht ...

  7. android.util.LruCache类

    值得一提的另一个类是android.util.LruCache<K, V>,这个类是Android 3.1(代号 Honeycomb MR1)引入的,可以在创建时定义缓存的最大长度.另外, ...

  8. android之LruCache源代码解析

    移动设备开发中,因为移动设备(手机等)的内存有限,所以使用有效的缓存技术是必要的.android提供来一个缓存工具类LruCache,开发中我们会经经常使用到,以下来他是怎样实现的. 在package ...

  9. 让App中加入LruCache缓存,轻松解决图片过多造成的OOM

    上次有过电话面试中问到Android中的缓存策略,当时模糊不清的回答,现在好好理一下吧. Android中一般情况下采取的缓存策略是使用二级缓存,即内存缓存+硬盘缓存->LruCache+Dis ...

随机推荐

  1. 缓冲区溢出实战教程系列(三):利用OllyDbg了解程序运行机制

    想要进行缓冲区溢出的分析与利用,当然就要懂得程序运行的机制.今天我们就用动态分析神器ollydbg来了解一下在windows下程序是如何运行的. 戳这里看之前发布的文章: 缓冲区溢出实战教程系列(一) ...

  2. 多重网格方法(Multigridmethod)

    原文链接 多重网格方法是解微分方程的方法.这个方法的好处是在利用迭代法收敛结果的时候速度特别快.并且,不管是否对称,是否线性都无所谓.它的值要思想是在粗糙结果和精细结果之间插值. 前面介绍了Gauss ...

  3. 成员变量和成员函数前加static的作用?

    成员变量和成员函数前加static的作用?答:它们被称为常成员变量和常成员函数,又称为类成员变量和类成员函数.分别用来反映类的状态.比如类成员变量可以用来统计类实例的数量,类成员函数负责这种统计的动作 ...

  4. CSU 1216异或最大值 (0-1 trie树)

    Description 给定一些数,求这些数中两个数的异或值最大的那个值 Input 多组数据.第一行为数字个数n,1 <= n <= 10 ^ 5.接下来n行每行一个32位有符号非负整数 ...

  5. 【ODT】cf896C - Willem, Chtholly and Seniorious

    仿佛没用过std::set Seniorious has n pieces of talisman. Willem puts them in a line, the i-th of which is ...

  6. 【例题收藏】◇例题·I◇ Snuke's Subway Trip

    ◇例题·I◇ Snuke's Subway Trip 题目来源:Atcoder Regular 061 E题(beta版) +传送门+ 一.解析 (1)最短路实现 由于在同一家公司的铁路上移动是不花费 ...

  7. IE6兼容png图片

    <!--[if IE 6]> <script src="/js/DD_belatedPNG.js"></script> <script&g ...

  8. React路由-进阶篇

    路由进阶 1.多级路由,和之前的思想一样,在子路由里面继续写Route,继续挂载组件,就可以实现多级路由 比如这样:class Food extends Component{ render() { r ...

  9. React路由-基础篇

    React-Router-DOM ReactRouter网址, 安装 -npmjs找到react-router-dom -yarn add react-router-dom 基本使用方法 1.创建一个 ...

  10. 第1章 HTML5 概述

    第 1 章  HTML5 概述 学习要点: 1.HTML5 的历史 2.HTML5 的功能 3.HTML5 的特点 4.课程学习问题 HTML5 是继 HTML4.01 和 XHTML1.0 之后的超 ...