Android中的图片加载

  • Android中的图片加载所出现的问题

    • 在Android的开发中,经常需要去加载图片,但是图片的尺寸有时候往往会很大,而我们的内存是有限的,加载进来的时候很有可能会造成内存溢出,这种结果也是我们不想看到的,所以我们为了避免这种情况的发生,就要采取一些有效的措施了。
  • 当我们去加载图片的时候可以先去获取图片的大小,然后对图片进行压缩,来展示在我们的控件上,可以使用BitmapFactory来完成
public static int calculateInSampleSize(BitmapFactory.Options options,  
        int reqWidth, int reqHeight) {  
    // 源图片的高度和宽度  
    final int height = options.outHeight;  
    final int width = options.outWidth;  
    int inSampleSize = 1;  
    if (height > reqHeight || width > reqWidth) {  
        // 计算出实际宽高和目标宽高的比率  
        final int heightRatio = Math.round((float) height / (float) reqHeight);  
        final int widthRatio = Math.round((float) width / (float) reqWidth);  
        // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高  
        // 一定都会大于等于目标的宽和高。  
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;  
    }  
    return inSampleSize;  
} 
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,  
        int reqWidth, int reqHeight) {  
    // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小  
    final BitmapFactory.Options options = new BitmapFactory.Options();  
    options.inJustDecodeBounds = true;  
    BitmapFactory.decodeResource(res, resId, options);  
    // 调用上面定义的方法计算inSampleSize值  
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  
    // 使用获取到的inSampleSize值再次解析图片  
    options.inJustDecodeBounds = false;  
    return BitmapFactory.decodeResource(res, resId, options);  
}  

然后使用控件展示图片就可以了

mImageView.setImageBitmap(  
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 宽, 高));  
  • 当只是简单的加载几张图片,不用进行压缩也是可以的,但是当加载很多图片的时候,比如使用listview或者gridview来展示图片,屏幕不断的滑动,不断的加载,就会出现OOM,这就需要使用缓存技术了
    • Android中提供了一个缓存的核心类LruCache,它的Lru算法是通过LinkedHashMap来实现的,LinkedHashMap是一个 双向链表的结构,是HashMap的子类,它里面的操作是,当调用缓存中的图片,会把图片放在尾端,插入的对象也是存储在链表的尾端,这样,当内存达到设定的最大值的时候,它会去remove掉头部的对象,也就是最近最少使用的,这样操作就可以有效的减少加载图片时的OOM了
    • 知道LruCache的原理就可以自己写一个图片的三级缓存,加载图片的时候先在内存中查找,如果内存中没有,再去本地,本地没有,再去请求网络,从网络中请求成功后,再把图片存储到本地和内存中
/**
 * 图片三级缓存操作
 * @author Administrator
 *  */
public class ImageCacheUtil {

    private LruCache<String, Bitmap> lruCache;
    private File cacheDir;
    private ExecutorService newFixedThreadPool;

    private Handler mHandler;

