在上一篇中,我简单说了用AsyncTask来完成简单异步任务,但AsyncTask是把所有的异步任务放到一个队列中依次在同一个线程中执行。这样就带来一个问题,它无法处理那些耗时长、需要并行的的任务。如何处理这个难题呢?一是自己开启线程然后处理线程通信问题,二是使用HandlerThread这一便捷类来处理。万变不离其宗,先来说明Android线程、及线程通信的原理,然后对于那些便捷的API自然就懂了。

二、Thread 与 Handler

  本节涉及的概念较多,有Thread,Handler,Looper,Message,MessageQuene,对于Looper和MessageQueue只是简单的谈及它们在线程通信中的作用,Thread,Handler及Message我会尽力讲清楚些。

  1.Thread基础:

  1)参考文档:http://developer.android.com/reference/java/lang/Thread.html

  我包括我的好些朋友学东西时都会忽略官方文档的重要性,其中很大的原因是因为英文的缘故吧,但其实看懂文档要求英语能力并不高。看别人写的文章、博客、书籍,听别人讲技术,那始终是经过他人过滤后的知识,而这一切未必是你需要的。前车之鉴固然重要,但并不是每一个老人的话都得听。歪果仁写东西有个特点就是他不仅把原理给你讲清楚了,还爱举列子,该怎么做不建议怎么做都会涉及。说了这么多,就是建议自己去看看Android的开发文档,从Guide到Reference内化出自己的知识体系。

  2)简单介绍:

  看Android的Thread继承树,我们就知道它完全继承了Java的线程体系,在这就不赘言过多的Thread细节了,我就如何开启一个线程讲一讲。开启一个新线程有两种方法,第一种是拓展Thread类,在子类中重写run()方法;第二种是声明Thread对象时传入一个Runnable对象,因为只涉及到一个抽象方法,Runnable对象可以用jdk8的Lambda表达式来代替,这样使得线程的创建变得更加简单。创建好了线程后,如何让线程开始执行呢?调用Thread.run()即可让线程进入待执行队列,当获得CPU时间时它就运行起来了。具体的用法例子会在后面的示例中展现。

  2.Handler基础:

  1)参考文档:http://developer.android.com/reference/android/os/Handler.html

  2)概述:

  之前看过一部美剧《天蝎计划》,在介绍团队负责人时,这样说道”He is our goverment handler“。Android中的Handler就是这样一个概念,它是线程通信的发送者和处理者,线程要进行通信时会让handler发出相应的消息,通过Looper传递,Handler发出的消息会在目的线程中得以执行。再举个栗子吧,这是我在《Android编程权威指南》中看到的,它是这么描述线程通信的:两个线程的通信就好像现实中的两个人通过信件来通信,消息队列(MessageQueue)相对于通信时候的信箱;Message(消息)相当于信件;Looper相当于邮递员,它是MessageQueue的操作者;Handler时线程通信的发出者和处理者;每当Thread想要进行通信,它会让Handler投递一个Message给相应的MessageQueue,Looper会一直循环将MessageQueue里的Message发向它的目的地,到达目的地后Looper通知相应的Handler来处理消息。

  每一个Handler都是和唯一的Looper对象绑定的,也就是说一个Handler既仅可以个一个Looper绑定,但一个Looper可以有好几个Handler与之关联。Looper操作的MessageQueue是Handler取得消息进行处理和发出消息的地方。

  3)使用介绍:

  前面说过每一个Handler都是和特别的Looper绑定好的,同时Handler又是处理消息的地方,所以Handler中既要说明和哪个Looper绑定,又要告知怎么处理消息。所以Handler有4个构造方法,下面我来一一介绍:

  • Handler()这是无参数构造方法,它默认和当前线程的Looper绑定,未指定消息的处理方式。
  • Handler(Looper looper)它需要一个Looper对象作为参数传入,构成出来的Handler对象将和给定的Looper对象绑定。
  • Handler(Handler.Callback callback)它需要一个Handler.Callback对象作为参数传入,构造处理的对象和当前线程的Looper绑定并用传入的Handler.Callback对象来处理消息。
  • Handler(Looper looper,(Handler.Callback callback)这是第二种和第三种的结合,构成出一个和指定Looper绑定并用指定Callback的回调方法处理消息的Handler对像
  • 还有一种使用Handler的方法就是自己拓展Handler类,在子类中实现handlerMessage(Message msg)(这就是接口Callback中的抽象方法),这也是我用的比较多的方式。

  Handler的使用就是发送和处理消息,处理消息是在Callback接口中定义好的,当Looper操作的MessageQueue中有消息时,Looper对通知所有与它绑定的Handler调用handlerMessage(Message msg)去处理消息。那怎么发送消息呢?Hander中有一个方法叫sendMessage(Message msg),当调用这个方法后Handler就往Looper操作的MessageQueue中投递一个Message对象,发送消息就算是完成了。简单吧?so easy!

  关于Handler的其他方法还请查看文档,将主干的部分弄清楚了,指端末节随着使用慢慢就熟络了。一口气吃不成胖子。对了,下面的Message部分还会提及Handler的一些内容。

  3.Message基础:

  1)参考文档:http://developer.android.com/reference/android/os/Message.html

  2)使用介绍:

  线程通信既然叫通信肯定有一个消息的载体,Message就是这个消息的载体,它包含了线程想要通信的全部内容,比如线程运行后得到的结果就和可以包含在Message中。关于Message的构造本来可以按正常的方式构造(就是new一个Message对象,词穷不知道怎么说,就叫它”正常的方式“O(∩_∩)O~),但官方推荐的做法是通过Handler.obtainMessage()获得Message对象,因为这样的Message是从即将被回收的Message中取得的,会避免GC的反复运行和减少运行时的内存消耗。

  Message是消息的携带者,它有许多的携带消息的方法,比如setData(Bundle),Message.obj,Message.arg1,Message.arg2等等,需要特别说明的是Message里有一个公开的整形的全局变量what,即Message.what,它一般用来阐述消息的类型,Handler的handlerMessage(Message msg)通常会先检验Message的what变量,然后在决定如何处理。毕竟一个应用中和主线程通信的不可能只用一个线程,一种消息。

  4.Looper与MessageQueue简单介绍:

  1)参考文档:

  Looper:http://developer.android.com/reference/android/os/Looper.html

  MessageQueue:http://developer.android.com/reference/android/os/MessageQueue.html

  2)简单介绍:

  需要注意的地方就是,一个普通Thread创建时是没有Looper对象和它关联的,我们必须在线程的创建中进行关联,具体做法就是在Thread的创建时调用Looper.prepare()进行绑定,调用Looper.loop()使得与线程绑定的Looper对象开始工作。Looper中有一个巨好用的方法,Looper.getMainLooper(),这个方法直接返回当前应用的UI线程的Looper对象,有了Looper对象就可以往主线程发消息了,一会我在示例中会用到这样方法。

  关于MessageQueue,其实在线程通信中我们并不直接使用它,只需知道我们通过Handler发送出去的消息都是放在它里的就行了,它是一个”第层次“的对象,我们也不能直接往它里添加消息。但对于理解整个线程通信过程还是很重要的。

  5.实践——示例

  说了这么多,是时候用实践检验了!先说说我打算怎么做吧!我打算从UI线程向非UI线程(是不是叫主线程和子线程更好?)发一个消息,然后在非UI线程中处理这个消息(这里我只是打印一下日志),然后从非UI线程向主线程发送一个消息,然后在UI线程中处理这个消息(也是简单的打印一下)。好像有些偷懒,但真正使用也大致是这样的,就是把打印换成具体的任务。好了,开始吧!

  先给出布局吧!

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:paddingBottom="@dimen/activity_vertical_margin"
  8. android:paddingLeft="@dimen/activity_horizontal_margin"
  9. android:paddingRight="@dimen/activity_horizontal_margin"
  10. android:paddingTop="@dimen/activity_vertical_margin"
  11. tools:context=".MainActivity">
  12.  
  13. <TextView
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_alignParentTop="true"
  17. android:layout_centerHorizontal="true"
  18. android:text="Thread和Handler"/>
  19.  
  20. <Button
  21. android:id="@+id/send_message"
  22. android:layout_width="match_parent"
  23. android:layout_height="wrap_content"
  24. android:layout_alignParentBottom="true"
  25. android:text="发送消息"/>
  26.  
  27. <Button
  28. android:id="@+id/startThread"
  29. android:layout_width="match_parent"
  30. android:layout_height="wrap_content"
  31. android:layout_above="@id/send_message"
  32. android:text="开启子线程"/>
  33.  
  34. </RelativeLayout>

  布局很简单,就是一个textview和两个button,一个button开启线程,并接受来自子线程的信息,另一个从主线程发出消息给子线程。

  下面给出代码逻辑:

  第一部分是子线程的代码:

  1. package comfallblank.github.threadandhandler;
  2.  
  3. import android.os.Handler;
  4. import android.os.Looper;
  5. import android.os.Message;
  6. import android.util.Log;
  7.  
  8. /**
  9. * Created by fallb on 2015/10/7.
  10. */
  11. public class MyThread extends Thread {
  12. public static final int MSG_WORKER_THREAD = 100;
  13. private static final String TAG = "MyThread";
  14.  
  15. private Handler mWorkerHandler;
  16. private Handler mMainHandler;
  17.  
  18. public MyThread(Handler handler) {
  19. mMainHandler = handler;
  20. mWorkerHandler = new Handler(){
  21. @Override
  22. public void handleMessage(Message msg) {
  23. if(msg.what == MainActivity.MSG_MAIN){
  24. Log.d(TAG,"Message:"+msg.obj);
  25. }
  26. }
  27. };
  28. }
  29.  
  30. @Override
  31. public void run() {
  32. Looper.prepare();
  33. Message msg = mMainHandler.obtainMessage();
  34. msg.what = MyThread.MSG_WORKER_THREAD;
  35. msg.obj="子线程发出的消息";
  36. mMainHandler.sendMessage(msg);
  37.  
  38. Looper.loop();
  39. }
  40.  
  41. public Handler getWorkerHandler() {
  42. return mWorkerHandler;
  43. }
  44. }

  第二份是主线程的:

  1. package comfallblank.github.threadandhandler;
  2.  
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.support.v7.app.AppCompatActivity;
  7. import android.util.Log;
  8. import android.view.View;
  9. import android.widget.Button;
  10.  
  11. public class MainActivity extends AppCompatActivity {
  12. public static final int MSG_MAIN = 100;
  13. private static final String TAG = "MainActivity";
  14.  
  15. private Button mStartThread;
  16. private Button mSendMessage;
  17. private Handler mHandler = new MyHandler();
  18.  
  19. @Override
  20. protected void onCreate(Bundle savedInstanceState) {
  21. super.onCreate(savedInstanceState);
  22. setContentView(R.layout.activity_main);
  23.  
  24. final MyThread thread = new MyThread(mHandler);
  25. mStartThread = (Button) findViewById(R.id.startThread);
  26. mStartThread.setOnClickListener(new View.OnClickListener() {
  27. @Override
  28. public void onClick(View view) {
  29. Log.d(TAG, "开启线程");
  30. thread.start();
  31. }
  32. });
  33. mSendMessage = (Button) findViewById(R.id.send_message);
  34. mSendMessage.setOnClickListener(new View.OnClickListener() {
  35. @Override
  36. public void onClick(View view) {
  37. Handler workerHandler = thread.getWorkerHandler();
  38. Message msg = workerHandler.obtainMessage();
  39. msg.what = MSG_MAIN;
  40. msg.obj = "来自主线程的消息";
  41. workerHandler.sendMessage(msg);
  42. }
  43. });
  44.  
  45. }
  46.  
  47. class MyHandler extends Handler {
  48. @Override
  49. public void handleMessage(Message msg) {
  50. if (msg.what == MyThread.MSG_WORKER_THREAD) {
  51. Log.d(TAG,"Message:"+msg.obj);
  52. }
  53. }
  54. }
  55. }

  Logcat打印日志如下:

  10-07 19:34:34.424 19671-19671/comfallblank.github.threadandhandler D/MainActivity: 开启线程
  10-07 19:34:34.454 19671-19671/comfallblank.github.threadandhandler D/MainActivity: Message:子线程发出的消息
  10-07 19:34:37.267 19671-19671/comfallblank.github.threadandhandler D/MyThread: Message:来自主线程的消息

  6.关于Handler再说两句:

  Handler对象除了发出消息外,还有一族方法,来发出一个Runnable对象,Handler.post(Runnable runnable)及时其中的一个,它向MessageQueue中添加这个Runnable对象,然后目的线程的MessageQueue会执行该方法。

  关于Thread、Handler的内容就说到这里了,还是如前上一篇所说,本文只起到抛砖引玉的作用,多有偏颇,往交流指正。

