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