• Android加载图片的策略


    实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中

    1.简介 
    现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。

    现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。 

    2.图片缓存的原理 
    实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。 

    关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。 

    从代码上来说,采用一个ImageManager来负责图片的管理和缓存,函数接口为public void loadBitmap(String url, Handler handler) ;其中url为要下载的图片地址,handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。ImageManager中使用的ImageMemoryCache(内存缓存)、ImageFileCache(文件缓存)以及LruCache(最近最久未使用缓存)会在后续文章中介绍。 

    /* 
    * 图片管理 
    * 异步获取图片,直接调用loadImage()函数,该函数自己判断是从缓存还是网络加载 
    * 同步获取图片,直接调用getBitmap()函数,该函数自己判断是从缓存还是网络加载 
    * 仅从本地获取图片,调用getBitmapFromNative() 
    * 仅从网络加载图片,调用getBitmapFromHttp() 

    */ 
    public class ImageManager implements IManager 

    private final static String TAG = "ImageManager"; 

    private ImageMemoryCache imageMemoryCache; //内存缓存 

    private ImageFileCache imageFileCache; //文件缓存 

    //正在下载的image列表 
    public static HashMap<String, Handler> ongoingTaskMap = new HashMap<String, Handler>(); 

    //等待下载的image列表 
    public static HashMap<String, Handler> waitingTaskMap = new HashMap<String, Handler>(); 

    //同时下载图片的线程个数 
    final static int MAX_DOWNLOAD_IMAGE_THREAD = 4; 

    private final Handler downloadStatusHandler = new Handler(){ 
    public void handleMessage(Message msg) 

    startDownloadNext(); 

    }; 

    public ImageManager() 

    imageMemoryCache = new ImageMemoryCache(); 
    imageFileCache = new ImageFileCache(); 


    /** 
    * 获取图片,多线程的入口 
    */ 
    public void loadBitmap(String url, Handler handler) 

    //先从内存缓存中获取,取到直接加载 
    Bitmap bitmap = getBitmapFromNative(url); 
    if (bitmap != null) 

    Logger.d(TAG, "loadBitmap:loaded from native"); 
    Message msg = Message.obtain(); 
    Bundle bundle = new Bundle(); 
    bundle.putString("url", url); 
    msg.obj = bitmap; 
    msg.setData(bundle); 
    handler.sendMessage(msg); 

    else 

    Logger.d(TAG, "loadBitmap:will load by network"); 
    downloadBmpOnNewThread(url, handler); 


    /** 
    * 新起线程下载图片 
    */ 
    private void downloadBmpOnNewThread(final String url, final Handler handler) 

    Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size()); 

    if (ongoingTaskMap.size() >= MAX_DOWNLOAD_IMAGE_THREAD) 

    synchronized (waitingTaskMap) 

    waitingTaskMap.put(url, handler); 


    else 

    synchronized (ongoingTaskMap) 

    ongoingTaskMap.put(url, handler); 

    new Thread() 

    public void run() 

    Bitmap bmp = getBitmapFromHttp(url); 
    // 不论下载是否成功,都从下载队列中移除,再由业务逻辑判断是否重新下载 
    // 下载图片使用了httpClientRequest,本身已经带了重连机制 
    synchronized (ongoingTaskMap) 

    ongoingTaskMap.remove(url); 


    if(downloadStatusHandler != null) 

    downloadStatusHandler.sendEmptyMessage(0); 


    Message msg = Message.obtain(); 
    msg.obj = bmp; 
    Bundle bundle = new Bundle(); 
    bundle.putString("url", url); 
    msg.setData(bundle); 

    if(handler != null) 

    handler.sendMessage(msg); 


    }.start(); 


    /** 
    * 依次从内存,缓存文件,网络上加载单个bitmap,不考虑线程的问题 
    */ 
    public Bitmap getBitmap(String url) 

    // 从内存缓存中获取图片 
    Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url); 
    if (bitmap == null) 

    // 文件缓存中获取 
    bitmap = imageFileCache.getImageFromFile(url); 
    if (bitmap != null) 

    // 添加到内存缓存 
    imageMemoryCache.addBitmapToMemory(url, bitmap); 

    else 

    // 从网络获取 
    bitmap = getBitmapFromHttp(url); 


    return bitmap; 


    /** 
    * 从内存或者缓存文件中获取bitmap 
    */ 
    public Bitmap getBitmapFromNative(String url) 

    Bitmap bitmap = null; 
    bitmap = imageMemoryCache.getBitmapFromMemory(url); 

    if(bitmap == null) 

    bitmap = imageFileCache.getImageFromFile(url); 
    if(bitmap != null) 

    // 添加到内存缓存 
    imageMemoryCache.addBitmapToMemory(url, bitmap); 


    return bitmap; 


    /** 
    * 通过网络下载图片,与线程无关 
    */ 
    public Bitmap getBitmapFromHttp(String url) 

    Bitmap bmp = null; 

    try 

    byte[] tmpPicByte = getImageBytes(url); 

    if (tmpPicByte != null) 

    bmp = BitmapFactory.decodeByteArray(tmpPicByte, 0, 
    tmpPicByte.length); 

    tmpPicByte = null; 

    catch(Exception e) 

    e.printStackTrace(); 


    if(bmp != null) 

    // 添加到文件缓存 
    imageFileCache.saveBitmapToFile(bmp, url); 
    // 添加到内存缓存 
    imageMemoryCache.addBitmapToMemory(url, bmp); 

    return bmp; 


    /** 
    * 下载链接的图片资源 

    * @param url 

    * @return 图片 
    */ 
    public byte[] getImageBytes(String url) 

    byte[] pic = null; 
    if (url != null && !"".equals(url)) 

    Requester request = RequesterFactory.getRequester( 
    Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC); 
    // 执行请求 
    MyResponse myResponse = null; 
    MyRequest mMyRequest; 
    mMyRequest = new MyRequest(); 
    mMyRequest.setUrl(url); 
    mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity"); 
    InputStream is = null; 
    ByteArrayOutputStream baos = null; 
    try { 
    myResponse = request.execute(mMyRequest); 
    is = myResponse.getInputStream().getImpl(); 
    baos = new ByteArrayOutputStream(); 
    byte[] b = new byte[512]; 
    int len = 0; 
    while ((len = is.read(b)) != -1) 

    baos.write(b, 0, len); 
    baos.flush(); 

    pic = baos.toByteArray(); 
    Logger.d(TAG, "icon bytes.length=" + pic.length); 

    catch (Exception e3) 

    e3.printStackTrace(); 
    try 

    Logger.e(TAG, 
    "download shortcut icon faild and responsecode=" 
    + myResponse.getStatusCode()); 

    catch (Exception e4) 

    e4.printStackTrace(); 


    finally 

    try 

    if (is != null) 

    is.close(); 
    is = null; 


    catch (Exception e2) 

    e2.printStackTrace(); 

    try 

    if (baos != null) 

    baos.close(); 
    baos = null; 


    catch (Exception e2) 

    e2.printStackTrace(); 

    try 

    request.close(); 

    catch (Exception e1) 

    e1.printStackTrace(); 



    return pic; 


    /** 
    * 取出等待队列第一个任务,开始下载 
    */ 
    private void startDownloadNext() 

    synchronized(waitingTaskMap) 

    Logger.d(TAG, "begin start next"); 
    Iterator iter = waitingTaskMap.entrySet().iterator(); 

    while (iter.hasNext()) 


    Map.Entry entry = (Map.Entry) iter.next(); 
    Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey()); 

    if(entry != null) 

    waitingTaskMap.remove(entry.getKey()); 
    downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue()); 

    break; 




    public String startDownloadNext_ForUnitTest() 

    String urlString = null; 
    synchronized(waitingTaskMap) 

    Logger.d(TAG, "begin start next"); 
    Iterator iter = waitingTaskMap.entrySet().iterator(); 

    while (iter.hasNext()) 

    Map.Entry entry = (Map.Entry) iter.next(); 
    urlString = (String)entry.getKey(); 
    waitingTaskMap.remove(entry.getKey()); 
    break; 


    return urlString; 


    /** 
    * 图片变为圆角 
    * @param bitmap:传入的bitmap 
    * @param pixels:圆角的度数,值越大,圆角越大 
    * @return bitmap:加入圆角的bitmap 
    */ 
    public static Bitmap toRoundCorner(Bitmap bitmap, int pixels) 

    if(bitmap == null) 
    return null; 
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); 
    Canvas canvas = new Canvas(output); 
    final int color = 0xff424242; 
    final Paint paint = new Paint(); 
    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
    final RectF rectF = new RectF(rect); 
    final float roundPx = pixels; 
    paint.setAntiAlias(true); 
    canvas.drawARGB(0, 0, 0, 0); 
    paint.setColor(color); 
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
    canvas.drawBitmap(bitmap, rect, rect, paint); 
    return output; 


    public byte managerId() 

    return IMAGE_ID; 

  • 相关阅读:
    [Codeforces 1290C]Prefix Enlightenment
    [JLOI 2015]战争调度
    [APIO 2010]特别行动队
    [CEOI 2004]锯木厂选址
    [USACO 08MAR]土地购买
    [HNOI 2017]大佬
    [NOI 2011]NOI 嘉年华
    [SHOI 2013]超级跳马
    [NOI 2005]瑰丽华尔兹
    [SCOI 2010]股票交易
  • 原文地址:https://www.cnblogs.com/alexjie-123/p/6098197.html
Copyright © 2020-2023  润新知