子线程更新UI会发生Android.view.ViewRoot$CalledFromWrongThreadException异常的解决方法

子线程更新UI

显然假如你的程序需要执行耗时的操作的话,假如像上例一样由主线程来负责执行该操作是错误的。所以我们需要在onClick方法中创建一个新的子线程来负责调用GOOGLE API来获得天气数据。刚接触Android的开发者最轻易想到的方式就是如下:

 public void onClick(View v) {

       //创建一个子线程执行耗时的从网络上获得天气信息的操作

       new Thread() {

           @Override

           public void run() {

              //获得用户输入的城市名称

              String city = editText.getText().toString();

              //调用Google 天气API查询指定城市的当日天气情况

              String weather = getWetherByCity(city);

              //把天气信息显示在title上

              setTitle(weather);

           }

       }.start();

    }

但是很不幸,你会发现Android会提示程序由于异常而终止。为什么在其他平台上看起来很简单的代码在Android上运行的时候依然会出错呢?假如你观察LogCat中打印的日志信息就会发现这样的错误日志:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

从错误信息不难看出Android禁止其他子线程来更新由UI thread创建的试图。本例中显示天气信息的title实际是就是一个由UI thread所创建的TextView,所以参试在一个子线程中去更改TextView的时候就出错了。这显示违反了单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行

2.2 Message Queue

在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列),线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:

l Message Queue

Message Queue是一个消息队列,用来存放通过Handler发布的消息。消息队列通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列。Android在第一启动程序时会默认会为UI thread创建一个关联的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。

l Handler

通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。没个Handler都会与唯一的一个线程以及该线程的消息队列管理。当你创建一个新的Handler时候,默认情况下,它将关联到创建它的这个线程和该线程的消息队列。也就是说,假如你通过Handler发布消息的话,消息将只会发送到与它关联的这个消息队列,当然也只能处理该消息队列中的消息。

主要的方法有:

1)   public final boolean sendMessage(Message msg)

把消息放入该Handler所关联的消息队列,放置在所有当前时间前未被处理的消息后。

2)   public void handleMessage(Message msg)

关联该消息队列的线程将通过调用Handler的handleMessage方法来接收和处理消息,通常需要子类化Handler来实现handleMessage。

l Looper

Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传送给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。

1)   可以通过Looper类的静态方法Looper.myLooper得到当前线程的Looper实例,假如当前线程未关联一个Looper实例,该方法将返回空。

2)   可以通过静态方法Looper. getMainLooper方法得到主线程的Looper实例

线程,消息队列,Handler,Looper之间的关系可以通过一个图来展现:

在了解了消息队列及其相关组件的设计思想后,我们将把天气预告的案例通过消息队列来重新实现:

在了解了消息队列及其相关组件的设计思想后,我们将把天气预告的案例通过消息队列来重新实现:

private EditText editText;

    private Handler messageHandler;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        editText = (EditText) findViewById(R.id.weather_city_edit);

        Button button = (Button) findViewById(R.id.goQuery);

        button.setOnClickListener(this);

        //得到当前线程的Looper实例,由于当前线程是UI线程也可以通过Looper.getMainLooper()得到

        Looper looper = Looper.myLooper();

        //此处甚至可以不需要设置Looper,因为 Handler默认就使用当前线程的Looper

        messageHandler = new MessageHandler(looper);

    }

    @Override

    public void onClick(View v) {

        //创建一个子线程去做耗时的网络连接工作

        new Thread() {

            @Override

            public void run() {

                //活动用户输入的城市名称

                String city = editText.getText().toString();

                //调用Google 天气API查询指定城市的当日天气情况

                String weather = getWetherByCity(city);

                //创建一个Message对象,并把得到的天气信息赋值给Message对象

                Message message = Message.obtain();

                message.obj = weather;

                //通过Handler发布携带有天气情况的消息

                messageHandler.sendMessage(message);

            }

        }.start();

    }

    //子类化一个Handler

    class MessageHandler extends Handler {

        public MessageHandler(Looper looper) {

            super(looper);

        }

        @Override

        public void handleMessage(Message msg) {

            //处理收到的消息,把天气信息显示在title上

            setTitle((String) msg.obj);

        }

    }

通过消息队列改写过后的天气预告程序已经可以成功运行,因为Handler的handleMessage方法实际是由关联有该消息队列的UI thread调用,而在UI thread中更新title并没有违反Android的单线程模型的原则。

