您的位置:首页 > 其它

实现自己的ImageLoader(4)------同步异步加载与完整demo

2016-04-05 12:48 429 查看
终于写到最后一章了,先说同步加载,同步加载适用于图片已经存在本机上了

public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {
Bitmap bitmap = loadBitmapFromMemCache(uri);
if (bitmap != null) {
Log.d(TAG, "loadBitmapFromMemCache,url:" + uri);
return bitmap;
}
try {
bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
if (bitmap != null) {
Log.d(TAG, "loadBitmapFromDisk,url:" + uri);
return bitmap;
}
bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);
Log.d(TAG, "loadBitmapFromhttp,url:" + uri);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (bitmap == null && !mIsDiskLruCacheCreated) {
Log.w(TAG, "encounter error,DiskLruCache is not created");
bitmap = downloadBitmapFromUrl(uri);
}
return bitmap;
}具体分析上一篇博客已经讲过了,这里说说异步加载
在Android4.0以后,主线程是不能进行网络访问的,因为网络访问有可能耗时太多,引起ANR。我们第一次设置图片的时候图片还在网络上未下载到本地,imageView是个空的框架,里面并没有填充我们想要的图片。可能你看到这里觉得那太简单了,那等把图片从网络下载下来以后再更新imageview不就完了吗?逻辑是这样,但是我们首先需要解决一个问题,网络访问必须在子线程里,而更新UI必须要在主线程里。因此我们需要在子线程中下载图片,然后把子线程的图片传到主线程再进行更新。这里就要引入线程间的通信机制Handler,http://blog.csdn.net/yuwang_00/article/details/50979975Handler详解在另外一篇博客中,这里不再赘述

那我们就按照这个逻辑开始写吧,首先先在子线程中访问网络。

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);

@Override
public Thread newThread(Runnable arg0) {
// TODO Auto-generated method stub
return new Thread(arg0, "ImageLoader#" + mCount.getAndIncrement());
}
};
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), sThreadFactory);这里我们创建了一个线程池来管理新开的线程,线程池可以并发处理大量线程,并且可以复用线程,这个图片的网络请求加载完了还可以给正在等待加载的图片使用,减少我们在线程上的开销,这对于整体效率的提升是有帮助的,线程池解析在http://blog.csdn.net/yuwang_00/article/details/51000957中
public void bindBitmap(final String uri, final ImageView imageView,
final int reqWidth, final int reqHeight) {
imageView.setTag(TAG_KEY_URI, uri);
Bitmap bitmap = loadBitmapFromMemCache(uri);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
Runnable loadBitmapTask = new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);
if (bitmap != null) {
LoaderResult result = new LoaderResult(imageView, uri,
bitmap);
mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result)
.sendToTarget();
}
}
};
THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
}看这段代码,既然我们是ImageLoader,是一个工具类,那我们就尽可能的让MainActivity少做一些工作,这里传入了四个参数,uri和需要设置的imageView对象,规定的宽高(用于压缩图片),这里先给ImageView对象设置一个Tag,这里解释一下。
平常我们使用ListView或者GridView时,为了效率问题(我们总不可能每一个item都建立一个view吧),我们会在L与G中的getView方法复用暂时看不见的item。比如说一个listView中有50个item,但是只能同时显示7个,当我们滑动到第八个的时候,第一个item就暂时处于不可见的状态,那么这个item可以在getView中拿到,填充成为第八个item。这就是listView优化,代码就不贴了,网络上搜索listView优化出一堆。为什么要说这个呢?就是因为当我们还在访问网络请求图片的时候,ImageView可能已经被复用了,那我原来下载的图片就对应不上原来的imageview了,比如我第一个给第八个item复用了,那我第八个item有可能设置的就是第一个item的图片,所以我们需要一个Tag来区分我的item到底是不是原来那个。

接下来我们新建一个runnable,重写里面的run方法,里面先调用loadBitmap,不管从内存也好还是网络也好,最终它会获取到一个bitmap对象,然后我们将返回的bitmap与uri、imageview一起打包成一个LoaderResult类(自定义的类)一起通过message发送到主线程中。

调用THREAD_POOL_EXECUTOR.execute(loadBitmapTask);以后我们的runnable才开始执行(线程池中的方法默认为子线程)

让我们看看重写的handlemessage方法

private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
public void handleMessage(android.os.Message msg) {
LoaderResult result = (LoaderResult) msg.obj;
ImageView imageView = result.imageView;
imageView.setImageBitmap(result.bitmap);
String uri = (String) imageView.getTag(TAG_KEY_URI);
if (uri.equals(result.uri)) {
imageView.setImageBitmap(result.bitmap);
} else {
Log.w(TAG, "set image bitmap,but url has changed,ignored!");
}
};
};这里拿到了主线程的looper来构建Handler。先从LoaderResult中取出我们想要的东西,然后判断ImageView是否已经被复用(通过检查TAG,因为我们每一次使用ImageLoader都会把它的url设置为Tag),然后setImageBitmap,显示我们想要的图片。到这里,整个ImageLoader分析已经全部结束了
这个demo不是我自己写的,是《Android开发艺术探索》作者写的,下载地址在http://download.csdn.net/detail/yuwang_00/9480256

如果有什么疑问欢迎私信或者评论
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: