利用AsyncTask启动一个异步任务,在doInBackground方法中,根据图片的地址下载图像。当图像下载完毕后会将图像作为方法的返回结果提交到onPostExecute方法的参数中。 在onPostExecute方法中将该图像放到ImageView中进行显示。 public static void loadImage(final String url,final ImageView iv){ new AsyncTask<Void, Void, Bitmap>() { @Override protected Bitmap doInBackground(Void... params) { try { URL ur = new URL(url); HttpURLConnection connection = (HttpURLConnection) ur.openConnection(); connection.setRequestMethod("GET"); connection.setDoInput(true); // connection.setDoOutput(true); connection.connect(); InputStream is = connection.getInputStream(); //将二进制流转换为图片 Bitmap bitmap = BitmapFactory.decodeStream(is); is.close(); return bitmap; } catch (Exception e) { e.printStackTrace(); } return null; } //根据返回结果判断图片(头像)的设定值 @Override protected void onPostExecute(Bitmap result) { if (result==null) { iv.setImageResource(R.drawable.cover_default); } iv.setImageBitmap(result); } }.execute(); }当简单加载图像方式遇到了ListView可能产生的问题:1)ListView的条目复用机制导致的图片加载的“动画”效果2)随着ListView滚动,后台会启动大量的异步任务,影响程序的流畅度3)下载图像时不能直接使用,而需要进行压缩处理4)使用缓存技术来解决图片的不断重复加载。只有在迫不得已的情况下,才要去网络中获取图像
生产者与消费者关系模型
解决上面的问题,就是在ListView与下载任务队列间添加缓存程序
以下程序使用lruCache 和disclruCache分别完成了就判断图片是不是在内存中和磁盘中含有 ,有的化就不会再从网上加载,也不会在上下滑动ListView时出现图片的闪烁现象
package com.example.music.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import com.example.music.util.DiskLruCache.Editor;
import com.example.music.util.DiskLruCache.Snapshot;
public class ImageLoader {
//线程池中线程数量
//(这个数量应该与设备CPU的核数相同)
public static int Thread_Count;
public static Context c;//上下文
//内存缓存
public static LruCache<String , Bitmap> MemCache;
//(双向任务队列)
//未来可以根据需求决定选择FIFO或LIFO
public static LinkedBlockingDeque<Runnable> taskQuen;
//线程池
public static ExecutorService exec;
//从任务队列中取下载任务的handler
public static Handler pollHandler;
//任务下载完毕后,用来刷星ListView条目显示下载图片
public static Handler uiHandler;
//“养活”pollHandler
public static Thread pollThread;
//起到一个标示的作用:用来确保ImageLoader只要进行一次初始化就行
public static boolean isFirstTime = true;
public static final int LOAD_FINISH = 101;//加载完返回
public static final int NEW_TASK = 102;//有新的任务
//磁盘缓存
public static DiskLruCache DiskSCache;
/**
* 初始化所有imageloader属性值*/
public static void init(Context context){
if (!isFirstTime) {
return;
}
c = context;
//初始化
Thread_Count = getNumberOfCores();
MemCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory()/8)){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getHeight()*value.getWidth();
}
};
//初始化磁盘缓存
try {
DiskSCache = DiskLruCache.open(directory(), appVersion(), 1, 1024*1024*10);
} catch (Exception e1) {
e1.printStackTrace();
}
//初始化队列
taskQuen = new LinkedBlockingDeque<Runnable>();
exec = Executors.newFixedThreadPool(Thread_Count);
//TODO pollHandler uiHandler
uiHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
if (msg.what==LOAD_FINISH) {
//TODO msg.obj到地放什么 能解决ListView图片加载
ValueObject vo = (ValueObject) msg.obj;
ImageView iv = vo.iv;
String url = vo.url;
Bitmap bitmap = vo.bitmap;
if (iv.getTag().toString().equals(url)) {
iv.setImageBitmap(bitmap);
}
}else{
super.handleMessage(msg);
}
}
};
pollThread = new Thread(){//普通线程,要想有pollHandler,就要先具有Looper.prepare();Looper.loop();使线程能够跑起了
public void run() {
Looper.prepare();
pollHandler = new Handler(){
public void handleMessage(Message msg) {
if (msg.what==NEW_TASK) {
try {
Runnable task = taskQuen.takeFirst();
//将取出的任务放到线程池中
exec.execute(task);
} catch (Exception e) {
e.printStackTrace();
}
}else{
super.handleMessage(msg);
}
}
};
Looper.loop();//死循环,有关代码要写在其上面Looper.prepare()下面
}
};
pollThread.start();
isFirstTime = false;
}
private static int appVersion() {
try {
PackageInfo infor = c.getPackageManager().getPackageInfo(c.getPackageName(), 0);
return infor.versionCode;
} catch (Exception e) {
e.printStackTrace();
}
return 1;
}
private static File directory() {
String path = c.getCacheDir().getPath();
return new File(path,"imageloadercache");
}
/**获取URL指向的图片放到ListView中显示
* url 图像路径
* iv 需要被显示的图片
* */
public static void loadImage(final String url, final ImageView iv){
if (isFirstTime) {
throw new RuntimeException("ImageLoader未被初始化");
}
//先判断,url所指向的图像是否在缓存中有缓存
//url转为MD5格式的字符串 MD5常用于加密操作(只能正推,不能反推)
final String md5Url = getMD5(url);
//设置tag,到时候要与vo中的url进行对比
iv.setTag(md5Url);
Bitmap bitmap = MemCache.get(url);
if (bitmap!=null) {
Log.d("TAG", "图像是从内存中加载的");
iv.setImageBitmap(bitmap);
return;
}
try {
Snapshot snap = DiskSCache.get(md5Url);//从磁盘中取数据
if (snap!=null) {
Log.d("TAG", "图像是从磁盘中获取的");
InputStream in = snap.getInputStream(0);
bitmap = BitmapFactory.decodeStream(in);
//将从磁盘中取得的图片缓存到内存中(内存中最快的速度)
MemCache.put(md5Url, bitmap);
iv.setImageBitmap(bitmap);
return;
}
} catch (IOException e1) {
e1.printStackTrace();
}
//从网络中下载
taskQuen.add(new Runnable() {
@Override
public void run() {
//TODO 进行网络下载
//进行下载任务
//发起网络连接,获取图片资源
try {
URL u = new URL(url);
HttpURLConnection connection = (HttpURLConnection) u.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.connect();
InputStream is = connection.getInputStream();
//对图片进行适当的压缩
Bitmap bitmap = compress(is,iv);//根据IV的大小为依据进行压缩
is.close();
//放到内存中进行存储
MemCache.put(md5Url, bitmap);
//将压缩后的图片放到磁盘缓存中存储
Editor editor = DiskSCache.edit(md5Url);
OutputStream os = editor.newOutputStream(0);
bitmap.compress(CompressFormat.JPEG, 100, os);
editor.commit();
DiskSCache.flush();//写日志,可写可不写
ValueObject vo = new ValueObject(iv, md5Url, bitmap);
Message.obtain(uiHandler,LOAD_FINISH,vo).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}
}
});
Message.obtain(pollHandler , NEW_TASK).sendToTarget();
}
/**根据要显示image的大小对图像进行压缩
*
* is图像源
* iv */
protected static Bitmap compress(InputStream is, ImageView iv) {
Bitmap bitmap = null;
try {
//1)获得图像源的大小
//借助Optionins来获取图片的大小(不下载图片的情况下)
//is先转为byte数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buff = new byte[4096];
int len = -1;
while((len = is.read(buff))!=-1){
bos .write(buff, 0, len);
}
//2)获得图片的大小和默认显示的大小
Options opts = new Options();
opts.inJustDecodeBounds = true;//
BitmapFactory.decodeByteArray(bos.toByteArray(), 0, bos.toByteArray().length,opts);//设置opts.inJustDecodeBounds = true以后没有任何返回值 ,只会返回null 得到的值在optsz中
int width = opts.outWidth;
int height = opts.outHeight;
int targetWidth = iv.getWidth();
int targetHeight = iv.getHeight();
//targetWidth、targetHeight两个值有可能为零时 ,这时就取不到宽高了
if (targetHeight == 0 || targetWidth == 0) {
//这时可以手动指定一个值或者以设备的宽高为目标值
targetWidth = c.getResources().getDisplayMetrics().widthPixels;//屏幕的宽
targetHeight = c.getResources().getDisplayMetrics().heightPixels;//屏幕的高
}
//3)计算压缩比
int sampleSize = 1;
if (width*1.0 / targetWidth > 1 || height*1.0 / targetHeight > 1) {
//比值谁大就取谁的值为压缩比
sampleSize = (int) Math.ceil(Math.max(width*1.0/targetWidth, height*1.0/targetHeight));//向上取整ceil宁愿使图片大点
}
//4)压缩图像
opts.inSampleSize = sampleSize;
opts.inJustDecodeBounds = false;//设置为false,可以是下面的代码有返回值
bitmap = BitmapFactory.decodeByteArray(bos.toByteArray(),0, bos.toByteArray().length,opts);
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
/**将地址转换为MD5字符串格式*/
private static String getMD5(String url) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("md5");
md.update(url.getBytes());
byte[] bytes = md.digest();
StringBuilder sb = new StringBuilder();
for(byte b:bytes){
String str = Integer.toHexString(b & 0xFF);
if (str.length()==1) {
sb.append("0");
}
sb.append(str);
}
result = sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private static int getNumberOfCores() {
//判断手机是几核的
File file =new File("/sys/devices/system/cpu/");
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
if (filename.matches("cpu[0-9]")) {
return true;
}else{
return false;
}
}
});
if(files.length>0){
return files.length;
}else{
return 1;
}
}
private static class ValueObject{
ImageView iv;
String url;
Bitmap bitmap;
public ValueObject(ImageView iv, String url, Bitmap bitmap) {
super();
this.iv = iv;
this.url = url;
this.bitmap = bitmap;
}
}
}
Android从网上异步加载图片到ListView注意事项
最新推荐文章于 2021-05-27 03:36:56 发布