异步载入之使用多线程

初次尝试

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

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

下载好了之后,通过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机制

  1. 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进行下配对,最简单的方法就是使用一个对象来进行保存。

多线程异步下载正解

在进行初次尝试并分析了失败原因后。我们将上面两个坑填起来,改动后的方法例如以下:
  1. /**
  2. * Using Thread
  3. * @param imageView
  4. * @param url
  5. */
  6. public void showImageByThread(final ImageView imageView, final String url) {
  7.  
  8. mHandler = new Handler() {
  9. @Override
  10. public void handleMessage(Message msg) {
  11. ImgHolder holder = (ImgHolder) msg.obj;
  12. if (holder.imageView.getTag().equals(holder.url)) {
  13. holder.imageView.setImageBitmap(holder.bitmap);
  14. }
  15. }
  16. };
  17. new Thread() {
  18. @Override
  19. public void run() {
  20. Bitmap bitmap = getBitmapFromUrl(url);
  21. Message message = Message.obtain();
  22. message.obj = new ImgHolder(imageView, bitmap, url);
  23. mHandler.sendMessage(message);
  24. }
  25. }.start();
  26. }

  1. private class ImgHolder {
  2. public Bitmap bitmap;
  3. public ImageView imageView;
  4. public String url;
  5.  
  6. public ImgHolder(ImageView iv, Bitmap bm,String url) {
  7. this.imageView = iv;
  8. this.bitmap = bm;
  9. this.url = url;
  10. }
  11. }

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

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

  1. viewHolder.imageView.setTag(url);

这里就派上用场了。

最后。在Adapter中改动下代码:
  1. @Override
  2. public View getView(int position, View convertView, ViewGroup parent) {
  3. String url = mData.get(position);
  4. ViewHolder viewHolder = null;
  5. if (convertView == null) {
  6. viewHolder = new ViewHolder();
  7. convertView = mInflater.inflate(R.layout.listview_item, null);
  8. viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_lv_item);
  9. convertView.setTag(viewHolder);
  10. } else {
  11. viewHolder = (ViewHolder) convertView.getTag();
  12. }
  13. viewHolder.imageView.setTag(url);
  14. viewHolder.imageView.setImageResource(R.drawable.ic_launcher);
  15. mImageLoader.showImageByThread(viewHolder.imageView, url);
  16. return convertView;
  17. }

在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. 转: c++继承中的内存布局

    英文原文: http://www.openrce.org/articles/files/jangrayhood.pdf 翻译: http://blog.csdn.net/jiangyi711/arti ...

  2. linux中grep的用法

    http://www.9usb.net/200902/linux-grep.html http://blog.51yip.com/linux/1008.html http://blog.csdn.ne ...

  3. HDU 4741 Save Labman No.004 2013 ACM/ICPC 杭州网络赛

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4741 题意:给你两条异面直线,然你求着两条直线的最短距离,并求出这条中垂线与两直线的交点. 需要注意的是 ...

  4. CocoaPods的安装及设置

    1>CocoaPods简介 CocoaPods是一个用来帮助我们管理第三方依赖库的工具 在开发iOS应用时,会经常使用第三方类库,手动下载比较麻烦,通过CocoaPods可以便捷的下载与管理第三 ...

  5. C语言中操作符的优先级大全

    C语言中操作符的优先级大全, 当然c++, Objective-C,大部分语言都试用. 下面是来自The C Programming Language 2th的总结. OperatorsAssocia ...

  6. Python中NumPy基础使用

    Python发展至今,已经有越来越多的人使用python进行科学技术,NumPY是python中的一款高性能科学计算和数据分析的基础包. ndarray ndarray(以下简称数组)是numpy的数 ...

  7. 动态规划---最长上升子序列问题(O(nlogn),O(n^2))

    LIS(Longest Increasing Subsequence)最长上升子序列 或者 最长不下降子序列.很基础的题目,有两种算法,复杂度分别为O(n*logn)和O(n^2) . ******* ...

  8. instanceof 变量是否属于某一类 class 的实例

    <?phpclass MyClass{} class NotMyClass{}$a = new MyClass;$b = new NotMyClass;var_dump($a instanceo ...

  9. currentStyle和getComputedStyle的兼容写法

    今天学习javascript的时候,教程中介绍了一种简单实现jQuery 中css()方法的写法 <!DOCTYPE html> <html lang="en"& ...

  10. CodeForces 276D – Little Girl and Maximum XOR 贪心

    整整10个月后第二次搞这个问题才搞懂........第一次还是太随意了. 解题思路: 经过打表可得规律答案要么是0 要么是2的N次 - 1 要得到最大的XOR值,其值一定是2的N次 - 1 即在 l ...