闲来无事,准备好好梳理一下Handler机制,之前分析过没有写成博客,结果就是慢慢的淡忘了,这次趁着刚分析完,赶紧写下来。

在开始分析之前先打打基础,理解理解什么是线程以及什么是Handler,这里大部分内容引用一篇来自伯乐在线的文章,因为看来看去关于基础的部分这个人已经说得很好了,我就负责把主要的部分抽取出来。

原文地址:Android线程和Handler基础入门


现在大多数的移动设备已经变得越来越快,但是它们其实也不算是非常快。如果你想让你的APP既可以承受一些繁杂的工作而又不影响用户体验的话,那么必须把任务并行执行。在Android上,我们使用线程。

什么是线程?

线程或者线程执行本质上就是一串命令(也是程序代码),然后我们把它发送给操作系统执行。

一般来说,我们的CPU在任何时候一个核只能处理一个线程。多核处理器(目前大多数Android设备已经都是多核)顾名思义,就是可以同时处理多线程(通俗地讲就是可以同时处理多件事)。

多核处理与单核多任务处理的实质

上面我说的是一般情况,并不是所有的描述都是一定正确的。因为单核也可以用多任务模拟出多线程。

每个运行在线程中的任务都可以分解成多条指令,而且这些指令不用同时执行。所以,单核设备可以首先切换到线程1去执行指令1A,然后切换到线程2去执行指令2A,接着返回到线程1再去执行1B、1C、1D,然后继续切换到线程2,执行2B、2C等等,以此类推。

这个线程之间的切换十分迅速,以至于在单核的设备中也会发生。几乎所有的线程都在相同的时间内进行任务处理。其实,这都是因为速度太快造成的假象,就像电影《黑客帝国》里的特工Brown一样,可以变幻出很多的头和手。

Java核心里的线程

在Java中,如果要想做平行任务处理的话,会在Runnable里面执行你的代码。可以继承Thread类,或者实现Runnable接口:

// 1
public class IAmAThread extends Thread {public IAmAThread() {super("IAmAThread");}@Overridepublic void run() {// your code (sequence of instructions)}
}
// to execute this sequence of instructions in a separate thread.
new IAmAThread().start();// 2
public class IAmARunnable implements Runnable {@Overridepublic void run() {// your code (sequence of instructions)}
}
// to execute this sequence of instructions in a separate thread.
IAmARunnable myRunnable = new IAmARunnable();
new Thread(myRunnable).start();

这两个方法基本上是一样的。第一个版本是创建一个Thread类,第二个版本是需要创建一个Runnable对象,然后也需要一个Thread类来调用它。

Android上的线程

无论何时启动APP,所有的组件都会运行在一个单独的线程中(默认的)——叫做主线程。这个线程主要用于处理UI的操作并为视图组件和小部件分发事件等,因此主线程也被称作UI线程(Main Thread)。除了Main Thread之外的线程都可称为Worker Thread。Main Thread主要负责控制UI页面的显示、更新、交互等。 因此所有在UI线程中的操作要求越短越好,只有这样用户才会觉得操作比较流畅。一个比较好的做法是把一些比较耗时的操作,例如网络请求、数据库操作、 复杂计算等逻辑都封装到单独的线程,这样就可以避免阻塞主线程,这样就需要用到了Android的Handler机制。

这里划重点:Handler负责与子线程进行通讯,从而让子线程与主线程之间建立起协作的桥梁,使Android的UI更新的问题得到完美的解决

怎么创建Handler

既然Handler有这样的好处,那么看Handler怎么用,官方给出了两种方式创建一个Handler:

1、使用默认的构造方法:new Handler()。
2、使用带参的构造方法,参数是一个Runnable对象或者回调对象。

//第一种方法
private Handler handler = new Handler();  private Runnable myRunnable= new Runnable() {    public void run() {  //一些耗时操作}  }; //其他地方调用handler.post(xxx);这里就写一个post方法,实际上还有很多,诸如postDelayed、postAtTime
//第二种方法
Handler myHandler = new Handler() {  public void handleMessage(Message msg) {   switch (msg.what) {   //根据参数进行操作break;   }   super.handleMessage(msg);   }   };  //其他地方调用
myHandler.sendMessage(xxx);
如何使用Handler

这里使用一个简单的Demo来演示Handler的用法,界面偏简单就不贴了,直接贴代码,模拟的是点击Button执行下载,下载完成后更新UI。

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();}}}
}

按照以前写Java的思路的话可能会这么写,但是运行程序时候会发现控制台报错:

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

错误的意思是只有创建View的原始线程才能更新View。出现这样错误的原因是Android中的View不是线程安全的,下面给出合理的解释:

因为UI访问是没有加锁的,在多个线程中访问UI是不安全的,如果有多个子线程都去更新UI,会导致界面不断改变而混乱不堪。所以最好的解决办法就是只有一个线程有更新UI的权限,所以这个时候就只能有一个线程振臂高呼:放开那女孩,让我来!那么最合适的人选只能是主线程。

来自—Android中线程那些事

那么为了规避Android的这种机制,我们这里分别采用Handler的两种方式来实现上面的代码:

A、使用post方式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);}@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("文件下载完成");//文件下载完成后更新UIRunnable runnable = new Runnable() {@Overridepublic void run() {MainActivity.this.statusTextView.setText("文件下载完成");}};uiHandler.post(runnable);}catch (InterruptedException e){e.printStackTrace();}}}
}B、使用sendMessage方式实现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("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休眠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();}}}
}

