TextView异步加载HTML格式数据中的图片(解决4.0以上主线程加载失败)
2015-08-31 08:44
597 查看
4.0以下的系统TextView显示带HTML标签并含有网络图片信息的文本时在主线程调用这条语句textView.setText(Html.fromHtml(txtString,
imageGetter, null))便可以直接正常显示HTML中的图片了;(txtString为带html标签的字符串,imageGetter为自重写的Html.ImageGetter)
但是在4.0以上这是不可以的,因为系统规定在主线程(UI线程)中不可以访问网络,为的是避免主线程阻塞;
因此必须开启子线程加载图片,但是又存在另一个严重问题,子线程中是不可以访问UI控件的(UI控件是在主线程定义、创建的),就是说对textView的操作是不允许的;
因此我的解决方式是:开启子线程加载图片,用主线程的handler变量使用post将加载内容返给UI控件显示,即显示操作压到主线程消息队列中(以下代码在4.0以上手机中测试进行)
下面分几种情况(正确方法以及几种错误方法)进行比较分析
注意,final Spanned text = Html.fromHtml(txtString,imgGetter,null) 这句是必须放在子线程Thread里面的run方法中的,
其实本质来说imageGetter(自写,以下同)就是访问网络的操作( 核心语句drawable = Drawable.createFromStream(new URL(source).openStream(), ""); )以往抛错的原因之一就是因为这句没有在子线程中进行;
之后调用handler(主线程定义的Handler变量)进行post,即在主线程中去对textView进行操作,就可以正常显示了。
程序会直接抛错崩溃,原因是textView.setText(text)放在新的子线程中访问了,其实不管是不是新的子线程,都违反了【子线程中是不可以访问UI控件的(UI控件是在主线程定义、创建的),就是说对textView的操作是不允许的】的原则
本质上和错误一是一样的,都违反了【子线程中是不可以访问UI控件的(UI控件是在主线程定义、创建的),就是说对textView的操作是不允许的】的原则
这里注意程序是不会崩溃的,原因我的理解是:毕竟Html.fromHtml(txtString,imgGetter, null)是在主线程handler的Runnable中进行的,并非直接调用,因此不会抛错,这也可能跟线程的深层含义有关,介于我是新手,期待有高手会进行点拨, 或者以后会有深入理解后给出合理解释。
总结一下我进行的错误示范实验:
一是子线程1加载,子线程2显示;
二是子线程1加载,子线程1显示;
三、四是主线程handler的Runnable加载和显示;
当然,直接在主线程中直接写这句textView.setText(Html.fromHtml(txtString, imageGetter, null))直接加载和显示肯定是会抛错的。
可见,这个问题其实就是一层“窗户纸”的问题:
子线程网络图片加载(imageGetter必须在子线程run方法中),主线程显示(需用handler)
PS.网上看见很多人解决这个方法需要重写一个类进行下载、解析,或者着重点在对imageGetter的自写上,代码很多,比较繁琐;而在我的试验中觉得上面的才是核心点,只要抓住,几行代码就能搞定;
当然,可能我的理解不够深,不能窥测大牛们的繁琐步骤可能有更远见性的考虑,还望多多指正、批评!
imageGetter, null))便可以直接正常显示HTML中的图片了;(txtString为带html标签的字符串,imageGetter为自重写的Html.ImageGetter)
但是在4.0以上这是不可以的,因为系统规定在主线程(UI线程)中不可以访问网络,为的是避免主线程阻塞;
因此必须开启子线程加载图片,但是又存在另一个严重问题,子线程中是不可以访问UI控件的(UI控件是在主线程定义、创建的),就是说对textView的操作是不允许的;
因此我的解决方式是:开启子线程加载图片,用主线程的handler变量使用post将加载内容返给UI控件显示,即显示操作压到主线程消息队列中(以下代码在4.0以上手机中测试进行)
下面分几种情况(正确方法以及几种错误方法)进行比较分析
0、子线程加载图片,handler调用控件显示(即正确方法)
new Thread(new Runnable() { @Override public void run() { final Spanned text = Html.fromHtml(txtString,imgGetter,null); handler.post(new Runnable() { @Override public void run() { textView.setText(text); } }); } }).start();
private ImageGetter imgGetter = new Html.ImageGetter() //格式语句不一定相同,只要进行网络加载图片即可 { public Drawable getDrawable(String source) { Drawable drawable = null; try { drawable = Drawable.createFromStream(new URL(source).openStream(), "");//加载网络图片资源核心语句 int screenWidth = AndroidUtil.getLayoutWidth(AllianceNewsDetailActivity.this); int draWidth = screenWidth - AndroidUtil.dip2px(AllianceNewsDetailActivity.this, 24); int draHeight = draWidth * drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth(); drawable.setBounds(0, 0, draWidth,draHeight); } catch (Exception e) { return new Drawable() { public void setColorFilter(ColorFilter cf) {} public void setAlpha(int alpha) {} public int getOpacity() {return 0;} public void draw(Canvas canvas) {} }; } return drawable; } };
注意,final Spanned text = Html.fromHtml(txtString,imgGetter,null) 这句是必须放在子线程Thread里面的run方法中的,
其实本质来说imageGetter(自写,以下同)就是访问网络的操作( 核心语句drawable = Drawable.createFromStream(new URL(source).openStream(), ""); )以往抛错的原因之一就是因为这句没有在子线程中进行;
之后调用handler(主线程定义的Handler变量)进行post,即在主线程中去对textView进行操作,就可以正常显示了。
1、错误调用方式一(开启新的子线程中刷新显示textView)
new Thread(new Runnable() { @Override public void run() { final Spanned text = Html.fromHtml(txtString,imgGetter,null); //开启新线程刷新显示,抛错崩溃 new Thread(new Runnable() { @Override public void run() { textView.setText(text); } }).start(); } }).start();
程序会直接抛错崩溃,原因是textView.setText(text)放在新的子线程中访问了,其实不管是不是新的子线程,都违反了【子线程中是不可以访问UI控件的(UI控件是在主线程定义、创建的),就是说对textView的操作是不允许的】的原则
2、错误调用方式二(子线程加载后直接显示)
new Thread(new Runnable() { @Override public void run() { final Spanned text = Html.fromHtml(txtString,imgGetter,null); //直接刷新显示,抛错崩溃 textView.setText(text); } }).start();
本质上和错误一是一样的,都违反了【子线程中是不可以访问UI控件的(UI控件是在主线程定义、创建的),就是说对textView的操作是不允许的】的原则
3、错误调用方式三(主线程handler压入Runnable线程消息进行加载和显示)
//不抛错,但加载不出图片 handler.post(new Runnable() { @Override public void run() { textView.setText(Html.fromHtml(txtString,imgGetter, null)); } });有人会想,我在handler中压入一个线程消息,在里面进行显示和加载不可以么?就是以上,很遗憾,这样加载不出图片,我的理解是:这样默认整体是在主线程框架下进行的网络加载和显示,也是不可以的。
这里注意程序是不会崩溃的,原因我的理解是:毕竟Html.fromHtml(txtString,imgGetter, null)是在主线程handler的Runnable中进行的,并非直接调用,因此不会抛错,这也可能跟线程的深层含义有关,介于我是新手,期待有高手会进行点拨, 或者以后会有深入理解后给出合理解释。
4、错误调用方式四(主线程handler加载和显示)
//不抛错,但加载不出图片 new Thread(new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { textView.setText(Html.fromHtml(txtString, imgGetter, null)); } }); } }).start();虽然handler在子线程但是handler是主线程的变量,在哪里post都是一样的都是在主线程中;本质和错误三其实一样。
总结一下我进行的错误示范实验:
一是子线程1加载,子线程2显示;
二是子线程1加载,子线程1显示;
三、四是主线程handler的Runnable加载和显示;
当然,直接在主线程中直接写这句textView.setText(Html.fromHtml(txtString, imageGetter, null))直接加载和显示肯定是会抛错的。
可见,这个问题其实就是一层“窗户纸”的问题:
子线程网络图片加载(imageGetter必须在子线程run方法中),主线程显示(需用handler)
PS.网上看见很多人解决这个方法需要重写一个类进行下载、解析,或者着重点在对imageGetter的自写上,代码很多,比较繁琐;而在我的试验中觉得上面的才是核心点,只要抓住,几行代码就能搞定;
当然,可能我的理解不够深,不能窥测大牛们的繁琐步骤可能有更远见性的考虑,还望多多指正、批评!
相关文章推荐
- html2D转换
- HTML文件中文打开乱码的问题
- 富文本Html.fromHtml方法
- HTML页面刷新、跳转
- HTML初步,编写简历页面。
- html中的标签元素
- HtmlTestRunner自动化报告代码示例
- DOM操作HTML元素属性
- Textview解析带图片的html示例
- 用html基础知识编辑简易计算器
- 【实例】html-canvas中实现图片的放大
- HTML:5的新特性之音频和视频播放
- html history
- HTML 5 全局属性
- 简单的.html文件
- 解决问题 “You don't have permission to access /index.html on this server.”
- HTML URL 编码
- HTML简单广告轮播器
- 工作记录_HTML属性值调整
- 快速学习HTML开发(从学习到项目上线用3周时间)