Android进阶图片处理内存优化系列之图片墙案例

Android内存优化案例之图片墙

说明:本文章是翻阅郭霖老师的博客之后有得而自行编写的,转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9526203

本篇文章的主要内容是说图片的内存缓存和从网络异步获取图片的过程

工程效果图如下:
Android进阶图片处理内存优化系列之图片墙案例_第1张图片

图片处理在Android中是挺头疼的一件事情,本篇播客只是为了自己对该模块的知识有一个更深层吃的认知,以后在该模块精通之后,再加以改进

总体图片墙的布局文件就是一个GridView里面平铺ImageView,内存缓存使用Lrucache的方式,在本地没有缓存,也没有对图片进行压缩化(后期再做这些操作)。

首先看代码,MainActivity的布局文件 activity_main.xml 如下


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    >

    <GridView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/gv_photowall"
        android:numColumns="auto_fit"
        android:columnWidth="100dp"
        android:stretchMode="columnWidth"
        android:verticalSpacing="5dp"
        android:gravity="center"
        >

    GridView>
LinearLayout>

代码中部分不常见的属性解释如下(百度也可以找到的)

android:numColumns="auto_fit"  意思是图片墙分成几列  :auto_fit自适应

android:stretchMode="columnWidth" 图片墙右边多出来或者少的区域适配方式:在列宽上进行加减来适配

同时给GridView适配的adapter的View的布局文件 view_photo.xml如下


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    >
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/iv_photo"

        />
RelativeLayout>

布局文件很简单,接下来就是解决图片源的问题,我这里使用的是本地的tomcat服务器,将图片保存在本地的tomcat服务器上,自己定义好链接来进行访问:

tomcat上的文件如图:
Android进阶图片处理内存优化系列之图片墙案例_第2张图片

图片的链接类如下:

package com.jishihuitong.photowalldemo;

/**
 * Created by hss on 2016/7/27.
 */
public class Images {
    public static final String[] IMAGE = new String[]{
            "http://10.0.2.2:8080/images/01.png",
            "http://10.0.2.2:8080/images/02.png",
            "http://10.0.2.2:8080/images/03.png",
            "http://10.0.2.2:8080/images/04.png",
            "http://10.0.2.2:8080/images/05.png",
            "http://10.0.2.2:8080/images/06.png",
            "http://10.0.2.2:8080/images/07.png",
            "http://10.0.2.2:8080/images/08.png",
            "http://10.0.2.2:8080/images/09.png",
            "http://10.0.2.2:8080/images/10.png",
            "http://10.0.2.2:8080/images/11.png",
            "http://10.0.2.2:8080/images/12.png",
            "http://10.0.2.2:8080/images/13.png",
            "http://10.0.2.2:8080/images/14.png",
            "http://10.0.2.2:8080/images/15.png",
            "http://10.0.2.2:8080/images/16.png",
            "http://10.0.2.2:8080/images/17.png",
            "http://10.0.2.2:8080/images/18.png",
            "http://10.0.2.2:8080/images/19.png",
            "http://10.0.2.2:8080/images/20.png",
            "http://10.0.2.2:8080/images/21.png",
            "http://10.0.2.2:8080/images/22.png",
            "http://10.0.2.2:8080/images/23.png",
            "http://10.0.2.2:8080/images/24.png",
            "http://10.0.2.2:8080/images/25.png",
            "http://10.0.2.2:8080/images/26.png",
            "http://10.0.2.2:8080/images/27.png",
            "http://10.0.2.2:8080/images/28.png",
            "http://10.0.2.2:8080/images/29.png",
            "http://10.0.2.2:8080/images/30.png",
            "http://10.0.2.2:8080/images/31.png",
            "http://10.0.2.2:8080/images/32.png",
            "http://10.0.2.2:8080/images/33.png",
            "http://10.0.2.2:8080/images/34.png",
            "http://10.0.2.2:8080/images/35.png",
            "http://10.0.2.2:8080/images/36.png",
            "http://10.0.2.2:8080/images/37.png",
            "http://10.0.2.2:8080/images/38.png",
            "http://10.0.2.2:8080/images/39.png",
            "http://10.0.2.2:8080/images/40.png",
            "http://10.0.2.2:8080/images/41.png",
            "http://10.0.2.2:8080/images/42.png",
            "http://10.0.2.2:8080/images/43.png",
            "http://10.0.2.2:8080/images/44.png",
            "http://10.0.2.2:8080/images/45.png",
            "http://10.0.2.2:8080/images/46.png",
            "http://10.0.2.2:8080/images/47.png",
            "http://10.0.2.2:8080/images/48.png",
            "http://10.0.2.2:8080/images/49.png",
            "http://10.0.2.2:8080/images/50.png",
            "http://10.0.2.2:8080/images/51.png",
            "http://10.0.2.2:8080/images/52.png",
            "http://10.0.2.2:8080/images/53.png",
            "http://10.0.2.2:8080/images/54.png",
            "http://10.0.2.2:8080/images/55.png",
            "http://10.0.2.2:8080/images/56.png",
            "http://10.0.2.2:8080/images/67.png",
            "http://10.0.2.2:8080/images/58.png",
            "http://10.0.2.2:8080/images/59.png",
            "http://10.0.2.2:8080/images/60.png",
            "http://10.0.2.2:8080/images/61.png",
            "http://10.0.2.2:8080/images/62.png",
            "http://10.0.2.2:8080/images/63.png",
            "http://10.0.2.2:8080/images/64.png",
            "http://10.0.2.2:8080/images/65.png",
            "http://10.0.2.2:8080/images/66.png",
            "http://10.0.2.2:8080/images/67.png",
            "http://10.0.2.2:8080/images/68.png",
            "http://10.0.2.2:8080/images/69.png",
            "http://10.0.2.2:8080/images/70.png",
            "http://10.0.2.2:8080/images/71.png",
            "http://10.0.2.2:8080/images/72.png",
            "http://10.0.2.2:8080/images/73.png",
            "http://10.0.2.2:8080/images/74.png",
            "http://10.0.2.2:8080/images/75.png",
            "http://10.0.2.2:8080/images/76.png",
            "http://10.0.2.2:8080/images/77.png",
            "http://10.0.2.2:8080/images/78.png"
    };
}