以上代码来自博客:Android中Handler的使用

上面这两种形式都能达到我们的要求,在此不一一测验,注释写的很详细了,看到这里应该已经大致知道了如何使用Handler,但是我想我们应该远远不满足于此,下一篇博客将带着大家从源码一起看看Handler机制到底是怎么实现的。

Handler机制从入门到放弃(二)

Handler机制从入门到放弃(一)相关推荐

  1. BLE安全机制从入门到放弃

    端午安康,今天借Jayden这篇文章和大家谈一下无线传输的信息安全,该文从加密,认证,以及对应的算法优劣做了清晰明确的介绍,并在此基础上对蓝牙的配对加密过程进行了分析,是我看到把信息安全和蓝牙配对讲的 ...

  2. 【转载】BLE安全机制从入门到放弃

    BLE安全机制从入门到放弃 原文作者: Jayden Huang 原文链接: https://jaydenh215.github.io/2019/05/14/BLE安全机制从入门到放弃/ 网上介绍BL ...

  3. AOP埋点从入门到放弃(二)

    其实人最大悲哀莫过于知道自己想要什么,却不知道怎么坚持!最近迷恋上了死侍 其实和我平时的状态差不多,以一个混子的心态去做任何事情,往往成功的概率会更大!!! 一张图片镇楼!!! 上文说到了Aspect ...

  4. CYQ.Data 从入门到放弃ORM系列:开篇:自动化框架编程思维

    前言: 随着CYQ.Data 开始回归免费使用之后,发现用户的情绪越来越激动,为了保持这持续的激动性,让我有了开源的念头. 同时,由于框架经过这5-6年来的不断演进,以前发的早期教程已经太落后了,包括 ...

  5. 操作系统锁的实现方法有哪几种_「从入门到放弃-Java」并发编程-锁-synchronized...

    简介 上篇[从入门到放弃-Java]并发编程-线程安全中,我们了解到,可以通过加锁机制来保护共享对象,来实现线程安全. synchronized是java提供的一种内置的锁机制.通过synchroni ...

  6. python嵩天第二版第五章_如何避免从入门到放弃——python小组学习复盘

    2019年春节python学习行动复盘2019-02-09 为了主攻python,没有参加心理学晨读.对心理学也不敢兴趣,怕耽误学习python的时间. 那么没学习心理学的情况下,python学的怎么 ...

  7. 【从入门到放弃-Java】并发编程-锁-synchronized

    简介 上篇[从入门到放弃-Java]并发编程-线程安全中,我们了解到,可以通过加锁机制来保护共享对象,来实现线程安全. synchronized是java提供的一种内置的锁机制.通过synchroni ...

  8. JavaScript从入门到放弃 - (一)构造函数和原型

    目录 1. 创建对象的三种方式 1.1 用字面量创建 1.2 用new关键字创建 1.3 借用构造函数创建 2. 实例成员和静态成员 2.1 实例成员 2.2 静态成员 3 构造函数原型 4 对象原型 ...

  9. OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据...

    OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据 我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的 ...

  10. Struts2入门到放弃

    写在前面------------------------------------------------------------------------- 本文章主要从三个方面来学习Struts2框架 ...

最新文章

  1. 数字图像处理:腐蚀与膨胀操作
  2. 如何使用C++ Builder 6.0连接Access数据库
  3. java简单的面试题目_简单的面试题目,大跌眼镜的结果
  4. 安卓import android.support.v7.app.AppCompatActivity报错
  5. python paperclip_Python pyplot.sca方法代码示例
  6. 关于类、抽象类和接口的继承关系
  7. 【java】分布式一致性hash算法
  8. win10+ubuntu18.04双系统配置深度学习环境
  9. 如何优化js代码(2)——for循环
  10. Spring Boot 使用Dubbo 创建Hello Wrold
  11. SQL Server数据表提示NOLOCK和READPAST
  12. 如何在 React Native 中使用 NFC 标签
  13. linux ftp上传命令实例,ftp上传文件命令,Linux中使用ftp命令上传文件
  14. 如何自定义Mac锁屏界面消息?
  15. MongoDB:查询文档
  16. 总结10个Python赚钱的接单平台 兼职月入5000+
  17. Android Image Edit Lib. Android 图片编辑库,微信图片编辑库
  18. 树莓派控制16路PWM输出的PCA9685模块
  19. 【邂逅Django】——(三)视图
  20. 关于区块链的三个隐喻

热门文章

  1. 【Python笔记】序列知识点大全,包含列表.元组.字典的知识点
  2. idea插件 Foldable projectview使用方法
  3. 相同薪资的不同工作,如何选择?
  4. 玲听丨微众银行区块链开源观察:把代码丢出去,把信任拿回来
  5. Java学习第一周总结笔记
  6. 更高效、更精确的预测性维护方案是如何实现的?
  7. 小李拿奥斯卡奖之热点营销
  8. 小米手机 fastboot模式 显示 press any key to shutdown 解决方法(亲测有效,已解决)
  9. sketch放入app组件_如何在Sketch中设计Finance App注册流程
  10. Java中使用Jackson,对JSON和对象进行转换