做网站设计能赚钱吗,定制车需要多少钱,西安知名的网站建设公司,百度做销售网站多少钱1. 简介
Handler是一套 Android 消息传递机制,主要用于线程间通信。
用最简单的话描述#xff1a; handler其实就是主线程在起了一个子线程#xff0c;子线程运行并生成Message#xff0c;Looper获取message并传递给Handler#xff0c;Handler逐个获取子线程中的Message.…1. 简介
Handler是一套 Android 消息传递机制,主要用于线程间通信。
用最简单的话描述 handler其实就是主线程在起了一个子线程子线程运行并生成MessageLooper获取message并传递给HandlerHandler逐个获取子线程中的Message.
Binder/Socket用于进程间通信而Handler消息机制用于同进程的线程间通信
可以说只要有异步线程与主线程通信的地方就一定会有 Handler。
在多线程的应用场景中将工作线程中需更新UI的操作信息 传递到 UI主线程从而实现 工作线程对UI的更新处理最终实现异步消息的处理 使用Handler消息传递机制主要是为了多个线程并发更新UI的同时保证线程安全 2. 相关概念解释
Handler、Message、Message Queue、Looper
Message 代表一个行为what或者一串动作Runnable, 每一个消息在加入消息队列时,都有明确的目标HandlerThreadLocal 线程本地存储区Thread Local Storage简称为TLS每个线程都有自己的私有的本地存储区域不同线程之间彼此不能访问对方的TLS区域。ThreadLocal的作用是提供线程内的局部变量TLS,这种变量在线程的生命周期内起作用,每一个线程有他自己所属的值(线程隔离)MessageQueue (C层与Java层都有实现) 以队列的形式对外提供插入和删除的工作, 其内部结构是以双向链表的形式存储消息的Looper (C层与Java层都有实现) Looper是循环的意思,它负责从消息队列中循环的取出消息然后把消息交给Handler处理 Handler 消息的真正处理者, 具备获取消息、发送消息、处理消息、移除消息等功能 Android消息机制 以Handler的sendMessage方法为例当发送一个消息后会将此消息加入消息队列MessageQueue中。Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。在Handler的handleMessage方法中处理该消息这就完成了一个消息的发送和处理过程。
Handler示意图 消息机制的模型
Message需要传递的消息可以传递数据MessageQueue消息队列但是它的内部实现并不是用的队列实际上是通过一个单链表的数据结构来维护消息列表因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)Handler消息辅助类主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)Looper不断循环执行(Looper.loop)从MessageQueue中读取消息按分发机制将消息分发给目标处理者。
消息机制的架构
在子线程执行完耗时操作当Handler发送消息时将会调用 MessageQueue.enqueueMessage 向消息队列中添加消息。当通过 Looper.loop 开启循环后会不断地从线程池中读取消息即调用 MessageQueue.next然后调用目标Handler即发送该消息的Handler的 dispatchMessage 方法传递消息然后返回到Handler所在线程目标Handler收到消息调用 handleMessage 方法接收消息处理消息。
3. Handler 的基本使用
3.1 创建 Handler
Handler 允许我们发送延时消息如果在延时期间用户关闭了 Activity那么该 Activity 会泄露。
这个泄露是因为 Message 会持有 Handler而又因为 Java 的特性内部类会持有外部类使得 Activity 会被 Handler 持有这样最终就导致 Activity 泄露。
解决方案将 Handler 定义成静态的内部类在内部持有 Activity 的弱引用并及时移除所有消息。
public class HandlerActivity extends AppCompatActivity {private Button bt_handler_send;private static class MyHandler extends Handler {//弱引用持有HandlerActivity , GC 回收时会被回收掉private WeakReferenceHandlerActivity weakReference;public MyHandler(HandlerActivity activity) {this.weakReference new WeakReference(activity);}Overridepublic void handleMessage(Message msg) {HandlerActivity activity weakReference.get();super.handleMessage(msg);if (null ! activity) {//执行业务逻辑Toast.makeText(activity,handleMessage,Toast.LENGTH_SHORT).show();}}}Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.HandlerActivity);//创建 Handlerfinal MyHandler handler new MyHandler(this);bt_handler_send findViewById(R.id.bt_handler_send);bt_handler_send.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) {new Thread(new Runnable() {Overridepublic void run() {//使用 handler 发送空消息handler.sendEmptyMessage(0);}}).start();}});}Overrideprotected void onDestroy() {//移除所有回调及消息myHandler.removeCallbacksAndMessages(null);super.onDestroy();}
}3.2 Message 获取
获取 Message 大概有如下几种方式
Message message myHandler.obtainMessage(); //通过 Handler 实例获取
Message message1 Message.obtain(); //通过 Message 获取
Message message2 new Message(); //直接创建新的 Message 实例通过查看源码可知Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法
public final Message obtainMessage()
{return Message.obtain(this);
}通过查看 Message 的 obtain 方法
public static Message obtain(Handler h) {//调用下面的方法获取 MessageMessage m obtain();//将当前 Handler 指定给 message 的 target 用来区分是哪个 Handler 的消息m.target h;return m;}//从消息池中拿取 Message如果有则返回否则创建新的 Message
public static Message obtain() {synchronized (sPoolSync) {if (sPool ! null) {Message m sPool;sPool m.next;m.next null;m.flags 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}为了节省开销我们在使用的时候尽量复用 Message使用前两种方式进行创建。
3.3 Handler 发送消息
Handler 提供了一些列的方法让我们来发送消息如 send()系列 post()系列,post方法需要传入一个Runnalbe对象 ,我们来看看post方法源码 public final boolean post(Runnable r){return sendMessageDelayed(getPostMessage(r), 0);}不过不管我们调用什么方法最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。 以 sendEmptyMessage(int) 方法为例
//Handler
sendEmptyMessage(int)- sendEmptyMessageDelayed(int,int)- sendMessageAtTime(Message,long)- enqueueMessage(MessageQueue,Message,long)- queue.enqueueMessage(Message, long);从中可以发现 MessageQueue 这个消息队列负责消息的入队出队。
4. 两个实例主线程-子线程
4.1 子线程向主线程
首先我们在MainActivity中添加一个静态内部类并重写其handleMessage方法。
private static class MyHandler extends Handler {private final WeakReferenceMainActivity mTarget;public MyHandler(MainActivity activity) {mTarget new WeakReferenceMainActivity(activity);}Overridepublic void handleMessage(NonNull Message msg) {super.handleMessage(msg);HandlerActivity activity weakReference.get();super.handleMessage(msg);if (null ! activity) {//执行业务逻辑if (msg.what 0) {Log.e(myhandler, change textview);MainActivity ma mTarget.get();ma.textView.setText(hahah);}Toast.makeText(activity,handleMessage,Toast.LENGTH_SHORT).show();}}}然后创建一个类型为MyHandler的私有属性
private Handler handler1 new MyHandler(this);最后在onCreate回调中创建一个线程用于接收发送消息
new Thread(new Runnable() {Overridepublic void run() {handler1.sendEmptyMessage(0);}}).start();总结一下
Handler的子类对象一般是在主线程中进行创建以便在两个线程中都能访问。我们创建了Handler类的子类MyHandler并重写了handlerMessage方法这个方法是当使用接收处理发送的消息的。然后我们创建了一个子线程在子线程中我们使用MyHandler的对象调用sendEmptyMessage方法发送了一个空的Message。然后我们就能在主线程中接收到这个数据。
4.2 主线程向子线程
首先创建一个MyHandler类。
private static class MyHandler extends Handler {Overridepublic void handleMessage(NonNull Message msg) {super.handleMessage(msg);if (msg.what 0) {Log.e(child thread, receive msg from main thread);}}}声明一个Handler类型的私有变量进行默认初始化为null。
private Handler handler1;创建子线程使handler指向新创建的MyHandler对象。
new Thread(new Runnable() {Overridepublic void run() {Looper.prepare();handler1 new MyHandler();Looper.loop();Log.e(child thread, child thread end);}}).start();在主线程中向子线程中发送消息
while (handler1 null) {}handler1.sendEmptyMessage(0);handler1.getLooper().quitSafely();5. Handler机制原理
5.1 Handler原理概述
普通的线程是没有looper的如果需要looper对象那么必须要先调用Looper.prepare方法而且一个线程只能有一个looper
Handler是如何完成跨线程通信的
Android中采用的是Linux中的 管道通信 关于管道简单来说管道就是一个文件在管道的两端分别是两个打开文件文件描述符这两个打开文件描述符都是对应同一个文件其中一个是用来读的别一个是用来写的 消息队列创建时 调用JNI函数初始化NativeMessageQueue对象。NativeMessageQueue则会初始化Looper对象Looper的作用就是当Java层的消息队列中没有消息时就使Android应用程序主线程进入等待状态而当Java层的消息队列中来了新的消息后就唤醒Android应用程序的主线程来处理这个消息 Handler通过sendMessage()发送Message到MessageQueue队列Looper通过loop()不断提取出达到触发条件的Message并将Message交给target来处理经过dispatchMessage()后交回给Handler的handleMessage()来进行相应地处理将Message加入MessageQueue时处往管道写入字符可以会唤醒loop线程如果MessageQueue中- 没有Message并处于Idle状态则会执行IdelHandler接口中的方法往往用于做一些清理性地工作
5.2 Handler与管道通信
管道其本质是也是文件但又和普通的文件会有所不同管道缓冲区大小一般为1页即4K字节。管道分为读端和写端读端负责从管道拿数据当数据为空时则阻塞写端向管道写数据当管道缓存区满时则阻塞。
在Handler机制中Looper.loop方法会不断循环处理Message其中消息的获取是通过 Message msg queue.next(); 方法获取下一条消息。该方法中会调用nativePollOnce()方法这便是一个native方法再通过JNI调用进入Native层在Native层的代码中便采用了管道机制。我们知道线程之间内存共享通过Handler通信消息池的内容并不需要从一个线程拷贝到另一个线程因为两线程可使用的内存时同一个区域都有权直接访问当然也存在线程私有区域ThreadLocal这里不涉及。即然不需要拷贝内存那管道是何作用呢Handler机制中管道作用就是当一个线程A准备好Message并放入消息池这时需要通知另一个线程B去处理这个消息。线程A向管道的写端写入数据1对于老的Android版本是写入字符W管道有数据便会唤醒线程B去处理消息。管道主要工作是用于通知另一个线程的这便是最核心的作用。 为什么采用管道而非Binder
从内存角度通信过程中Binder还涉及一次内存拷贝handler机制中的Message根本不需要拷贝本身就是在同一个内存。Handler需要的仅仅是告诉另一个线程数据有了。从CPU角度为了Binder通信底层驱动还需要为何一个binder线程池每次通信涉及binder线程的创建和内存分配等比较浪费CPU资源。
5.3 Handler举例详解
Handler是Android消息机制的上层接口。Handler的使用过程很简单通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下Handler的使用场景就是更新UI
如下就是使用消息机制的一个简单实例
public class Activity extends android.app.Activity {private Handler mHandler new Handler(){Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);System.out.println(msg.what);}};Overridepublic void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {super.onCreate(savedInstanceState, persistentState);setContentView(R.layout.activity_main);new Thread(new Runnable() {Overridepublic void run() {...............耗时操作Message message Message.obtain();message.what 1;mHandler.sendMessage(message);}}).start();}
}再举个例子怎么从主线程发送消息到子线程虽然这种应用场景很少
Thread thread new Thread(){Overridepublic void run() {super.run();//初始化Looper,一定要写在Handler初始化之前Looper.prepare();//在子线程内部初始化handler即可发送消息的代码可在主线程任意地方发送handlernew Handler(){Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);//所有的事情处理完成后要退出looper即终止Looper循环//这两个方法都可以有关这两个方法的区别自行寻找答案handler.getLooper().quit();handler.getLooper().quitSafely();}};//启动Looper循环否则Handler无法收到消息Looper.loop();}};thread.start();//在主线程中发送消息handler.sendMessage();先来解释一下第一行代码Looper.prepare();先看看Handler构造方法
//空参的构造方法这个方法调用了两个参数的构造方法public Handler() {this(null, false);}//两个参数的构造方法
public Handler(Callback callback, boolean async) {mLooper Looper.myLooper();if (mLooper null) {throw new RuntimeException(Cant create handler inside thread that has not called Looper.prepare());}mQueue mLooper.mQueue;mCallback callback;mAsynchronous async;}Handler的构造方法中会验证Looper如果Looper为空那么会抛出空指针异常Handler在构造方法中还做了一件事将自己的一个全局消息队列对象mQueue指向了Looper中的消息队列即构造方法中的这行代码mQueue mLooper.mQueue;
第二行代码初始化了Hanlder并且重写HandleMessage方法没啥好讲的。
然后调用了Looper.loop()方法后面会解释。
我们先来看看最后一行代码handler.sendMessage(message) 的主要作用
先看看这行代码之后的代码执行流程 sendMessage之后代码通过图中所示的几个方法最终执行到了MessageQueue的enqueueMessage方法我们来就看看它
boolean enqueueMessage(Message msg, long when) {if (msg.target null) {throw new IllegalArgumentException(Message must have a target.);}synchronized (this) {msg.markInUse();msg.when when;Message p mMessages;boolean needWake;if (p null || when 0 || when p.when) {// New head, wake up the event queue if blocked.msg.next p;mMessages msg;needWake mBlocked;} else {needWake mBlocked p.target null msg.isAsynchronous();Message prev;for (;;) {prev p;p p.next;if (p null || when p.when) {break;}if (needWake p.isAsynchronous()) {needWake false;}}msg.next p; // invariant: p prev.nextprev.next msg;}// We can assume mPtr ! 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}MessageQueue是一个单向列表结构而MessageQueue 的 enqueueMessage方法主要做的事情就是将 Handler发送过来的 Message插入到列表中。调用handler.senMessage()方法的时候最终的结果只是将这个消息插入到了消息队列中
发送消息的工作已经完成那么Looper是什么时候取的消息来看看
public static void loop() {for (;;) {Message msg queue.next(); // might blockif (msg null) {// No message indicates that the message queue is quitting.return;}try {msg.target.dispatchMessage(msg);} }}这是一个死循环这个循环的目的是从MessageQueue中取出消息取消息的方法是MessageQueue.next()方法取出消息后调用message.target对象的dispatchMessage()方法分发消息循环跳出的条件是MessageQueue.next()方法返回了null
看到这里我们应该自然会想到message.target.是哪个对象dispatchMessage有什么作用
public final class Message implements Parcelable {/*package*/ int flags;/*package*/ long when;/*package*/ Bundle data;/*package*/ Handler target;/*package*/ Runnable callback;// sometimes we store linked lists of these things/*package*/ Message next;
}在Looper从MessageQueue中取出Message之后调用了Handler的dispatchMessage方法 这里我们不禁要问这个target指向了哪个Handler再来看看之前的enqueueMessage
//Handler的方法private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}第一行代码就是将message的target属性赋值为发送message的handler自身Looper取出消息后调用了发送消息的Handler的dispatchMessage方法并且将message本身作为参数传了回去。到此时代码的执行逻辑又回到了Handler中。
接着看handler的dispatchMessage方法
/***handler的方法* Handle system messages here.*/public void dispatchMessage(Message msg) {if (msg.callback ! null) {handleCallback(msg);} else {if (mCallback ! null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}看到这里面一个我们非常熟悉到方法了没有—handleMessage方法也是我们处理消息时候的逻辑。
最后再来一个系统的工作示意图 6. Handler用法android与java区别
6.1 刷新UI界面
使用java
new Thread( new Runnable() { public void run() { myView.invalidate(); }
}).start();可以实现功能刷新UI界面。但是这样是不行的因为它违背了单线程模型Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
ThreadHandler Handler来根据接收的消息处理UI更新。Thread线程发出Handler消息通知更新UI。
Handler myHandler new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case TestHandler.GUIUPDATEIDENTIFIER: myBounceView.invalidate(); break; } super.handleMessage(msg); } }; class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message new Message(); message.what TestHandler.GUIUPDATEIDENTIFIER; TestHandler.this.myHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
6.2 定时器延时操作
使用java 使用Java上自带的TimerTask类TimerTask相对于Thread来说对于资源消耗的更低引入import java.util.Timer; 和 import java.util.TimerTask;
public class JavaTimer extends Activity { Timer timer new Timer(); TimerTask task new TimerTask(){ public void run() { setTitle(hear me?); } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); timer.schedule(task, 10000); // 延迟delay毫秒后执行一次task。}
}TimerTask Handler
public class TestTimer extends Activity { Timer timer new Timer(); Handler handler new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case 1: setTitle(hear me?); break; } super.handleMessage(msg); } }; TimerTask task new TimerTask(){ public void run() { Message message new Message(); message.what 1; handler.sendMessage(message); } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); timer.schedule(task, 10000); // 延迟delay毫秒后执行一次task。}
} 6.3 定时更新 UI
Runnable Handler.postDelayed(runnable,time) private Handler handler new Handler(); private Runnable myRunnable new Runnable() { public void run() { if (run) { handler.postDelayed(this, 1000); count; } tvCounter.setText(Count: count); } }; 然后在其他地方调用
handler.post(myRunnable);
handler.post(myRunnable,time);6.4 Handler实现延迟执行
Handler延迟2s执行一个runnable
Handler handlernew Handler();
Runnable runnablenew Runnable() {Overridepublic void run() {// TODO Auto-generated method stubif(xsLayout.getVisibility()View.VISIBLE){xsLayout.setVisibility(View.GONE);}}
};
handler.postDelayed(runnable, 2000);在runnable被执行之前取消这个定时任务
handler.removeCallbacks(runnable);7. 总结
7.1 总结
Handler 的背后有着 Looper 以及 MessageQueue 的协助三者通力合作分工明确。
Looper 负责关联线程以及消息的分发在该线程下从 MessageQueue 获取 Message分发给Handler MessageQueue 是个队列负责消息的存储与管理负责管理由 Handler 发送过来的MessageHandler : 负责发送并处理消息面向开发者提供 API并隐藏背后实现的细节。
Handler 发送的消息由 MessageQueue 存储管理并由 Loopler 负责回调消息到 handleMessage()。
线程的转换由 Looper 完成handleMessage() 所在线程由 Looper.loop() 调用者所在线程决
7.2 注意
1一个线程有几个Handler
多个通常我们开发过程中就会new出不止一个Handler。
2一个线程有几个Looper如何保证
仅有1个LooperLooper的构造是私有的只有通过其prepare()方法构建出来当调用了Looper的prepare()方法后会调用ThreadLocal中的get()方法检查ThreadLocalMap中是否已经set过Looper如果有则会抛出异常提示每个线程只能有一个Looper如果没有则会往ThreadLocalMap中set一个new出来的Looper对象。这样可以保证ThreadLocalMap和Looper一一对应即一个ThreadLocalMap只会对应一个Looper。而这里的ThreadLocalMap是在Thread中的一个全局变量也只会有一个所以就可以保证一个Thread中只有一个Looper。
3Handler内存泄漏的原因
内部类持有外部的引用。Handler原理由于Handler可以发送延迟消息所以为了保证消息执行完毕后由同一个Handler接收到所以发送出去的Message中会持有Handler的引用这个引用存在Message的target字段中是Handler所有的sendMessage()方法最后都会调用enqueueMessage()而在enqueueMessage()中会给Message的target字段赋值this。因此Message持有Handler的引用Handler又持有Activity的引用所以在Message处理完之前如果Activity被销毁了就会造成内存泄漏。怎么解决可以使用static修饰Handler对象。
4为何主线程可以new Handler如果想要在子线程中new Handler要做些什么准备
因为在ActivityThread中的main()已经对Looper进行了prepar()操作所以可以直接在主线程new Handler。 android-28的SystemServer类中main方法是整个android应用的入口在子线程中调用Looper.prepare()是为了创建一个Looper对象并将该对象存储在当前线程的ThreadLocal中每个线程都会有一个ThreadLocal它为每个线程提供了一个本地的副本变量机制实现了和其它线程隔离并且这种变量只在本线程的生命周期内起作用可以减少同一个线程内多个方法之间的公共变量传递的复杂度。Looper.loop()方法是为了取出消息队列中的消息并将消息发送给指定的handler,通过msg.target.dispatchMassage()方法 /*** The main entry point from zygote.*/public static void main(String[] args) {new SystemServer().run();}private void run() {......Looper.prepareMainLooper();......}如果想在子线程中new Handler则需要先手动调用Looper的prepare()方法初始化Looper再调用Looper的loop()方法使Looper运转。 new Thread(new Runnable() {Overridepublic void run() {Looper.prepare(); //初始化Loopernew Handler(){Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};Looper.loop();}})5子线程中维护的Looper消息队列无消息的时候的处理方案是什么
如果不处理的话会阻塞线程处理方案是调用Looper的quitSafely()quitSafely()会调用MessageQueue的quit()方法清空所有的Message并调用nativeWake()方法唤醒之前被阻塞的nativePollOnce()使得方法next()方法中的for循环继续执行接下来发现Message为null后就会结束循环Looper结束。如此便可以释放内存和线程。
6内部是如何确保线程安全的
可以存在多个Handler往MessageQueue中添加数据发消息时各个Handler可能处于不同线程 添加消息的方法enqueueMessage()中有synchronize修饰取消息的方法next()中也有synchronize修饰。Handler的delay消息延迟消息时间准确吗 由于上述的加锁操作所以时间不能保证完全准确。
7使用Message时应该如何创建它
使用Message的obtain()方法创建直接new出来容易造成内存抖动。内存抖动是由于频繁new对象gc频繁回收导致而且由于可能被别的地方持有导致无法及时回收所以会导致内存占用越来越高。使用obtain()对内存复用可以避免内存抖动的发生。其内部维护了一个Message池其是一个链表结构当调用obtain()的时候会复用表头的Message然后会指向下一个。如果表头没有可复用的message则会创建一个新的对象这个对象池的最大长度是50。
8使用Handler的postDelay后消息队列会有什么变化
如果此时消息队列为空不会执行会计算消息需要等待的时间等待时间到了继续执行。
9Looper死循环为什么不会导致应用卡死
卡死就是ANR产生的原因有2个 1、在5s内没有响应输入的事件(例如按键触摸等)2、BroadcastReceiver在10s内没有执行完毕。 事实上我们所有的Activity和Service都是运行在loop()函数中以消息的方式存在所以在没有消息产生的时候looper会被block(阻塞)主线程会进入休眠一旦有输入事件或者Looper添加消息的操作后主线程就会被唤醒从而对事件进行响应所以不会导致ANR简单来说looper的阻塞表明没有事件输入而ANR是由于有事件没响应导致所以looper的死循环并不会导致应用卡死。
6.3 接下来
简介
在Android系统中我们执行完耗时操作都要另外开启子线程来执行执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作如果经常要开启线程接着又销毁线程这无疑是很消耗性能的。
HandlerThread是Google帮我们封装好的可以用来执行多个耗时操作而不需要多次开启线程里面是采用Handler和Looper实现的。
HanderThread实际上就是一个线程
怎么使用
//创建实例对象,该参数表示线程的名字
HandlerThread handlerThread new HandlerThread(myHandlerThread);//启动我们创建的HandlerThread线程
handlerThread.start();//怎样将Handler与线程对象绑定在一起
mThreadHandler new Handler(mHandlerThread.getLooper()) {Overridepublic void handleMessage(Message msg) {//发生myHandlerThread线程中checkForUpdate();if(isUpdate){mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);}}
};看个实例
public class MainActivity extends AppCompatActivity {private static final int MSG_UPDATE_INFO 0x100;Handler mMainHandler new Handler();private TextView mTv;private Handler mThreadHandler;private HandlerThread mHandlerThread;private boolean isUpdate true;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTv (TextView) findViewById(R.id.tv);initHandlerThread();}private void initHandlerThread() {mHandlerThread new HandlerThread(xujun);mHandlerThread.start();mThreadHandler new Handler(mHandlerThread.getLooper()){Overridepublic void handleMessage(Message msg) {checkForUpdate();if (isUpdate) {mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);}}};}/**
* 模拟从服务器解析数据
*/private void checkForUpdate() {try {//模拟耗时Thread.sleep(1200);mMainHandler.post(new Runnable() {Overridepublic void run() {String result 实时更新中当前股票行情fontcolorred%d/font;result String.format(result, (int) (Math.random() * 5000 1000));mTv.setText(Html.fromHtml(result));} });} catch (InterruptedException e) {e.printStackTrace();}}Overrideprotected void onResume() {isUpdate true;super.onResume();mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);}Overrideprotected void onPause() {super.onPause();isUpdate false;mThreadHandler.removeMessages(MSG_UPDATE_INFO);}
Overrideprotected void onDestroy() {super.onDestroy();mHandlerThread.quit();mMainHandler.removeCallbacksAndMessages(null);}
}运行以上测试代码将可以看到如下效果图(例子不太恰当主要使用场景是在handleMessage中执行耗时操作) 看看源码
基于sdk23的可以看到只有一百多行代码
public class HandlerThread extends Thread {int mPriority;int mTid -1;Looper mLooper;public HandlerThread(String name) {super(name);mPriority Process.THREAD_PRIORITY_DEFAULT;}public HandlerThread(String name, int priority) {super(name);mPriority priority;}
/**
* Call back method that can be explicitly overridden if nee
ded to execute some
* setup before Looper loops.
*/protected void onLooperPrepared() {}Overridepublic void run() {mTid Process.myTid();Looper.prepare();//持有锁机制来获得当前线程的Looper对象synchronized (this) {mLooper Looper.myLooper();//发出通知当前线程已经创建mLooper对象成功这里主要是通知getLooper方法中的waitnotifyAll();}//设置线程的优先级别Process.setThreadPriority(mPriority);//这里默认是空方法的实现我们可以重写这个方法来做一些线程开始之前的准备方便扩展onLooperPrepared();Looper.loop();mTid -1;}public Looper getLooper() {if (!isAlive()) {return null;}// 直到线程创建完Looper之后才能获得Looper对象Looper未创建成功阻塞synchronized (this) {while (isAlive() mLooper null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}public boolean quit() {Looper looper getLooper();if (looper ! null) {looper.quit();return true;}return false;}public boolean quitSafely() {Looper looper getLooper();if (looper ! null) {looper.quitSafely();return true;}return false;}
/**
* Returns the identifier of this thread. See Process.myTid(
).
*/public int getThreadId() {return mTid;}
}1首先看看构造函数 public HandlerThread(String name) {super(name);mPriority Process.THREAD_PRIORITY_DEFAULT;}public HandlerThread(String name, int priority) {super(name);mPriority priority;}一个参数的和两个参数的name代表当前线程的名称priority为线程的优先级别
2看一下run()方法 在run方法里面我们可以看到我们会初始化一个Looper并设置线程的优先级别 public void run() {mTid Process.myTid();Looper.prepare();//持有锁机制来获得当前线程的Looper对象synchronized (this) {mLooper Looper.myLooper();//发出通知当前线程已经创建mLooper对象成功这里主要是通知getLooper方法中的waitnotifyAll();}//设置线程的优先级别Process.setThreadPriority(mPriority);//这里默认是空方法的实现我们可以重写这个方法来做一些线程开始之前的准备方便扩展onLooperPrepared();Looper.loop();mTid -1;}前面我们说到使用HandlerThread的时候必须调用 start() 方法接着才可以将我们的HandlerThread和我们的handler绑定在一起
原因就是我们是在 run() 方法才开始初始化我们的looper而我们调用HandlerThread的 start() 方法的时候线程会交给虚拟机调度由虚拟机自动调用run方法
为什么要使用锁机制和 notifyAll()
原因我们可以从 getLooper() 方法中知道 public Looper getLooper() {if (!isAlive()) {return null;}// 直到线程创建完Looper之后才能获得Looper对象Looper未创建成功阻塞synchronized (this) {while (isAlive() mLooper null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;
}总结在获得mLooper对象的时候存在一个同步的问题只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完成同步问题。
3接着我们来看一下quit方法和quitSafe方法 public boolean quit() {Looper looper getLooper();if (looper ! null) {looper.quit();return true;}return false;}public boolean quitSafely() {Looper looper getLooper();if (looper ! null) {looper.quitSafely();return true;}return false;}跟踪这两个方法容易知道只两个方法最终都会调用MessageQueue的 quit(boolean safe) 方法
void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException(Main thread not allowedto quit.);}synchronized (this) {if (mQuitting) {return;}mQuitting true;//安全退出调用这个方法if (safe) {removeAllFutureMessagesLocked();} else {//不安全退出调用这个方法removeAllMessagesLocked();}// We can assume mPtr ! 0 because mQuitting was previously false.nativeWake(mPtr);}
}不安全的会调用 removeAllMessagesLocked(); 我们来看这个方法是怎样处理的其实就是遍历Message链表移除所有信息的回调并重置为null。
private void removeAllMessagesLocked() {Message p mMessages;while (p ! null) {Message n p.next;p.recycleUnchecked();p n;}mMessages null;
}安全地会调用 removeAllFutureMessagesLocked(); 这个方法它会根据Message.when这个属性判断我们当前消息队列是否正在处理消息没有正在处理消息的话直接移除所有回调正在处理的话等待该消息处理处理完毕再退出该循环。因此说 quitSafe() 是安全的而 quit() 方法是不安全的因为quit方法不管是否正在处理消息直接移除所有回调
private void removeAllFutureMessagesLocked() {final long now SystemClock.uptimeMillis();Message p mMessages;if (p ! null) {//判断当前队列中的消息是否正在处理这个消息没有的话直接移除所有回调if (p.when now) {removeAllMessagesLocked();} else {//正在处理的话等待该消息处理处理完毕再退出该循环Message n;for (;;) {n p.next;if (n null) {return;}if (n.when now) {break;}p n;}p.next null;do {p n;n p.next;p.recycleUnchecked();} while (n ! null);}}
}