异步载入之使用多线程

初次尝试

异步、异步,事实上说白了就是多任务处理。也就是多线程执行。多线程那就会有各种问题,我们一步步来看。首先。我们创建一个class——ImageLoaderWithoutCaches,从命名上。大家也看出来,这个类,我们实现的是不带缓存的图像载入,不多说,我们再创建一个方法——showImageByThread,通过多线程来载入图像:
/**
* Using Thread
* @param imageView
* @param url
*/
public void showImageByThread(final ImageView imageView, final String url) { mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
imageView.setImageBitmap(bitmap);
}
};
new Thread() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromUrl(url);
Message message = Message.obtain();
message.obj = bitmap;
mHandler.sendMessage(message);
}
}.start();
}

在这种方法中,我们开启了一个线程,并在新线程中使用我们前面说的下载图像的方法进行下载,由于这时候已经不是主线程了,所以我们想怎么下就能够怎么下。天高皇帝远。

下载好了之后,通过Handler对象将消息告知在主线程中等了一辈子的Handler,收到消息之后,Handler操纵UI,改动imageView的图像。OK。跑起。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZWNsaXBzZXh5cw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

卧草,坑我啊,有的图老闪,有的图动都不动,滑一滑还不停的变,这是什么鬼。

这当然不是我们想要看见的结果。可是为什么会这样呢?我们先来分析下。首先,我们须要了解下ListView的Recycler机制。

ListView之Recycler机制

public View getView(int position, View convertView, ViewGroup parent)

这个就是ListView中的getView()方法,參数convertView就是我们的头号嫌疑犯。假如我们有100条数据,可是页面上仅仅能显示10条。Android还不会白痴到所有先创建出来。在初始创建的时候,显示多少,convertView就创建了多少,当你滑动的时候。比方向上滑动一个Item。那么Item1就会隐藏。Item11会从以下出来,那么这时候,假设Android再去创建一个convertView,那它就要被人骂死了,由于它首先要回收Item1,再去创建Item11。有意思吗?
为了解决这样一个问题,Android在ListView中提供了Recycler机制,说白了就是创建了一个Item的缓冲池,当Item1消失后。它并没有被回收。而是进入了缓冲池。成了二手货,当Item11显示的时候。它会从缓冲池中取出Item1。把Item1的数据擦擦干净继续用。所以这个时候,Item11和Item1显示的内容会是同样的。由于它们在内存中的地址是一样的,本质上是一个Item。但这个时候,Item1已经看不见了。所以它显示成什么,跟Item11没一点关系。

当它再显示的时候,又一次再写上正确的数据就好了。

这里在网上盗了张图,帮助大家来理解这样一个捡二手货的概念:
当然,假设不是异步操作,屁事都没有,由于Android UI是单线程的,或者说你重用了convertView但每次都new一个,那也没事。当然你也不会这样做。太Low了。

可是异步就不一样了,就拿我们这里下载图片来说,每一个图片都有自己的脾气。大家下载的速度都是不一样的,当初始显示ListView时。Item1的图片下载太慢,当你滑上去,显示Item11了,Item11的图显示的飞起,立即就下好了。OK,Item11的图显示好了,可立即。Item1下的慢的图也下好了,它去找Item1,可是它不知道这个时候的Item1已经变成Item11了。它还去给人家设置图像。这不就乱了吗?

OK,知道了原因,我们怎么解决呢?如今的问题就是,下载的图像就好像一个冬眠的人,它睡了20年。起来发现,他大爷已经不是当年的那个大爷了,但它的记忆还停在20年前。

所以,我们须要建立一个标志,来让冬眠的朋友回来看看清楚,如今的这个大爷是不是你曾经的那个大爷。

在Android中。我们能够很方便的使用tag来对View进行标识。Item1显示的时候,tag被设置为url1。然后它就去下载url1了,当滑动后,显示Item11了,这时候tag被改动为url11,下载url11的图了。而当url1的图片下载好了之后,回来仅仅要看看tag是不是url1就知道大爷还是不是那个大爷了,假设不是。就不显示了,这样就能够避免错位的问题了。
当然。前面的样例中另一个坑,我就不埋大家了,完成下载后。通过handler将图片通知UI线程改动ImageView,可是这个时候。參数中的ImageView与这时候传递过来的图像可能并非一一相应的关系,显示肯定不对了,所以我们还须要让url和相应的ImageView进行下配对,最简单的方法就是使用一个对象来进行保存。

多线程异步下载正解

在进行初次尝试并分析了失败原因后。我们将上面两个坑填起来,改动后的方法例如以下:
/**
* Using Thread
* @param imageView
* @param url
*/
public void showImageByThread(final ImageView imageView, final String url) { mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
ImgHolder holder = (ImgHolder) msg.obj;
if (holder.imageView.getTag().equals(holder.url)) {
holder.imageView.setImageBitmap(holder.bitmap);
}
}
};
new Thread() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromUrl(url);
Message message = Message.obtain();
message.obj = new ImgHolder(imageView, bitmap, url);
mHandler.sendMessage(message);
}
}.start();
}

private class ImgHolder {
public Bitmap bitmap;
public ImageView imageView;
public String url; public ImgHolder(ImageView iv, Bitmap bm,String url) {
this.imageView = iv;
this.bitmap = bm;
this.url = url;
}
}

