android图片加载各种问题小结

android图片加载各种问题小结

现在做android开发,在加载图片时,都是用现成的框架,简单粗暴,但是如果没有这些框架你是怎么处理的,你会有什么样的疑问。
关于图片加载时的疑问:
1,对于一些大图片,特别加载很多大图片的时候,出现OOM怎么办?

压缩处理,官方给出的压缩算法:

public int calculateInSampleSize(BitmapFactory.Options op, int reqWidth,  
            int reqheight) {  
        int originalWidth = op.outWidth;  
        int originalHeight = op.outHeight;  
        int inSampleSize = 1;  
        if (originalWidth > reqWidth || originalHeight > reqheight) {  
            int halfWidth = originalWidth / 2;  
            int halfHeight = originalHeight / 2;  
            while ((halfWidth / inSampleSize > reqWidth)  
                    &&(halfHeight / inSampleSize > reqheight)) {  
                inSampleSize *= 2;  
  
            }  
        }  
        return inSampleSize;  
}  

2,对于listview中的图片,如果每从网络获取一张图片都要开启一个线程,势必会浪费掉很多资源,服务器也承受不起这么多的连接,如何解决?
使用线程池,可以避免了过多线程频繁创建和销毁,有的童鞋每次总是new一个线程去执行这是非常不可取的,
好一点的用的AsyncTask类,其实内部也是用到了线程池。

3,如果每显示一张图片都要去请求一次网络,势必会浪费掉很多流量,怎样才能节省流量,这又如何解决?
使用缓存,内存缓存,本地缓存

4,如果listview在滑动的过程中都要去加载图片,势必是不科学的,如何解决?
监听listview的滑动状态,Listview滑动时不加载数据,停下来时再去加载数据


5,有时由于listview中convertView的复用,引发图片显示错位,改如何解决?

图片错位问题的本质源于我们的listview使用了缓存convertView,假设一种场景,一个listview一屏显示九个item,那么在拉出第十个item的时候,事实上该item是重复使用了第一个item,
也就是说在第一个item从网络中下载图片并最终要显示的时候其实该item已经不在当前显示区域内了,此时显示的后果将是在可能在第十个item上输出图像,
这就导致了图片错位的问题。所以解决之道在于可见则显示,不可见则不显示。在ImageLoader里有个imageViews的map对象,就是用于保存当前显示区域图像对应的url集,在显示前判断处理一下即可。

最简单的解决方法就是网上说的,给 ImageView 设置一个 tag, 并预设一个图片

图片处理考虑点:
1,图片压缩
2,图片缓存: 
a,内存缓存
1,软引用:
map里面的键是用来放图片地址的,既可以是网络上的图片地址,也可以SDcard上的图片地址,map里面的值里面放的是持有软引用的Bitmap,
当然如果你要放Drawable,那也是可以的。
使用:private Map<String, SoftReference<Bitmap>> imageMap = new HashMap<String, SoftReference<Bitmap>>();
2,LruCache缓存(最近最少使用算法):
当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。

b,本地缓存
1,DiskLruCache
3,listview图片加载错位


图片三级缓存所用知识点:
1,内存缓存,使用LRUCache(最近最少使用)算法
2,使用了线程池来管理下载任务,提升多个图片加载效率
3,如何防止图片错位,设置给imageView设置tag

步骤:
1,从内存中加载图片,有则显示,没有则从本地加载图片
2,本地有则显示,没有则从网络加载图片

3,从网络加载完成,转化成bitMap之后,缓存在本地,然后缓存在内存

图片缓存流程图:


代码实现:

package com.hdc.applictionframwork.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.Handler;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 图片三级缓存的工具类
 * Created by wK on 2016/6/18.
 */
public class ImageUtils {

    //LruCache内部实现实际就是HashMap集合
    private LruCache<String, Bitmap> mMemoryCache;
    private Context mContext;

    private Map<ImageView, String> mTags	= new LinkedHashMap<ImageView, String>();
    //线程池
    private static ExecutorService mPool;
    private Handler mHandler;

    public ImageUtils(Context context){
        this.mContext = context;
        if(mMemoryCache == null){
            //申请的内存空间占到总内存的1/8,
            // (int)Runtime.getRuntime().maxMemory()/8
            mMemoryCache = new LruCache<String, Bitmap>((int)Runtime.getRuntime().maxMemory()/8){
                @Override

                protected int sizeOf(String key, Bitmap value) {
                    // 判断添加进入的bitmap的占用内存的大小
                    return value.getRowBytes()*value.getHeight();
                }
                @Override
                protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                    super.entryRemoved(evicted, key, oldValue, newValue);
                    Log.v("tag", "hard cache is full , push to soft cache");
                }
            };
        }
        if (mHandler == null)
        {
            mHandler = new Handler();
        }
        if (mPool == null){
            synchronized(ExecutorService.class){
                if(mPool == null){
                    //为了下载图片更加的流畅,我们用了3个线程来下载图片
                    mPool = Executors.newFixedThreadPool(3);
                }
            }
        }
    }
    //显示图片
    public void display(ImageView iv,String picUrl){

        //1,从内存中获取图片
        Bitmap bm = getFromMemory(picUrl);
        if(bm!=null){
            iv.setImageBitmap(bm);
            return;
        }

        //2,从本地获取图片
        bm = getFromLocal(picUrl);
        if(bm != null){
            //保存到内存
            addBitmapToMemoryCache(picUrl,bm);
            iv.setImageBitmap(bm);
            return;
        }

        //3,从网络获取图片
        getFromNet(iv,picUrl);

    }

    private void getFromNet(ImageView iv,String picUrl) {

        mTags.put(iv,picUrl);
        // 线程池管理
        mPool.execute(new LoadImageTask(iv,picUrl));

    }

    public class LoadImageTask implements Runnable{
        ImageView iv;
        String url;

        public LoadImageTask(ImageView iv, String url) {
            this.iv = iv;
            this.url = url;
        }
        @Override
        public void run() {

            HttpURLConnection conn = null;

            try {
                conn = (HttpURLConnection) new URL(url).openConnection();
                conn.setConnectTimeout(5000);// 连接服务器超时时间
                conn.setReadTimeout(5000);// 读取超时时间
                conn.connect();// 连接服务器

                if(conn.getResponseCode() == 200){
                    // 获取输入流
                    InputStream is = conn.getInputStream();
                    //把输入转化为bitmap
                    Bitmap bm = BitmapFactory.decodeStream(is);
                    // 存储到本地
                    save2Local(bm, url);

                    // 存储到内存
                    addBitmapToMemoryCache(url, bm);

                    String recently = mTags.get(iv);

                    //判断此imageView要显示的url和显示url是否相同,防止错位
                    if(recently.equals(url)){
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                display(iv,url);
                            }
                        });
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if (conn != null) {
                    conn.disconnect();
                }
            }
        }
    }

    /**
     * 添加Bitmap到内存缓存
     * @param key
     * @param bitmap
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getFromMemory(key) == null && bitmap != null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    /**
     * 把bitmap保存在本地
     * @param bm
     * @param url
     */
    private void save2Local(Bitmap bm, String url) throws Exception {
        File file = getCacheFile(url);

        FileOutputStream out = new FileOutputStream(file);
        bm.compress(Bitmap.CompressFormat.JPEG,100,out);

        out.flush();
        out.close();

    }


    /**
     * 从本地获取图片
     * @param picUrl
     * @return
     */
    private Bitmap getFromLocal(String picUrl) {

        File file = getCacheFile(picUrl);
        if(file.exists()){
            Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());

            // 存储到内存
            mMemoryCache.put(picUrl, bm);
            return bm;
        }
        return null;
    }

    /**
     * 根据url从内存中获取图片
     * @param picUrl
     * @return
     */
    private Bitmap getFromMemory(String picUrl) {

       Bitmap bitmap =  mMemoryCache.get(picUrl);

        return bitmap;
    }

    /**
     * 根据图片url获取存储路径
     * @param picUrl
     * @return
     */
    public File getCacheFile(String picUrl){
        //文件名加密
        String name = MD5Utils.encode(picUrl);

        String state = Environment.getExternalStorageState();
        //判断是否有sd卡
        if(Environment.MEDIA_MOUNTED.equals(state)){
            //sd存在的
            File dir = new File(Environment.getExternalStorageDirectory()+"/Android/data/"+mContext.getPackageName()+"/icon");
            if(!dir.exists()){
                dir.mkdir();
            }
            return new File(dir,name);

        }else{
            //不存在存放在缓存中
            File dir = new File(mContext.getCacheDir(),"/icon");
            if(!dir.exists()){
                dir.mkdir();
            }
            return new File(dir,name);
        }
    }
}
使用方法:

private ImageUtils mImageUtils = new ImageUtils(context);
mImageUtils.display(holder.item_pic_iv_pic,bean.listimage);

参考链接:
Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
http://blog.csdn.net/xiaanming/article/details/9825113

软引用和LruCache的区别:
http://blog.chinaunix.net/uid-26930580-id-4138306.html

BitmapFactory.Options 解决加载大图片OOM:
http://deep-fish.iteye.com/blog/2021016
http://blog.csdn.net/hjj0212/article/details/8467830

内存缓存LruCache实现原理:
http://www.cnblogs.com/liuling/p/2015-9-24-1.html

解决listview图片错位:
http://www.cnblogs.com/lesliefang/p/3619223.html

Android Listview滑动时不加载数据,停下来时加载数据,让App更优

http://blog.csdn.net/jdsjlzx/article/details/45914707



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值