Android多线程(二)的更多相关文章

  1. Android多线程分析之二:Thread的实现

    Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处   在前文<Android多线程分析之一 ...

  2. android程序---->android多线程下载(二)

    上篇我们讲到了android中下载的断点续传问题,今天我们开始学习下载的多线程问题.本次的多线程源码下载:androdi中多线程下载的实现代码.有关断点续传的问题,请参见博客:android程序--- ...

  3. Android多线程分析之四:MessageQueue的实现

    Android多线程分析之四:MessageQueue的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前面两篇文章<Androi ...

  4. Android多线程分析之三:Handler,Looper的实现

    Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多 ...

  5. android程序---->android多线程下载(一)

    多线程下载是加快下载速度的一种方式,通过开启多个线程去执行一个任务,可以使任务的执行速度变快.多线程的任务下载时常都会使用得到断点续传下载,就是我们在一次下载未结束时退出下载,第二次下载时会接着第一次 ...

  6. Android多线程----异步消息处理机制之Handler详解

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  7. android 多线程断点续传下载

    今天跟大家一起分享下Android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基 ...

  8. android多线程-AsyncTask之工作原理深入解析(下)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  9. android多线程-AsyncTask之工作原理深入解析(上)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  10. Android多线程源码学习笔记一:handler、looper、message、messageQueue

    最近在学习Android多线程相关知识的源码,现在把自己的笔记整理一下,写出来加深印象. Android多线程通讯的核心是handler.looper.message.messageQueue,这篇文 ...

