Android的UI主线程主要负责初始化屏幕组件以及负责处理用户的按键事件等,如果主线程长时间被阻塞,会导致UI界面停止响应(大约是5s),也就是我们常说的ANR异常(现在Android中)。因此建议将耗时的操作放在子线程中完成,但是有时候子线程可能需要动态的更新UI组件,而UI线程是线程不安全的,因此Google禁止在子线程中更新UI组件,为了解决这个问题,Android提出了一下几种方案:
使用Handler实现线程通信
Activity.runOnUiThread(Runable);
View.post(Runable);
View.postDelayed(Runable,long)
AsyncTask
(注意2、3、4比较繁琐)
AsyncTask(异步任务)更轻量,不需要借助线程和Handler即可实现。
这里我们重点只谈第一种方式——Handler。在使用Handler之前,我们先简单的了解几个概念。
1、MessageQueue:
是一种数据结构,就是一个消息队列,存放消息Message的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。创建一个线程的时候,并不会自动创建其MessageQueue。通常使用一个Looper对象对该线程的MessageQueue进行管理。但在主线程创建Handler是,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程也就是子线程,则不会自动创建Looper,因此在创建Handler之前需要先调用Looper儿的prepare函数。
下面我们简单的看一下Looper源代码:
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }
</pre><p></p><p style="color:rgb(0,0,0); font-family:'Times New Roman',serif; font-size:10.5pt; font-style:normal; margin-top:0cm; margin-bottom:0pt"></p><p style="font-weight:normal"><strong></strong></p><p><strong>2、<span style="color:red">Message</span>:</strong></p><p style="font-weight:normal">Handler接受和处理的消息对象,线程之间通过Message来通信,同时也是MessageQueue中的存放的对象。一个MessagQueue中包含多个Message。通常使用Message类里的静态方法obtain()来获取Message实例对象,<span style="font-family:'Times New Roman',serif; font-size:14px">obtain()</span>方法有多个重载版本可供选择;要注意的是Message的设计基于消息池,在获取Message对象时,首先先从<span style="color:red">Message Pool(</span><span style="color:red">消息池)</span>中查看是否有可用的Message实例,存在则直接取出返回这个实例。如果Message Pool中没有可用的Message实例,则才用给定的参数创建一个Message对象。此外,也可以通过Handler对象的obtainMessage()获取一个Message实例。 </p><p><strong><span style="color:red">3</span><span style="color:red">、Looper</span>:</strong> </p><p>MessageQueue的管理者,它会不断的从MessageQueue中取出Message,并将Message分发到指定的子线程。每一个MessageQueue都绑定了一个Looper,也就是说MessageQueue不能脱离Looper存在,而Looper对象的创建则是通过Looper.<span style="color:red">prepare()</span><span style="color:red">函数</span>来实现的。同时每一个Looper对象 和一个线程关联(每个线程只能拥有一个Looper,一个Looper管理一个MessageQueue,而一个Looper可以被多个线程拥有)。在Handler中是通过调用Looper.myLooper()可以获得当前线程的Looper对象 。创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有<span style="font-family:'Times New Roman',serif; font-size:14px">Looper</span>对象的,所以,不能接受Message。(主线程中,系统已经初始化了一个Looper对象并关联了主线程,因此程序中直接创建Handler即可)子线程中如需要使用Handler,则必须通过prepare0来创建一个Looper对象并绑定当前子线程,这样该线程就有了自己的Looper对象和MessageQueue数据结构了。</p><p> Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()将其放入Message Pool中。 </p><p style="color:rgb(0,0,0); font-family:'Times New Roman','serif'; font-size:10.5pt; font-style:normal; font-weight:normal; margin-top:0cm; margin-bottom:0pt"><span style="font-family:黑体; font-size:12pt"></span></p><pre name="code" class="java" style="font-size: 14px;"> public static void prepare() { if (sThreadLocal.get() != null) {//一个线程只能有一个Looper throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper());//线程关联Looper }
4、Handler:
其作用有两个:发送消息和处理消息。发送消息:调用Handler的sendMessage发送消息Message,被Handler发送的消息Message必须被送到指定的MessageQueue。
处理消息:当Looper对象看到MessageQueue中含有Message,就将其广播到指定的Handler所在的子线程。该Handler对象收到该消息后,调用相应的handler对象的handleMessage(Message msg)方法对其进行处理。
注意:如果希望Handler正常工作,必须在当前线程中有一个Looper对象,因此如果要在自己的子线程中处理消息,必须首先为子线程创建一个looper对象(Loooper.prepare()),然后初始化Handler并重写handleMessage(Message msg) 方法,最后启动通过Looper.loop()来启动Looper。
最后我们用一张图来展示他们之间的关系:
现在我们来总结一下Handler的使用流程:
1、调用Looper.prepare(),为当前的线程创建Looper对象,在创建Looper对象的时候,会自动创建MessageQueue。
2、之后,创建Handler子类 的实例,重写handleMessage()方法,该方法用来处理来自于其他线程的消息。
3、调用Looper的loop()方法启动Looper。
(注意:主线程中只需要第2步)
比如说A线程需要传递数据给B线程,流程如下:
1、在B线程中首先调用Looper.prepare。(主线程不需要)
2、 编写Handler类,重写其中的handleMessage方法。
3、在B线程调用Looper.loop启动Looper.
4、创建Handler实例,在A线程中调用Handler的sentMessage方法发送消息。
附上简单的代码:
主线程Handler的定义:
mainHandler = new Handler() { private ImageView image; @Override public void handleMessage(Message msg) { //逻辑代码 } } };
class ConnURLThread extends Thread { private Handler subHandler; @Override public void run() { Looper.prepare(); subHandler = new Handler() { @Override public void handleMessage(Message msg) { //逻辑代码 } } }; Looper.loop(); } }