再来看MainActivity里面的代码:

package com.jishihuitong.photowalldemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.GridView;

public class MainActivity extends AppCompatActivity {

    /**
     * 图片墙GridView的适配器Adapter
     */
    private PhotoWallAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GridView photowall = (GridView) findViewById(R.id.gv_photowall);
        adapter = new PhotoWallAdapter(getApplicationContext(),0, Images.IMAGE,photowall);
        photowall.setAdapter(adapter);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //activity退出的时候取消所有正在下载的图片任务
        adapter.cancelAllTasks();
    }
}

主要的思路是photoWallAdapter继承ArrayAdapter重写里面的getView方法,将View加载进去,

1、在构造方法中把GridView对象传进来,便于引用
2、首先在Adapter的构造方法里面实例化Lrucache对象,
3、实例化任务集合,实例化任务BitmapWorkerTask参数是String类型的,返回值是Bitmap类型的
4、给GridView设置滚动监听,当第一次进入app的时候,把屏幕上出现的所有的图片都加载出来,当不是第一次进入的时候,判断是滚动状态还是停止状态,只有在停止状态的时候才去加载图片,滚动状态下取消所有的下载图片任务
5、下载完图片之后给保存到内存中

具体过程见代码:

MainActivity里面没有多少代码,最主要的 看这个PhotoWallAdapter

package com.jishihuitong.photowalldemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;

/**
 * Created by hss on 2016/7/27.
 */
public class PhotoWallAdapter extends ArrayAdapter<String> {
    /**
     * GridView实例
     */
    private GridView mGridView;
    /**
     * 图片内存缓存类
     */
    private LruCache mLruCache;
    /**
     * 任务管理集合
     */
    private HashSet mTaskCollection;

