在Android开发中,我们常常会遇到这样一种情况:在UI界面上进行某项操作后要运行一段非常耗时的代码,比方我们在界面上点击了一个”下载“button,那么我们须要运行网络请求,这是一个耗时操作。由于不知道什么时候才干完毕。为了保证不影响UI线程,所以我们会创建一个新的线程去运行我们的耗时的代码。

当我们的耗时操作完毕时,我们须要更新UI界面以告知用户操作完毕了。

所以我们可能会写出例如以下的代码:

package ispring.com.testhandler;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener {private TextView statusTextView = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);statusTextView = (TextView)findViewById(R.id.statusTextView);Button btnDownload = (Button)findViewById(R.id.btnDownload);btnDownload.setOnClickListener(this);}@Overridepublic void onClick(View v) {DownloadThread downloadThread = new DownloadThread();downloadThread.start();}class DownloadThread extends Thread{@Overridepublic void run() {try{System.out.println("開始下载文件");//此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程Thread.sleep(5000);System.out.println("文件下载完毕");//文件下载完毕后更新UIMainActivity.this.statusTextView.setText("文件下载完毕");}catch (InterruptedException e){e.printStackTrace();}}}
}

上面的代码演示了单击”下载“button后会启动一个新的线程去运行实际的下载操作。运行完毕后更新UI界面。

可是在实际运行到代码MainActivity.this.statusTextView.setText(“文件下载完毕”)时,会报错例如以下。系统崩溃退出:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
错误的意思是仅仅有创建View的原始线程才干更新View。出现这样错误的原因是Android中的View不是线程安全的。在Android应用启动时,会自己主动创建一个线程,即程序的主线程,主线程负责UI的展示、UI事件消息的派发处理等等,因此主线程也叫做UI线程,statusTextView是在UI线程中创建的。当我们在DownloadThread线程中去更新UI线程中创建的statusTextView时自然会报上面的错误。

Android的UI控件是非线程安全的,事实上非常多平台的UI控件都是非线程安全的,比方C#的.Net Framework中的UI控件也是非线程安全的,所以不仅仅在Android平台中存在从一个新线程中去更新UI线程中创建的UI控件的问题。不同的平台提供了不同的解决方式以实现跨线程跟新UI控件,Android为了解决这样的问题引入了Handler机制。

那么Handler究竟是什么呢?Handler是Android中引入的一种让开发人员參与处理线程中消息循环的机制。每一个Hanlder都关联了一个线程,每一个线程内部都维护了一个消息队列MessageQueue,这样Handler实际上也就关联了一个消息队列。能够通过Handler将Message和Runnable对象发送到该Handler所关联线程的MessageQueue(消息队列)中。然后该消息队列一直在循环拿出一个Message,对其进行处理,处理完之后拿出下一个Message,继续进行处理。周而复始。当创建一个Handler的时候,该Handler就绑定了当前创建Hanlder的线程。从这时起,该Hanlder就能够发送Message和Runnable对象到该Handler相应的消息队列中。当从MessageQueue取出某个Message时。会让Handler对其进行处理。

Handler能够用来在多线程间进行通信,在还有一个线程中去更新UI线程中的UI控件仅仅是Handler使用中的一种典型案例。除此之外,Handler能够做非常多其它的事情。每一个Handler都绑定了一个线程。假设存在两个线程ThreadA和ThreadB。并且HandlerA绑定了 ThreadA,在ThreadB中的代码运行到某处时,出于某些原因,我们须要让ThreadA运行某些代码,此时我们就能够使用Handler。我们能够在ThreadB中向HandlerA中增加某些信息以告知ThreadA中该做某些处理了。

由此能够看出,Handler是Thread的代言人。是多线程之间通信的桥梁,通过Handler,我们能够在一个线程中控制还有一个线程去做某事。

Handler提供了两种方式解决我们在本文一開始遇到的问题(在一个新线程中更新主线程中的UI控件),一种是通过post方法,一种是调用sendMessage方法。

a. 使用post方法。代码例如以下:

package ispring.com.testhandler;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener {private TextView statusTextView = null;//uiHandler在主线程中创建,所以自己主动绑定主线程private Handler uiHandler = new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);statusTextView = (TextView)findViewById(R.id.statusTextView);Button btnDownload = (Button)findViewById(R.id.btnDownload);btnDownload.setOnClickListener(this);System.out.println("Main thread id " + Thread.currentThread().getId());}@Overridepublic void onClick(View v) {DownloadThread downloadThread = new DownloadThread();downloadThread.start();}class DownloadThread extends Thread{@Overridepublic void run() {try{System.out.println("DownloadThread id " + Thread.currentThread().getId());System.out.println("開始下载文件");//此处让线程DownloadThread休眠5秒中。模拟文件的耗时过程Thread.sleep(5000);System.out.println("文件下载完毕");//文件下载完毕后更新UIRunnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("Runnable thread id " + Thread.currentThread().getId());MainActivity.this.statusTextView.setText("文件下载完毕");}};uiHandler.post(runnable);}catch (InterruptedException e){e.printStackTrace();}}}
}

我们在Activity中创建了一个Handler成员变量uiHandler,Handler有个特点。在运行new Handler()的时候,默认情况下Handler会绑定当前代码运行的线程,我们在主线程中实例化了uiHandler,所以uiHandler就自己主动绑定了主线程,即UI线程。当我们在DownloadThread中运行完耗时代码后。我们将一个Runnable对象通过post方法传入到了Handler中,Handler会在合适的时候让主线程运行Runnable中的代码,这样Runnable就在主线程中运行了,从而正确更新了主线程中的UI。下面是输出结果:

通过输出结果能够看出。Runnable中的代码所运行的线程ID与DownloadThread的线程ID不同,而与主线程的线程ID同样,因此我们也由此看出在运行了Handler.post(Runnable)这句代码之后,运行Runnable代码的线程与Handler所绑定的线程是一致的,而与运行Handler.post(Runnable)这句代码的线程(DownloadThread)无关。

b. 使用sendMessage方法,代码例如以下:

package ispring.com.testhandler;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener {private TextView statusTextView = null;//uiHandler在主线程中创建,所以自己主动绑定主线程private Handler uiHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case 1:System.out.println("handleMessage thread id " + Thread.currentThread().getId());System.out.println("msg.arg1:" + msg.arg1);System.out.println("msg.arg2:" + msg.arg2);MainActivity.this.statusTextView.setText("文件下载完毕");break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);statusTextView = (TextView)findViewById(R.id.statusTextView);Button btnDownload = (Button)findViewById(R.id.btnDownload);btnDownload.setOnClickListener(this);System.out.println("Main thread id " + Thread.currentThread().getId());}@Overridepublic void onClick(View v) {DownloadThread downloadThread = new DownloadThread();downloadThread.start();}class DownloadThread extends Thread{@Overridepublic void run() {try{System.out.println("DownloadThread id " + Thread.currentThread().getId());System.out.println("開始下载文件");//此处让线程DownloadThread休眠5秒中。模拟文件的耗时过程Thread.sleep(5000);System.out.println("文件下载完毕");//文件下载完毕后更新UIMessage msg = new Message();//尽管Message的构造函数式public的,我们也能够通过下面两种方式通过循环对象获取Message//msg = Message.obtain(uiHandler);//msg = uiHandler.obtainMessage();//what是我们自己定义的一个Message的识别码。以便于在Handler的handleMessage方法中依据what识别//出不同的Message。以便我们做出不同的处理操作msg.what = 1;//我们能够通过arg1和arg2给Message传入简单的数据msg.arg1 = 123;msg.arg2 = 321;//我们也能够通过给obj赋值Object类型传递向Message传入随意数据//msg.obj = null;//我们还能够通过setData方法和getData方法向Message中写入和读取Bundle类型的数据//msg.setData(null);//Bundle data = msg.getData();//将该Message发送给相应的HandleruiHandler.sendMessage(msg);}catch (InterruptedException e){e.printStackTrace();}}}
}

通过Message与Handler进行通信的步骤是:
1. 重写Handler的handleMessage方法,依据Message的what值进行不同的处理操作
2. 创建Message对象
尽管Message的构造函数式public的,我们还能够通过Message.obtain()或Handler.obtainMessage()来获得一个Message对象(Handler.obtainMessage()内部事实上调用了Message.obtain())。
3. 设置Message的what值
Message.what是我们自己定义的一个Message的识别码,以便于在Handler的handleMessage方法中依据what识别出不同的Message。以便我们做出不同的处理操作。
4. 设置Message的所携带的数据。简单数据能够通过两个int类型的field arg1和arg2来赋值。并能够在handleMessage中读取。
5. 假设Message须要携带复杂的数据,那么能够设置Message的obj字段,obj是Object类型,能够赋予随意类型的数据。或者能够通过调用Message的setData方法赋值Bundle类型的数据,能够通过getData方法获取该Bundle数据。
6. 我们通过Handler.sendMessage(Message)方法将Message传入Handler中让其在handleMessage中对其进行处理。
须要说明的是,假设在handleMessage中 不须要推断Message类型,那么就无须设置Message的what值;并且让Message携带数据也不是必须的,仅仅有在须要的时候才须要让其携带数据;假设确实须要让Message携带数据,应该尽量使用arg1或arg2或两者。能用arg1和arg2解决的话就不要用obj,由于用arg1和arg2更高效。
程序的运行结果例如以下:

由上我们能够看出。运行handleMessage的线程与创建Handler的线程是同一线程,在本演示样例中都是主线程。

运行handleMessage的线程与运行uiHandler.sendMessage(msg)的线程没有关系。

本文主要是对Android中Handler的作用于怎样使用进行了初步介绍,假设大家想了解Handler的内部实现原理,能够參见下一篇博文《深入源代码解析Android中的Handler,Message,MessageQueue,Looper》。

Android中Handler的使用相关推荐

  1. Android中handler的使用及原理---学习笔记

    Handler类的基本介绍以及使用: Android中UI操作是线程不安全的操作,如果有多个线程并发操作UI组件,就会出现线程安全问题,所以Android中制定了一个规则:在Android中只允许主线 ...

  2. Android中Handler

    本文通过例子来验证和说明使用Handler对象开启的线程是否和主线程为在同一线程中. 程序结构图如下: [1] HandlerActivity.java中的源码如下: [html] view plai ...

  3. Android中Handler消息传递机制应用之子线程不允许操作主线程的组件

    场景 进程 一个Android应用就是一个一个进程,每个应用在各自的进程中运行. 线程 比进程更小的独立运行的基本单位,一个进程可以包含多个线程. 要求 一个TextView和一个Button,点击B ...

  4. android handler,Android中Handler原理

    Handler主要是主线程和子线程通信.一般子线程中做一些耗时操作做完之后通知主线程来修改UI. 实际上android系统在Activity启动或者状态变化等都是通过Handler机制实现的. 首先进 ...

  5. Android中Handler的使用方法——在子线程中更新界面

    本文主要介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Ha ...

  6. android中handler简单用法

    原址:http://blog.csdn.net/wtianok/article/details/26283353 在Android中,耗时的操作常常需要由子线程来完成,但是在子线程中,无法完成对UI的 ...

  7. android中handler机制,如何使用?,Android中的Handler机制

    一.Handler概述 二.Handler发送消息的方法 三.MessageQueue的enqueueMessage() 四.Message的when字段 五.子线程中使用Handler 六.Loop ...

  8. android的handler使用方法,android中handler用法总结

    一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在handler的线程中 ...

  9. Android Studio——android中handler用法总结

    一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在handler的线程中 ...

最新文章

  1. openstack代码解读之 neutron.agent.linux.iptables_manager模块
  2. 共享服务器设置权限修改,共享服务器权限设置
  3. 求职 .net程序员!
  4. IEEE R10 2021 Special Call For Proposals Related To CoViD‘19
  5. 艾媒咨询:泛娱乐「体验共享」报告发布,网易云信多个案例领衔
  6. Android Studio的Gradle插件文档
  7. 2020快手食品行业数据价值报告
  8. 改善深层神经网络:超参数调整、正则化以及优化——2.8 Adam算法(Adaptive Moment Estimation)
  9. 双y轴设置 颜色_项目实战:Qt多段Y轴折线图框架(双Y轴段折线、支持拽拖、浮动游标显示X值各段Y值、实时下位机数据)...
  10. 设计师社区网站-交流、学习、展示
  11. 【QT学习之路】Charts的简单使用
  12. 谷歌大脑全新软件能让马赛克样模糊人脸变清晰
  13. 计算机室教师使用制度,[最新]教师计算机室管理制度
  14. CSP 201712-3 Crontab(100)
  15. 手机游戏服务器使用UDP还是TCP
  16. [蓝桥杯2015初赛]垒骰子
  17. 林大OJ--2031 凯撒密码
  18. 参与社团活动的意义_参加社团活动的意义为主题写一篇英语作文
  19. 新品发布-T3M系列高性能SDR MIMO mesh自组网主板
  20. 通过身边小事解释机器学习是什么?

热门文章

  1. Typora入门(1)
  2. 机器学习基础专题:高斯混合模型和最大期望EM算法以及代码实现
  3. 时序数据采样、原始循环神经网络RNN、RNN梯度爆炸原因推导
  4. Windows下以Local模式调试SparkStreaming的WordCount例子
  5. 记录一次读取hdfs文件时出现的问题java.net.ConnectException: Connection refused
  6. SpringCloud系列——Zuul 动态路由
  7. 整合初步---------SSH(注解版)
  8. 关于数据挖掘的几篇文章(1)
  9. java中的权限修饰符_Java的权限修饰符的区别和用法总结
  10. auth0的java-jwt_Spring boot + JWT 实现安全验证 ---auth0.jwt