Android图片异步加载的实现

android应用经常会遇到多个图片的加载。

我在写图片加载的思路是:先看内存中有没有,若没有去本地看有没有,最后去网络上下载。

上代码,抛砖引玉。


import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.drawable.BitmapDrawable;

import android.graphics.drawable.Drawable;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.support.annotation.Nullable;

import android.util.Base64;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.lang.ref.SoftReference;

import java.net.MalformedURLException;

import java.net.URL;

import java.util.HashMap;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

* 异步图片加载器

* 2018-04-06

* StoneFu

*/

public class AsyncImageLoader {

private static final StringTAG="AsyncImageLoader";

    private static final StringDIR=".www.cctv.com";

    private static final int MSG_MEMORY=0;

    private static final int MSG_SDCARD=1;

    private static final int MSG_NET=2;

    private HashMap>imageCache;

    private ExecutorServicemExecutorService;

    private static AsyncImageLoadersAsyncImageLoader;

    private  AsyncImageLoader() {

imageCache =new HashMap<>();

        int i=Runtime.getRuntime().availableProcessors()/2+1;

        JLog.e(TAG+",AsyncImageLoader-->i:"+i);

        mExecutorService = Executors.newFixedThreadPool(i);

    }

public static AsyncImageLoadergetInstance(){

if(sAsyncImageLoader==null){

sAsyncImageLoader=new AsyncImageLoader();

        }

return sAsyncImageLoader;

    }

private StringgetFileName(String url){

String encodedString = Base64.encodeToString(url.getBytes(), Base64.DEFAULT);

        String name=""+encodedString+".jpg";

        return name;

    }

private DrawableloadDrawableFromSDCard(String url){

String name=getFileName(url);

        try{

File file=getFile(name);

            FileInputStream fis =new FileInputStream(file);

            Bitmap bitmap  = BitmapFactory.decodeStream(fis);

            if(bitmap!=null){

Drawable drawable=new BitmapDrawable(bitmap);

                return drawable;

            }

}catch (IOException e){

e.printStackTrace();

        }

return null;

    }

public void loadDrawable(final String imageUrl, final ImageCallback imageCallback) {

final  Handler handler =new Handler() {

public void handleMessage(@Nullable Message message) {

JLog.e("---------------------->msg.what:"+message.what);

                    imageCallback.imageLoaded((Drawable) message.obj, imageUrl);

                }

};

        //memory

        if (imageCache.containsKey(imageUrl)) {

SoftReference softReference =imageCache.get(imageUrl);

            Drawable drawable = softReference.get();

            if (drawable !=null) {

Message message = handler.obtainMessage(MSG_MEMORY, drawable);

                handler.sendMessage(message);

            }

}

//sdcard

        Drawable drawable=loadDrawableFromSDCard(imageUrl);

        if(drawable!=null){

imageCache.put(imageUrl, new SoftReference<>(drawable));

            Message message = handler.obtainMessage(MSG_SDCARD, drawable);

            handler.sendMessage(message);

return ;

        }

//net

        mExecutorService.execute(new Runnable() {

@Override

            public void run() {

Drawable drawable =loadImageFromUrl(imageUrl);

                if(drawable!=null){

Bitmap bmp=((BitmapDrawable)drawable).getBitmap();

                    saveBitmapToSDCard(bmp,getFileName(imageUrl),Bitmap.CompressFormat.JPEG);

                }

imageCache.put(imageUrl, new SoftReference(drawable));

                Message message =handler.obtainMessage(MSG_NET, drawable);

                handler.sendMessage(message);

            }

});

    }

private static DrawableloadImageFromUrl(String strUrl) {

URL url;

        InputStream i =null;

        try {

url =new URL(strUrl);

            i = (InputStream) url.getContent();

        }catch (MalformedURLException e1) {

e1.printStackTrace();

        }catch (IOException e) {

e.printStackTrace();

        }

Drawable d = Drawable.createFromStream(i, "src");

        return d;

    }

private FilegetFile(String name){

File appDir=new File(Environment.getExternalStorageDirectory(),DIR);

        if(!appDir.exists())

appDir.mkdir();

        File file=new File(appDir,name);

        return file;

    }

private  boolean saveBitmapToSDCard(Bitmap bitmap, String name, Bitmap.CompressFormat format){

JLog.e(TAG+",saveBitmapToSDCard");

        boolean isRet=true;

        FileOutputStream out;

        File file=getFile(name);

        try{

out=new FileOutputStream(file);

            bitmap.compress(format,100,out);

            out.flush();

            out.close();

        }catch (IOException e){

e.printStackTrace();

            isRet=false;

        }

return  isRet;

    }

public interface ImageCallback {

void imageLoaded(Drawable imageDrawable, String imageUrl);

    }

}

2018年4月24日更新

这个文章(http://dev.bizo.com/2014/06/cached-thread-pool-considered-harmlful.html)说明了直接使用Executors.newCachedThreadPool()不是最佳的。主要有以下两个原因:

1,FixedTheadPool 和 SignalThreadPool:允许的请求列对长度为Integer.MAX_VALUE,可能会堆积大量的请求,导致OOM.

2.CachedThreadPool 和 ScheduledThreadPool:允许创建线程数据为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

基于上述原因,ExecutorService的实例化如下:

private static final int KEEP_ALIVE_TIME=30;

private static final TimeUnitKEEP_ALIVE_TIME_UNIT=TimeUnit.SECONDS;

private BlockingQueueblockingQueue=new LinkedBlockingDeque();

private void initExecutorService(){

int i=Runtime.getRuntime().availableProcessors();

//JLog.e(TAG+",AsyncImageLoader-->i:"+i);

//mExecutorService = Executors.newFixedThreadPool(i);

mExecutorService=new ThreadPoolExecutor(i, i *2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, blockingQueue, new ThreadFactory() {

@Override

    public ThreadnewThread(@NonNull Runnable r) {

return null;

    }

}, new RejectedExecutionHandler() {

@Override

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

}

});

}

facebook的开源的图片加载库

https://www.fresco-cn.org/docs/index.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容

  • 1.内存泄露 内存泄漏两种情况: 在堆中申请的空间没有被释放(虚拟机gc可以解决) 对象已不在使用,但仍然在内存中...
    Aimerwhy阅读 595评论 0 0
  • 曲不尽愿倾天下,歌不尽愿众人听。
    虞鶄阅读 138评论 0 0
  • JS把定义在后面的变量(并不赋值)或函数(整个函数)提升到前面定义。作用域(scoping)变量提升(Hoisti...
    adtk阅读 157评论 0 0
  • 幼儿园美食
    Miko小魔女阅读 103评论 0 0