彻底掌握如何有效处理高清大图:Android-Universal-Image-Loader框架解析(一)基本使用

  1. 为什么要写这章

在最近的项目中,有一个高清大图片处理任务,自己尝试进行了一些封装,虽然使用起来还算顺利,但中间还是遇到一些槽点,如不当调用bitmap的recycle方法导致app crash,图片缓存策略设计不够合理,框架只能适应用于较少的任务…顾方才有了此系列

  1. Android-Universal-Image-Loader(Android通过图片加载框架)基本使用

实现效果如下:
  在gridview中显示N张下载的图片,下载的图片会被三级缓存,同时每一张照片淡入显示
在这里插入图片描述

代码部分

package com.cn.china.imageloader;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.display.CircleBitmapDisplayer;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer;
import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;

public class ImageLoaderActivity extends Activity {

    private DisplayImageOptions options;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_loader);
        initImageLoader(this);
        configPic();
        GridView gridView = (GridView) findViewById(R.id.gridview);
        gridView.setAdapter(new GridAdapter());
    }

    /*
     * 初始化ImageLoader
     */
    private void initImageLoader(Context context) {
        ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(context);
        //设置线程有限级,默认为Thread.NORM_PRIORITY - 2,这行代码我们可以不写,这里只是做一个学习用,线程的优先级会在构造ImageLoaderConfiguration的时候,自动设置
        config.threadPriority(Thread.NORM_PRIORITY);
        //不允许缓存同一张图片的多个尺寸,调用之后,会已图片uil为key,如果缓存中有相同的图片,则会将原先的bitmap进行覆盖
        config.denyCacheImageMultipleSizesInMemory();
        //图片缓存的时候总需要一个名字吧,这里以MD5的方式加上图片URL来生成一个缓存文件来表示一个图片
        config.diskCacheFileNameGenerator(new Md5FileNameGenerator());
        //设置硬盘缓存的最大存储空间50M
        config.diskCacheSize(50 * 1024 * 1024);
        //设置加载和显示图片的队列类型,默认为FIFO,即先进先出的队列;此处设置为LIFO表示后进先出
        //当加载、显示、和缓存图片的队列满的时候,其他待进行的任务将按照FIFO或者LIFO的策略进行排队
        config.tasksProcessingOrder(QueueProcessingType.LIFO);
        //控制log的输出,不调用则不输出相关日志
        config.writeDebugLogs();
        //初始化ImageLoader
        ImageLoader.getInstance().init(config.build());
    }

    private void configPic() {
        options = new DisplayImageOptions.Builder()
                //图片在刚加载的时候图片
                .showImageOnLoading(R.drawable.ic_stub)
                //url为空时展示的图片
                .showImageForEmptyUri(R.drawable.ic_empty)
                //图片加载失败时的图片
                .showImageOnFail(R.drawable.ic_error)
                //缓存到内存
                .cacheInMemory(true)
                //缓存到磁盘
                .cacheOnDisk(true)
                //考虑JPEG图像EXIF参数(旋转,翻转)
                .considerExifParams(true)
                //设置图片显示方式,此处为淡入模式
                .displayer(new FadeInBitmapDisplayer(1500))
                //设置图片的解码模式为RGB_565
                .bitmapConfig(Bitmap.Config.RGB_565).build();
    }

    class GridAdapter extends BaseAdapter {
        private LayoutInflater inflater;

        GridAdapter() {
            inflater = LayoutInflater.from(ImageLoaderActivity.this);
        }

        @Override
        public int getCount() {
            return Constants.imageThumbUrls.length;
        }

        @Override
        public Object getItem(int position) {
            return Constants.imageThumbUrls[position];
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final ViewHolder holder;
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.item_grid_image,parent, false);
                holder = new ViewHolder();
                holder.imageView = (ImageView) convertView.findViewById(R.id.image);
                holder.progressBar = (ProgressBar) convertView.findViewById(R.id.progress);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            String url = Constants.imageThumbUrls[position];
            ImageLoader.getInstance().displayImage(url,holder.imageView, options,
                    new SimpleImageLoadingListener() {
                        @Override
                        public void onLoadingStarted(String imageUri, View view) {
                            holder.progressBar.setProgress(0);
                            holder.progressBar.setVisibility(View.VISIBLE);
                        }

                        @Override
                        public void onLoadingFailed(String imageUri, View view,FailReason failReason) {
                            holder.progressBar.setVisibility(View.GONE);
                        }

                        @Override
                        public void onLoadingComplete(String imageUri,View view, Bitmap loadedImage) {
                            holder.progressBar.setVisibility(View.GONE);
                        }
                    }, new ImageLoadingProgressListener() {
                        @Override
                        public void onProgressUpdate(String imageUri, View view, int current, int total) {
                            holder.progressBar.setProgress(Math.round(100.0f * current / total));
                        }
                    });
            return convertView;
        }
    }

    private static class ViewHolder {
        ImageView imageView;
        ProgressBar progressBar;
    }
}

  第44行,initImageLoader方法主要作用是完成ImageLoader的配置和初始化
  第47行,线程池里面待执行的任务以线程的方式存在,而线程的优先级会影响线程的执行先后顺序,在Android-Universal-Image-Loader(后面简称UIL)中线程的优先级默认为Thread.NORM_PRIORITY - 2;新建一个线程默认的级别是thread.norm_priority,线程的优先级从1~10,其中1为thread.min_priority,5为thread.norm_priority,10为thread.max_priority;其实我们大可不必进行这样的设置,因为这些现在cpu的性能已经比较强悍,先后执行的任务之间差别很小很小
  第48行,当我们下载一张图片之后,如果这个图片的尺寸有所改变,那么通过denyCacheImageMultipleSizesInMemory我们就可以以覆盖的形式将之前保存的图片给覆盖掉,以保证缓存中只有这个图片的唯一副本
  第56行,通过tasksProcessingOrder来设置当队列开始排队的时候,后续的任务的插入方式,默认为FIFO,即先进先出的队列;tasksProcessingOrder会影响执行加载线程池和缓存线程池中队列的创建,如果设置了tasksProcessingOrder方法为FIFO,则这两个线程池创建的时候采用的队列是LinkedBlockingQueue,反之这两个线程池创建的时候采用的队列是LIFOLinkedBlockingDeque,其中LIFOLinkedBlockingDeque是UIL框架中根据LIFO算法自定义的一个队列
  第63行的configPic方法的作用是针对图片进行一些配置
  第76行,通过调用considerExifParams方法,我们可以获取到图片的exif信息,