Android 编程:calledfromWrongThreadException 的原因的更多相关文章

  1. 读《Android编程权威指南》

    因为去年双十二购买了一折的<Android 编程权威指南(第一版)>,在第二版出来后图灵社区给我推送了第二版的优惠码,激动之余就立马下单购买电子书,不得不说Big Nerd Ranch G ...

  2. 菜鸟学Android编程——简单计算器《一》

    菜鸟瞎搞,高手莫进 本人菜鸟一枚,最近在学Android编程,网上看了一些视频教程,于是想着平时手机上的计算器应该很简单,自己何不尝试着做一个呢? 于是就冒冒失失的开撸了. 简单计算器嘛,功能当然很少 ...

  3. [电子书] 《Android编程入门很简单》

    <Android编程入门很简单>是一本与众不同的Android学习读物,是一本化繁为简,把抽象问题具体化,把复杂问题简单化的书.本书避免出现云山雾罩.晦涩难懂的讲解,代之以轻松活泼.由浅入 ...

  4. Eclipse下Android编程代码自动提示

    在用Eclipse进行Android编程,为了代码自动提示,需要进行如下操作: 1.设置 java 文件的代码提示功能 打 开 Eclipse 依次选择 Window > Preferences ...

  5. Android编程心得-在任意类中获取当前屏幕宽高

    进行Android编程时,很多时候都需要获取当前屏幕的宽度与高度,但是当我们需要在别的类中调用屏幕宽高时,直接用原来的方法是不行的,下面我来介绍如何在任意类中调用宽度高度的两种方法. public v ...

  6. Android编程中的实用快捷键

    作为一个优秀的程序员,不但要能开发出漂亮的软件,也要能熟练掌握编程的技巧,包括IDE的快捷键使用.比如linux 下的VI编辑器,对于不熟练快捷键的人来说就是一个噩梦,但一旦你熟练了VI的快捷键,VI ...

  7. Android编程中的5种数据存储方式

    Android编程中的5种数据存储方式 作者:牛奶.不加糖 字体:[增加 减小] 类型:转载 时间:2015-12-03我要评论 这篇文章主要介绍了Android编程中的5种数据存储方式,结合实例形式 ...

  8. 《Android编程权威指南》

    <Android编程权威指南> 基本信息 原书名:Android programming: the big nerd ranch guide 原出版社: Big Nerd Ranch Gu ...

  9. 《Delphi XE6 android 编程入门教程》推荐

    近5.6年已经没有看见关于delphi的新技术的书出来了(看来在国内delphi的使用量确实很低了), 高勇同学最近出了一本<Delphi XE6 android 编程入门教程>,上周刚拿 ...

  10. Android编程 EditView 中如何设置最多可以输入的字符数量 属性 android:ems 与 android:maxLength 的区别

    最近有一个新的感悟,那就是工作的时候千万不要遇到那种特要人无语的领导,很不幸我现在就遇到了这样的一个领导,说是要给领导认识的一个熟人家的孩子写本科毕业设计预算把我给派过去给本科生写毕业设计,这事情的确 ...

随机推荐

  1. HDOJ 1326 Box of Bricks(简单题)

    Problem Description Little Bob likes playing with his box of bricks. He puts the bricks one upon ano ...

  2. linux下so动态库一些不为人知的秘密

    linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识.    基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些 ...

  3. ViewPager的使用方法和实现过程

    布局文件里添加viewPager布局 <android.support.v4.view.ViewPager android:id="@+id/search_viewpager" ...

  4. Vanya and Triangles 暴力枚举

    枚举法: 枚举法是利用计算机速度快, 精度高的特点, 对要解决的问题所有可能情况进行霸道的, 一个不漏检验, 从中找出符合要求的答案. 特点: 1. 得到的结果一定正确. 2. 可能做了很多无用功,效 ...

  5. zabbix discovery

    preface(见面礼): 仅扫tcp端口: netstat -tnlp|egrep -i "$1"

  6. Python爬虫实战(二)

    本来晚上是准备写贴吧爬虫的,但是在分析页面时就遇到了大麻烦!选取了某个帖子,在爬取的时候,发现正则匹配不全..很尴尬!!先来看看吧, #!/usr/bin/env python # -*- codin ...

  7. 正则表达式:网页爬虫:从TXT中获取邮箱地址(获取的练习,缺点:一行只能匹配一个)

    import java.util.regex.*; import java.io.*; class L { public static void main(String[] args) throws  ...

  8. SharePoint 要一个多行文本类型字段为特殊类型的链接

    1.插入在测试列表中的多行文本字段.名字叫做Content.例如下面的附图: 2.在Content字段里.加入一个Link.例如以下图: 3.尝试输入Notes格式的Link,例如以下图: 4.点击O ...

  9. linux中文乱码问题及locale详解

    一.修改系统默认语言及中文乱码问题记录系统默认使用语言的文件是/etc/sysconfig/i18n,如果默认安装的是中文的系统,i18n的内容如下: LANG="zh_CN.UTF-8&q ...

  10. 学习Android NestedScroll

    NestedScrollingChildHelper 这是一个用于实现子视图嵌套滚动的辅助类,并提供对Android 5.0之前版本的前兼容. View要作为嵌套滚动中的Child,要在构造方法中实例 ...