    private View view;
    /**
     * 第一个可以看见的条目下标
     */
    private int mFirstVisibleItem;
    /**
     * 可以看见的条目总数
     */
    private int mVisibleItemCount;
    /**
     * 是否是第一次进入
     */
    private boolean isFirstEnter = true;
    /**
     * 全局tag标记
     */
    private static final String TAG = "PhotoWallAdapter";
    public PhotoWallAdapter(Context context, int resource, String[] objects, GridView gridView) {
        super(context, resource, objects);
        this.mGridView = gridView;
        //获取到当前系统的最大内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        //我们使用最大内存的1/8作为本应用缓存图片的空间大小
        int cacheMemory = maxMemory / 8;
        //创建内存缓存对象,参数为定义好的缓存大小,
        mLruCache = new LruCache(cacheMemory) {
            //重写sizeOf()方法,默认返回图片的数量
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        //实例化任务集合
        mTaskCollection = new HashSet();
        //给gridView设置滚动监听
        mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
            //开始滚动的时候,取消下载任务,
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                //成员变量随着滚动而变化
                mFirstVisibleItem = firstVisibleItem;
                mVisibleItemCount = visibleItemCount;
                //如果第一次进来的时候,滑动状态下可以加载图片,
                if(isFirstEnter && visibleItemCount>0){
                    //加载可见范围内全部的图片
                    loadBitmaps(firstVisibleItem,visibleItemCount);
                    isFirstEnter = false;
                }
            }
            //处于不滚动状态的时候,再开始任务
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if(scrollState== AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
                    LogUtil.d(TAG,"mFirstVisibleItem = "+mFirstVisibleItem+"  mVisibleItemCount = "+mVisibleItemCount);
                    loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
                }else{
                    //取消所有的下载图片的任务
                    cancelAllTasks();
                }
            }
        });
    }

    /**
     * 取消所有下载图片的任务
     */
    public void cancelAllTasks() {
        if(mTaskCollection!=null){
            for (BitmapWorkerTask task: mTaskCollection) {
                task.cancel(false);
            }
        }
    }

    /**
     * 定义任务,执行任务,选择方法从本地和网络上加载图片
     * @param firstVisibleItem gridView中可以看见的第一个角标
     * @param visibleItemCount gridView中可以看见的所有图片的总数
     */
    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
        try{
            for (int i = firstVisibleItem; i //先看看内存里能不能获取到图片对象
                Bitmap bm = getBitmapFromCacheMemory(imageUrl);
                if(bm==null){
                    //如果为空。则开启线程去下载图片,
                    //定义任务
                    BitmapWorkerTask task = new BitmapWorkerTask();
                    //将任务对象存在集合中
                    mTaskCollection.add(task);
                    //执行任务
                    task.execute(imageUrl);
                }else{
                    //如果获取到的图片不为空,通过Tag找到ImageView控件,设置图片
                    ImageView photo = (ImageView) mGridView.findViewWithTag(imageUrl);
                    if(photo!=null){
                        photo.setImageBitmap(bm);
                    }

                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        String url = getItem(position);

        if(convertView==null){
            view = View.inflate(getContext(),R.layout.view_photo,null);
        }else{
            view = convertView;
        }

        ImageView iv_photo = (ImageView) view.findViewById(R.id.iv_photo);
        //给ImageView设置一个标记
        iv_photo.setTag(url);
        setImageView(url,iv_photo);
        return view;
    }

    /**
     * 给IamgeView设置图片
     * @param url 图片的URL
     * @param iv_photo ImageView控件
     */
    private void setImageView(String url, ImageView iv_photo) {
        Bitmap bitmap = getBitmapFromCacheMemory(url);
        if(bitmap!=null){
            iv_photo.setImageBitmap(bitmap);
        }else{
            iv_photo.setImageResource(R.mipmap.empty_photo);
        }
    }

    /**
     * 从内存中获取图片对象
     * @param imageUrl 图片地址
     * @return 图片的Bitmap对象
     */
    private Bitmap getBitmapFromCacheMemory(String imageUrl) {

        return mLruCache.get(imageUrl);
    }

    /**
     * 将图片以键值对的形式,添加进内存中,可以按照键找到对应的图片对象
     * @param imageUrl 键 以图片的URL作为键
     * @param bitmap 值 图片的实例化对象
     */
    private void addBitmapToCacheMemory(String imageUrl,Bitmap bitmap) {
        if(getBitmapFromCacheMemory(imageUrl)==null){
            mLruCache.put(imageUrl,bitmap);
        }
    }

    /**
     * 下载图片任务类
     */
    private class BitmapWorkerTask extends AsyncTask<String,Void,Bitmap> {


        private String imageUrl;
        //需要在后台进程中干的事情
        @Override
        protected Bitmap doInBackground(String... params) {
            //从参数中获取到传进来的图片URL
            imageUrl = params[0];
            //从网络上下载图片
            Bitmap bitmap = downLoadBitmap(params[0]);
            //如果下载到的图片不为空,把图片保存进内存中
            if(bitmap!=null){
                addBitmapToCacheMemory(params[0],bitmap);
            }
            return bitmap;
        }
        //该方法在上面的方法,DoInBackground之后运行,方法里面的参数就是上面的方法的返回值
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView photo = (ImageView) mGridView.findViewWithTag(imageUrl);
            if(bitmap!=null&&photo!=null){
                photo.setImageBitmap(bitmap);
            }
            //完了记得把任务清除掉
            mTaskCollection.remove(this);
        }

        /**
         * 从网络上下载图片,在后台进程中运行
         * @param imageUrl
         * @return
         */
        private Bitmap downLoadBitmap(String imageUrl) {
            Bitmap bitmap = null;
            HttpURLConnection conn = null;
            try{
                URL url = new URL(imageUrl);
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5*1000);
                conn.setReadTimeout(10 * 1000);
                conn.setDoInput(true);
                conn.setDoOutput(true);
                bitmap = BitmapFactory.decodeStream(conn.getInputStream());
            }catch(Exception e){
                e.printStackTrace();
            }finally {
                if(conn!=null){
                    conn.disconnect();
                }
            }
            return bitmap;
        }

    }

}

运行的时候别忘了在清单文件中添加连接网络的权限

下载源码

你可能感兴趣的:(Android基础)