补充:exif信息是什么
Exif就是用来记录拍摄图像时的各种信息:图像信息(厂商,分辨率等),相机拍摄记录(ISO,白平衡,饱和度,锐度等),缩略图(缩略图宽度,高度等),gps(拍摄时的经度,纬度,高度)等,将这些信息按照JPEG文件标准放在图像文件头部。
你打开手机里面的Gallery,点击一张图片查看详细信息的时候,你变可以看见这张图片的这些设置,有了这些设置,我们就可以做一些额外的工作了,比如按照gps排序显示…
  第80行,设置图片的解码方式,此处配置为Bitmap.Config.RGB_565
补充:RGB_565,ARGB_8888,ARGB_4444,ALPHA_8区别
上面四种我们称之为四种色彩模式,A:透明度,R:红色;G:绿色; B: 蓝色
Bitmap.Config ARGB_8888:由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位(4字节)
Bitmap.Config ARGB_4444:由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 (2字节)
Bitmap.Config RGB_565:没有透明度,R=5,G=6,B=5,那么一个像素点占5+6+5=16位(2字节)
Bitmap.Config ALPHA_8:每个像素占8位,只有透明度,没有颜色。

一张图片是由若干个像素组成,假设一张480x800的图片,在色彩模式为ARGB_8888的情况下,会占用 4808004/1024KB=1500KB 的内存;而在RGB_565的情况下,占用的内存为:4808002/1024KB=750KB

所以一张图片如果以RGB_565的色彩模式进行存储,其占用内存会较少,但是我们也应该注意一点,这种色彩模式会损失一部分的真度,ARGB_4444色彩损失更多;ALPHA_8试用场景比较特殊,只保存透明度,可用于在图片上设置遮盖效果

  第118行,调用displayImage方法去加载并显示图片
  第119行,SimpleImageLoadingListener用于监听加载过程中的几个阶段,开始加载,加载失败,加载完成
  第135行,ImageLoadingProgressListener用于监听下载的进度

  1. 完整代码下载
    ImageLoader源码下载

  2. UIL流程简析
    在这里插入图片描述

Post-process Bitmap表示在显示图片之前,对这个bitmap的预处理;
Pre-process Bitmap表示在缓存这张图片之前,对这个bitmap的预处理;

其完成的流程为:
  如果在内存中存在图片缓存,则对这张图片进行预处理,然后显示;
  如果内存中没有缓存,但是在硬盘缓存了这张图片,则先将这张图片解码成bitmap,然后对其进行Pre-process处理,保存到内存缓存中,然后经过Post-process处理,再显示这张图片;
  如果内存和硬盘缓存都没有,则下载这张图片,然后缓存到硬盘,并将这张图片解码成bitmap,然后对其进行Pre-process处理,保存到内存缓存中,然后经过Post-process处理,再显示这张图片(这要是一个app最原始的状态)

  1. UIL API简介
    在这里插入图片描述

  DefaultConfigurationFactory:为ImageLoaderConfiguration提供一些默认的配置,我们知道在实例化ImageLoader之前,需要配置ImageLoaderConfiguration,在里面进行参数的设置,如果在ImageLoaderConfiguration设置参数的时候漏掉一些参数,则这些参数将有DefaultConfigurationFactory来进行补充
  DisplayBitmapTask:显示图片到imageview的一个任务,是实现了runable接口的类
  DisplayImageOptions:显示图片的时候一些配置参数
  ImageLoader:核心类,用于加载和显示图片
  ImageLoaderEngine:ImageLoader的引擎,负责LoadAndDisplayImageTask任务的执行
  LoadAndDisplayImageTask:负责加载和显示一个图片的任务,用于从Internet或文件系统加载图像,将其解码为Bitmap,以及使用DisplayBitmapTask在ImageAware中显示它,其中ImageAware是一个接口,在我们把图片显示在一个imgeview上的时候,首先会将这个imageview构建为ImageViewAware,ImageViewAware的作用是对imageview做了弱引用,并防止内存泄漏;ImageViewAware继承自ViewAware,ViewAware的作用是和一个view保持弱引用,并防止内存泄漏;
ViewAware实现了ImageAware接口;所以简单的理解LoadAndDisplayImageTask就是最终显示在imageview上,只不过在中间我们做了弱引用和防止内存泄漏的动作
  ProcessAndDisplayImageTask:通过DisplayBitmapTask来将一张图片显示在一个imageview上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值