随机推荐

  1. 毕设一:python 爬取苏宁的商品评论

    毕设需要大量的商品评论,网上找的数据比较旧了,自己动手 代理池用的proxypool,github:https://github.com/jhao104/proxy_pool ua:fake_user ...

  2. spark action之countbykey

    java public class CountByKeyDemo { private static SparkConf conf = new SparkConf().setMaster("l ...

  3. 常见的面试C#技术题目

    遍历查询窗体界面的textbox为空值 ? foreach (System.Windows.Forms.Control control in this.Controls)            {   ...

  4. 手动安装MySQL8.0

    首先跟大家唠一唠家常,随着MySQL迅速的更新,MySQL突飞猛进已经更新到了8.0版本,那么它和我们之前用的5.X版本有什么明显的区别那? 首先给大家看下MySQL5.X自带表的查询速度 之后献上M ...

  5. 英文构词法 —— ant、ent 后缀

    1. -ant:--人 accountant:会计, account(ac+count):计数,账户: assistant:助手: assist:帮助 descendant:后裔: descend:下 ...

  6. windows server 安装 mysql – 畅玩Coding

    原文:windows server 安装 mysql – 畅玩Coding windows server 安装 mysql 2018年12月11日2018年12月11日 admin 下载:https: ...

  7. poj1639 Picnic Planning,K度限制生成树

    题意: 矮人虽小却喜欢乘坐巨大的轿车,车大到能够装下不管多少矮人.某天,N(N≤20)个矮人打算到野外聚餐.为了集中到聚餐地点,矮人A 要么开车到矮人B 家中,留下自己的轿车在矮人B 家,然后乘坐B ...

  8. 根坤 eclipse配置android开发环境并搭建第一个helloWord工程

    一.搭建Android在eclipse下环境    一.JDK(不用安装  下载地址: http://www.xp510.com/xiazai/Application/program/23625.ht ...

  9. 数字电路基本概念 —— fan-in/fan-out

    0. 从模拟电路到数字电路 数字电路抗干扰能力强: 模拟电路会随着信号的传输而放大,这是因为模拟电路中信号几乎完全将真实信号按比例表现为电压或者电流的形式: 模拟电路是数字电路的基础 74LS283 ...

  10. ik_max_word ik_smart

    打开 ~/es_root/config/elasticsearch.yml 文件,加入以下配置: index: analysis: analyzer: ik_syno: type: custom to ...