改动后的方法,依旧是使用Handler,这个没有别的选择,首先我们下载图像,然后将图像和ImageView通过一个对象来进行匹配保存。在Handler拿到对象后,通过推断当前的tag与url是否相应来决定是否载入。

这里就利用到了我们在Android异步载入全解析之开篇瞎扯淡里面所提到的:

viewHolder.imageView.setTag(url);

这里就派上用场了。

最后。在Adapter中改动下代码:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String url = mData.get(position);
ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.listview_item, null);
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_lv_item);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setTag(url);
viewHolder.imageView.setImageResource(R.drawable.ic_launcher);
mImageLoader.showImageByThread(viewHolder.imageView, url);
return convertView;
}

在getView的时候,异步载入图片。

这时候,我们再来执行程序,效果例如以下:
OK,已经能够正常显示了。

我的Github 我的视频 慕课网

Android异步载入全解析之使用多线程的更多相关文章

  1. Android异步载入全解析之大图处理

    Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图 ...

  2. Android异步载入全解析之使用AsyncTask

    Android异步载入全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅能够对并发线程进行管理.更能够提高他们运行的效率.优化整个App.当然我们能够自己 ...

  3. Android异步载入全解析之IntentService

    Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个Intent ...

  4. Android异步载入全解析之开篇瞎扯淡

    Android异步载入 概述 Android异步载入在Android中使用的很广泛,除了是由于避免在主线程中做网络操作.更是为了避免在显示时由于时间太长而造成ANR,添加显示的流畅性,特别是像List ...

  5. Android异步载入AsyncTask具体解释

    曾看见有人说过.认为非常有道理.分享一下:   技术分为术和道两种:   (1)具体做事的方法是术.   (2)做事的原理和原则是道. 近期项目发现个重大问题.结果打log跟踪查是AsyncTask导 ...

  6. Android 异步消息处理机制解析

    Android 中的异步消息处理主要由四个部分组成,Message.Handler.MessageQueue.Looper.下面将会对这四个部分进行一下简要的介绍. 1. Message: Messa ...

  7. Android系统启动过程全解析

    Android系统是一款基于Linux的移动操作系统,那么Android是如何启动起来的呢?本文就详细阐述Android系统的启动过程. 从内核之上,我们首先应该从文件系统的init开始,因为 ini ...

  8. Android异步载入学习笔记之四:利用缓存优化网络载入图片及ListView载入优化

    假设不做不论什么处理.直接用网络载入图片在网速快的情况下可能没什么不好的感觉.可是假设使用移动流量或是网络不好的时候.问题就来了,要么用户会抱怨流量使用太多.要么抱怨图片载入太慢.如论从哪个角度出发, ...

  9. Eclipse,到了说再见的时候了——Android Studio最全解析

    转自:http://blog.jobbole.com/77635/ 去年的Google大会上,Google带给我们一个小玩具——Android Studio,说它是玩具,是因为它确实比较菜,界面过时, ...

随机推荐

  1. Spreadsheets

    很水的一道题,提醒自己要认真,做的头都快晕了.考虑26的特殊情况. D - Spreadsheets Time Limit:10000MS     Memory Limit:65536KB     6 ...

  2. 技术贴:解码时AVC1和H264的差别

    我一直疑问为什么有些视频解码时显示格式是:H264,大部分又是:AVC1 我在搜索编程资料时在微软的msdn上发现的: 原文:http://msdn.microsoft.com/en-us/libra ...

  3. Axure滚动效果实现

    下面的这个透明区域用于显示滚动效果,它本身是一个处于隐藏状态的动态面板,它里面也放了一个动态面板用于产生移动的效果 里面的动态面板起名“实际内容”,注意它的默认状态是“状态2”,状态2和状态一的内容一 ...

  4. python中的class

    尽管Python在Function Programming中有着其他语言难以企及的的优势,但是我们也不要忘了Python也是一门OO语言哦.因此我们关注Python在FP上的优势的同时,还得了解一下P ...

  5. iOS开发关于AppStore程序的上传流程

    主要内容: 1.创建唯一标示符App ID(前提是你的程序在真机上测试没有任何问题) 2.申请发布证书 3.申请发布描述文件 4.iTunes Connect创建App并填写信息 5.选择证书编译打包 ...

  6. new Intent(String action,Uri uri)构造器说明

    这是myDiary这个工程下MainActivity中的protected void onListItemClick(ListView l, View v, int position, long id ...

  7. DDL 和DML 区别

    DML(Data Manipulation Language)数据操纵语言: 适用范围:对数据库中的数据进行一些简单操作,如insert,delete,update,select等. DDL(Data ...

  8. [转]网络性能评估工具Iperf详解(可测丢包率)

    原文链接:安全运维之:网络性能评估工具Iperf详解:http://os.51cto.com/art/201410/454889.htm 参考博文:http://linoxide.com/monito ...

  9. jquery倒计时自动跳转

    刚开始我用下面这种方法一直报错,不知是什么原因,就是多加了页面加载时调用这个方法,还请高手看到后小小留言解惑

  10. Gauss elimination Template

    Gauss elimination : #include <iostream> #include <cstdlib> #include <cstring> #inc ...