    public ImageCacheUtil(Context context,Handler handler){
        int maxSize = (int) (Runtime.getRuntime().maxMemory()/8);
        lruCache = new LruCache<String, Bitmap>(maxSize){
            //获取移出图片的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //getRowBytes() : 获取图片一行占用的大小
                //getHeight() : 图片的高度,图片占用的行数
                return value.getRowBytes()*value.getHeight();
            }
        };
        cacheDir = context.getCacheDir();
        newFixedThreadPool = Executors.newFixedThreadPool(5);
        this.mHandler = handler;
    }

    /**
     * 根据图片的路径获取图片
     * @param url
     * @return
     */
    public Bitmap display(String url,int position){
        //1.先从内存中获取图片.
        //LruCache<K, V>;//key:图片的名称,一般图片的路径,v:value:图片
        //缓存到内存中:lruCache.put(url, bitmap);//key:图片的名称  value:图片
        //获取缓存的图片
        Bitmap bitmap = lruCache.get(url);//key:图片的名称
        if (bitmap!=null) {
            return bitmap;
        }
        //2.如果内存中没有,查看本地缓存文件中是否有图片,有,使用,并且再次保存到内存中.
        bitmap = getBitmapFromLocal(url);
        if (bitmap!=null) {
            return bitmap;
        }
        //3.如果本地缓存文件也没有,只能从网络重新下载,重新保存到本地缓存文件中和内存中.
        getBitmapFromNet(url,position);
        return null;
    }
    /**
     * 从网络下载图片
     * @param url
     */
    private void getBitmapFromNet(String url,int position) {
        newFixedThreadPool.execute(new RunnableTask(url,position));
    }

    private class RunnableTask implements Runnable{

        private String mUrl;
        private int mPosition;

        public RunnableTask(String url,int position) {
            this.mUrl = url;
            this.mPosition = position;
        }

        @Override
        public void run() {
            Message message = Message.obtain();
            try {
                //从网络下载图片
                URL url = new URL(mUrl);//spec:请求路径
                //获取连接操作
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(5000);//连接超时时间
                con.setReadTimeout(5000);//读取超时时间
                InputStream inputStream = con.getInputStream();//获取服务器返回的数据
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                //将下载的图片保存到缓存文件中和内存中
                lruCache.put(mUrl, bitmap);
                setBitmapToLocal(mUrl, bitmap);

                //通过Handler将数据传递给MenuPhotospager进行显示
                message.what = MenuPhotosPager.SUCCESS;
                message.obj = bitmap;
                message.arg1=mPosition;
                mHandler.sendMessage(message);

                return;//通知线程池,线程已经使用完毕,可以回收
            } catch (Exception e) {
                e.printStackTrace();
                //return;
            }
            message.what = MenuPhotosPager.FAIL;
            mHandler.sendMessage(message);
        }
    }

    /**
     * 获取本地缓存的图片
     * @param url
     */
    private Bitmap getBitmapFromLocal(String url) {
        try {
            File file = new File(cacheDir, MD5Util.Md5(url).substring(10));
            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
            //将图片保存内存中
            lruCache.put(url, bitmap);

            return bitmap;
        } catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }
    /**
     * 保存图片到本地文件中
     */
    public void setBitmapToLocal(String url,Bitmap bitmap){

        try {
            File file = new File(cacheDir, MD5Util.Md5(url).substring(10));
            FileOutputStream stream = new FileOutputStream(file);
            //format : 图片的格式
            //quality : 图片的质量
            //stream : 写入流
            //PNG : 图片质量不可变
            bitmap.compress(CompressFormat.JPEG, 100, stream);//设置图片的格式、质量,并保存到相应的文件中
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
  • 常见图片加载的开源框架
  • 常见的图片加载开源框架有很多,像Glide,Picasso,Universal-Image-Loader等
  • 其实Glide和Picasso差不多,但是Glide比Picasso更易用

    Glide和Picasso

Glide.with(context)  
    .load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")  
    .into(ivImg); 
  • Gilde的with里面可以传context,activity,Fragment的好处,图片的加载将会和activity和Fragment的生命周期保持一致,如:activity执行onPause的时候,图片回去暂停加载,当activity执行onResumed的时候,图片又会重新去加载。
  • Picasso的with里面只能传context
  • Glide默认图片的BitMap格式是RGB_565,而Picasso默认的是ARGB_8888,要比Picasso内存开销小一半,当然如果对这样的效果不满意,这还是可以设置的
public class GlideConfiguration implements GlideModule {  

    @Override  
    public void applyOptions(Context context, GlideBuilder builder) {  
        // Apply options to the builder here.  
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);  
    }  

    @Override  
    public void registerComponents(Context context, Glide glide) {  
        // register ModelLoaders here.  
    }  
}  

    同时在AndroidManifest.xml中将GlideModule定义为meta-data

 <meta-data android:name="com.inthecheesefactory.lab.glidepicasso.GlideConfiguration"  
            android:value="GlideModule"/>
  • Glide另一个主要的特点就是可以加载gif动画,而Picasso却不能,Glide还可以把本地的视频 解码成一张静态的图片
  • Glide和Picasso加载图片的方式也不一样,Glide缓存图片是和ImageView的大小相同的,而Picasso是全尺寸缓存的,从这里可以看出来,Glide更节省内存,并且速度也要比Picasso快
  • 我比较喜欢使用Glide,毕竟速度快,占用内存少,但是如果少量图片,并且想要高质量的图片,可以使用Picasso,当然萝卜白菜各有所爱,就看个人喜好了

    ImageLoader的使用

    • 新建一个MyApplication继承Application,并在onCreate()中创建ImageLoader的配置参数
public class MyApplication extends Application {  

    @Override  
    public void onCreate() {  
        super.onCreate();  

        //创建默认的ImageLoader配置参数  
        ImageLoaderConfiguration configuration = ImageLoaderConfiguration  
                .createDefault(this);  

        //Initialize ImageLoader with configuration.  
        ImageLoader.getInstance().init(configuration);  
    }  

}  
  • 在开发中使用ImageLoader的 DisplayImageOptions进行加载图片,它可以配置一些图片显示的选项,比如图片在加载中ImageView显示的图片,是否需要使用内存缓存,是否需要使用文件缓存等等
 //显示图片的配置  
        DisplayImageOptions options = new DisplayImageOptions.Builder()  
                .cacheInMemory(true)  
                .cacheOnDisk(true)  
                .bitmapConfig(Bitmap.Config.RGB_565)  
                .build(); 
                  ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);  
  • ImageLoader加载图片的时候,还有设置进度的功能,在参数里面添加一个ImageLoadingProgressListener的监听
imageLoader.displayImage(imageUrl, mImageView, options, new SimpleImageLoadingListener(), new ImageLoadingProgressListener() {  

            @Override  
            public void onProgressUpdate(String imageUri, View view, int current,  
                    int total) {  

            }  
        });  
  • 使用ImageLoader并不能一定去避免OOM产生,如果经常产生要怎样的去避免呢?
    • 减少线程池中线程的个数,在ImageLoaderConfiguration中的(.threadPoolSize)中配置,推荐配置1-5
    • 默认的图片是ARGB_8888,改成RGB_565,这样就可以节省一半的 内存
    • ImageLoaderConfiguration中配置图片的内存缓存为memoryCache(new WeakMemoryCache()) 或者不使用内存缓存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值