
在AndroID开发中会经常在子线程中进行一些 *** 作,当 *** 作完成后会通过Handler发送一些数据到主线程,通知主线程做相应的 *** 作,Handler原理:子线程 Handler 主线程 其实构成了线程模型中的经典问题 生产者-消费者 模型。 生产者-消费者模型:生产者和消费者在同一时间段内用同一个存储空间,生产者往存储空间添加数据,消费者从存储空间中取走数据。
优点:保证数据生产消费的顺序(通过MessageQueue先进先出)-不管是生产者(子线程)还是消费者(主线程)都只依赖缓冲区(Handler),生产者和消费者之间不会相互持有,使他们之间没有任何耦合。
1.1Handler机制相关类Handler:发消息和收消息
Looper:用于轮寻消息队列,一个线程只能有一个Looper
Message : 消息实体
MessageQueue : 消息队列用于存储信息和管理消息
ThreadLocal : 为每个线程提供单独的存储空间
ThreadLocalmap: ThreadLocal的内部类,set方法控制 对于每一个ThreadLocal只有一个value(looper)
Thread: 线程中持有一个threadLocals变量(ThreadLocal.ThreadLocalmap)
类图
1.2创建Looper子线程:子线程中如果需要自己创建Looper调用Looper.prepare()方法,然后调用Looper.loop()方法。
主线程: 主线程ActivityThread中的main方法已经为我们创建了Looper
public static voID main(String[] args) { //省略其它代码....... Looper.prepareMainLooper(); //初始化Looper以及MessageQueue ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); //开始轮寻 throw new RuntimeException("Main thread loop unexpectedly exited");}Looper.prepareMainLooper()
public static voID prepareMainLooper() { prepare(false); // 消息队列不允许quit,并创建Main Looper synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}prepare(boolean quitAllowed)
private static voID prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) {//不为null表示当前线程已经创建了Looper throw new RuntimeException("Only one Looper may be created per thread"); //每个线程只能创建一个Looper } sThreadLocal.set(new Looper(quitAllowed)); //将looper存储到ThreadLocal中,这样get的时候就不会为null,并且MessageQueue不允许quit}1.3创建MessageQueue以及Looper与当前线程绑定private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); //创建MessageQueue mThread = Thread.currentThread(); //当前线程绑定}MessageQueue的构造方法
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; //mQuitAllowed决定了队列是否可以销毁,主线程的队列不可以被销毁需要传入false,在MessageQueue的quit方法中判断的 mPtr = nativeInit();}Looper.loop() 轮寻
public static voID loop() { final Looper me = myLooper(); //通过sThreadLocal.get()方法获取ThreadLocal中保存的Looper对象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //把Looper对象中的消息队列取出 // Make sure the IDentity of this thread is that of the local process, // and keep track of what that IDentity token actually is. Binder.clearCallingIDentity(); final long IDent = Binder.clearCallingIDentity(); for (;;) { //死循环,不断的去消息队列中读取消息 Message msg = queue.next(); // might block //刚创建的MessageQueue中是没有消息的,等到Handler sendMessage enqueueMessage后队列里才有消息。 if (msg == null) { // No message indicates that the message queue is quitting. return; //return之后会执行ActivityThread中main的 throw new RuntimeException(“Main thread loop unexpectedly exited”) //接着整个主线程就会退出了。所以就是说,在主线程中当loop()方法没有轮询到消息的时候。主线程就直接退出了 //所以在这里我们知道了Looper.loop()消息循环,一旦退出消息循环,那么应用也就退出了。 // 什么时候在进入呢?????? } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg);// msg.target就是绑定的Handler,调用Handler的dispatchMessage去处理消息 //后面代码省略...... }}创建Handler
最常见的handler创建方式
Handler handler = new Handler() { @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); }};创建方式2:在内部调用this(null, false)
public Handler(Callback callback, boolean async) { //前面代码省略...... mLooper = Looper.myLooper(); //获取ThreadLocal中的looper对象,子线程中使用Handler需要调用Looper.prapare,或者使用getMainLooper来使用主线程looper //否则会抛出RunTimeException if (mLooper == null) { throw new RuntimeException( "Can't create handler insIDe thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; //获取消息队列 mCallback = callback; // 初始化回调接口 mAsynchronous = async;}创建Message
可以直接new Message,但是更好的方式是Message.obtain. 因为可以检查是否有可以复用的Message,避免过多的创建、销毁Message对象达到优化内存和性能的目的。
public static Message obtain(Handler h) { Message m = obtain(); //Message重载的obtain方法 m.target = h; //Message的handler和我们的handler绑定 return m;}obtain()
public static Message obtain() { synchronized (sPoolSync) { //同步锁,保证线程安全 if (sPool != null) { Message m = sPool; //sPool就是Handler dispatchMessage后通过recycleUncheckd回收以复用的Message sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message();}Message和Handler的绑定
创建Message的时候可以通过Message.obtain(Handler h)这个构造方法进行绑定。当然在Handler中的enqueueMessage也进行了绑定,所有发送Message的方法都会调用此方法入队,所以在创建Message的时候可以不绑定
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //Message和Handler 进行绑定 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}Handler发送消息
Handler发送消息的重载方法有很多,常用的主要有两个。1.sendMessage(Message), 它通过一系列重载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用enqueueMessage,继续调用messageQueue的enqueueMessage方法,将Msg保存到消息队列中,最终由Looper取出,交给Handler的dispatchMessage进行处理。
dispatchMessage方法中,message中的callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断Handler自己的mCallback是否为null,mCallback在Handler构造方法中初始化,在主线程中直接通过无参构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。
public voID dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); //Msg中的Runnable是否为null, callback在message的构造方法中初始化或者时候使用handler.post(Runnable)时才不为null } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) {//mCallback是一个callback对象,通过无参构造函数创建出来的handler该属性为null,所以不执行 return; } } handleMessage(msg); //最终执行这里 }}private static voID handleCallback(Message message) { message.callback.run();}Handler处理消息
最后调用handleMessage(Message)来处理消息,整个Handler机制的流程就结束了。
总结
Handler sendMessage发送消息到消息队列MessageQueue, 然后looper调用自己的loop方法轮寻MessageQueue里面的每一个Message。当Message达到了可以执行的时候就开始执行,执行后调用Message绑定的Hander来处理消息。
整体流程如下图
2.Handler中的难点问题线程同步问题
Handler是用于线程之间通信的,但是它产生的根本不只是用于UI处理,而更多的是handler是真个app通信的框架,可以在ActivityThread里面感受到,整个app都是用它来进行线程见的协调。Handler是如何保证自己的线程安全的呢????
Handler机制里面最主要的类是MessageQueue,这个类是所有Message的存储仓库,消息的管理其实就两点:1.消息入库(enqueueMessage),2.消息出库(next)。所以只要在进出口确保线程安全就可以了。
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { //msg必须有绑定的Handler,否则不允许入列 throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { //当前的msg是否已经有其它Handler在使用 throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { //添加同步锁,保证在同一时刻只有一个Msg插入到MessageQueue中 if (mQuitting) { //当前的Thread时候已经被销毁 IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } 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 { // Inserted within the mIDdle of the queue. Usually we don't have to wake // up the event queue unless there is a barrIEr at the head of the queue // and the message is the earlIEst asynchronous message in the queue. 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.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (neeDWake) { nativeWake(mPtr); } } //锁结束 return true;}synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机。
sychronized(this) 说明对所有调用MessageQueue对象的线程来说,他们都是互斥的,然而在我们Handler里面,一个线程是对应着一个唯一的Looper对象,然而Looper中又只有一个唯一的MessageQueue,所以我们主线程就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其它都需要等待,这样消息队列就不会出现混乱。
next函数
Message next() { //上面代码省略....... for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { //读取消息时加锁,锁仍然是MessageQueue对象 // Try to retrIEve the next message. Return if found. final long Now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrIEr. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (Now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - Now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message Now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time IDle, then get the number of IDlers to run. // IDle handles only run if the queue is empty or if the first message // in the queue (possibly a barrIEr) is due to be handled in the future. if (pendingIDleHandlerCount < 0 && (mMessages == null || Now < mMessages.when)) { pendingIDleHandlerCount = mIDleHandlers.size(); } if (pendingIDleHandlerCount <= 0) { // No IDle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIDleHandlers == null) { mPendingIDleHandlers = new IDleHandler[Math.max(pendingIDleHandlerCount, 4)]; } mPendingIDleHandlers = mIDleHandlers.toArray(mPendingIDleHandlers); } //锁结束 //省略 后面代码........}enqueueMessage 和 next 函数,它们都使用了synchronized(this), 保证next函数和enqueueMessage函数能够实现互斥,保证多线程访问的时候messageQueue的有序进行
消息机制之同步屏障
同步屏障,vIEw绘制中用 同步屏障
在Handler机制中,线程的消息都是放在同一个MessageQueue里面,取消息的时候是互斥取消息,而且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行排序的。这时候存在一个问题,同一个时间范围内的消息,如果它是需要立刻执行的,怎么处理?? 按照常规的方法,我们需要等待队列轮寻到这条消息的时候才能执行,那就不符合需求了。所以需要给这条消息开一个绿色通道,这个绿色通道就是同步屏障的概念。
同步屏障是什么?
屏障字面意思就是阻碍的意思,同步屏障就是阻碍同步消息,只让异步消息通过。
如何开启同步屏障?
/** * @hIDe */ public int postsyncbarrIEr() { return postsyncbarrIEr(SystemClock.uptimeMillis()); } private int postsyncbarrIEr(long when) { // Enqueue a new sync barrIEr token. // We don't need to wake the queue because the purpose of a barrIEr is to stall it. synchronized (this) { final int token = mNextbarrIErToken++; //在消息池获取msg final Message msg = Message.obtain(); msg.markInUse(); //就是这里!!!!! 初始化msg对象的时候,并没有给target赋值,因此target == null msg.when = when; msg.arg1 = token; Message prev = null; Message p = mMessages; if (when != 0) { while (p != null && p.when <= when) { // 如果开启同步屏障的时间(假设为T)T不为0,且当前的同步消息里有时间小于T的,则prev也不为null prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next msg.next = p; //如果prev不为null,执行顺序就是prev -> msg -> p prev.next = msg; } else { msg.next = p; //如果prev为null,执行顺序就是msg-> p mMessages = msg; } return token; } }可以看到,Message对象初始化的时候并没有给target赋值,因此 target == null的来源就找到了。一条target == null的消息就进入了消息队列
那么开启了同步屏障之后,所谓的异步消息又是如何被处理的呢??
Handler消息机制,消息最终处理是在消息轮寻器Looper#loop()中,而loop()循环中会调用MessageQueue#next从消息队列中取出消息。
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application trIEs to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIDleHandlerCount = -1; // -1 only during first iteration //如果nextPollTimeoutMillis=-1, 一直阻塞不会超时 //如果nextPollTimeoutMillis=0, 不会阻塞,直接返回 //如果nextPollTimeoutMillis>0, 最长阻塞nextPollTimeoutMillis毫秒(超时) // 如果期间有程序唤醒会立即返回 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrIEve the next message. Return if found. final long Now = SystemClock.uptimeMillis(); //获取系统开机到现在的时间 Message prevMsg = null; Message msg = mMessages; //当前链表的头节点 //关键点!!!!!!!!!!!!!! 如果target == null,那么他就是屏障,需要循环遍历一直往后找到第一个异步消息 if (msg != null && msg.target == null) { // Stalled by a barrIEr. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { //如果有消息需要处理,先判断时间有没有到,如果没有到的话设置一下阻塞时间,场景如常用的postDelay if (Now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. //计算出离执行时间还要多久,然后赋值给nextPollTimeoutMillis //表示nativePollOnce方法要等待nextPollTimeoutMillils时长后返回 nextPollTimeoutMillis = (int) Math.min(msg.when - Now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message Now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time IDle, then get the number of IDlers to run. // IDle handles only run if the queue is empty or if the first message // in the queue (possibly a barrIEr) is due to be handled in the future. if (pendingIDleHandlerCount < 0 && (mMessages == null || Now < mMessages.when)) { pendingIDleHandlerCount = mIDleHandlers.size(); } if (pendingIDleHandlerCount <= 0) { // No IDle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIDleHandlers == null) { mPendingIDleHandlers = new IDleHandler[Math.max(pendingIDleHandlerCount, 4)]; } mPendingIDleHandlers = mIDleHandlers.toArray(mPendingIDleHandlers); } // Run the IDle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIDleHandlerCount; i++) { final IDleHandler IDler = mPendingIDleHandlers[i]; mPendingIDleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = IDler.queueIDle(); } catch (Throwable t) { Log.wtf(TAG, "IDleHandler threw exception", t); } if (!keep) { synchronized (this) { mIDleHandlers.remove(IDler); } } } // reset the IDle handler count to 0 so we do not run them again. pendingIDleHandlerCount = 0; // While calling an IDle handler, a new message Could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }从上面就可以看出,当消息队列开启同步屏障的时候(即标识为msg.target == null),消息机制在处理消息的时候有限处理异步消息,这样同步屏障就起到了一种过滤和优先级的作用。
如上图所示,在消息队列中有同步消息和异步消息(黄色部分)以及一道墙---同步屏障(红色部分)。有了同步屏障的存在,msg_2和msg_M这两个异步消息就可以优先处理,而后面的msg_3等同步消息则不会被处理。那么这些同步消息什么时候被处理??? 就需要先移除这个同步屏障removeSyncBarrier
同步屏障应用场景
更新VIEw时相关的消息即为异步消息,需要优先处理
voID scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalbarrIEr = mHandler.getLooper().getQueue().postsyncbarrIEr(); //开启同步屏障 //发送异步消息 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedinputdispatch) { scheduleConsumeBatchedinput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }postCallback最终走到
private voID postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis); } synchronized (mlock) { final long Now = SystemClock.uptimeMillis(); final long dueTime = Now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= Now) { scheduleFrameLocked(Now); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDulE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); //设置为异步消息类型 mHandler.sendMessageAtTime(msg, dueTime); } } }这样就开启了同步屏障,并且发送了异步消息,这样系统就会有限处理这些异步消息。
最后,在处理完成后需要移除同步屏障
voID unscheduleTraversals() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncbarrIEr(mTraversalbarrIEr); //移除同步屏障 mChoreographer.removeCallbacks( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } }一个线程有几个Handler
一个线程可以有n个Handler,可以new很多个,没有限制。
一个线程可以有几个Looper
一个线程只允许有一个looper,在子线程中创建looper的方法Looper.prepare 创建的looper对象保存在当前线程的ThreadLocal的ThreadLocalmap中(threadlocal, looper)键值对的形式存储下来。
同一个线程再次调用prepare时会抛出异常,并且map中存储时对于key threadlocal只有唯一的一个looper与之对应
Handler内存泄漏原因?为什么其它内部类没有说过这个问题
在java中非静态内部类默认可以访问外部类的所有变量和方法,因为非静态内部类持有了外部类的引用。还有匿名内部类
当我们在Activity中实现一个非静态Handler内部类,来处理一些延时事件,这种情况下就会出现内存泄漏的情况
当Activity销毁时,Handler还有延迟消息没有处理完,这时候因为Handler持有activity的引用,导致activity无法被回收,造成内存泄漏
//匿名内部类 Handler handler=new Handler(){ @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); } }; //非静态内部类 protected class AppHandler extends Handler { @OverrIDe public voID handleMessage(Message msg) { switch (msg.what) { // Todo: 2019/4/30 } } }@R_502_6120@:
1.在activity销毁时,清空Handler中正在执行或未执行的Callback和Message
// 清空消息队列,移除对外部类的引用 @OverrIDe protected voID onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); } //Handler源码中removeCallbacksAndMessages()注释含义 /** * Remove any pending posts of callbacks and sent messages whose * <var>obj</var> is <var>token</var>. If <var>token</var> is null, * all callbacks and messages will be removed. */ public final voID removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token); }2.声明为静态内部类,静态内部类不会持有外部类的引用,所以不会影响GC时activity的回收,然后在静态内部类中手动持有activity的弱引用,这样gc的时候即使被持有也会被回收
private static class AppHandler extends Handler { //弱引用,在垃圾回收时,被回收 WeakReference<Activity> activity; AppHandler(Activity activity){ this.activity=new WeakReference<Activity>(activity); } public voID handleMessage(Message message){ switch (message.what){ //todo } } }3.即使内存泄漏了,在handler处理完后,下次GC时也会回收本次未被回收的内存
其它内部类中一般不会有延时的 *** 作,所以没有遇到过内存泄漏的问题
主线程中可以直接new Handler, 子线程中如果想new Handler需要做哪些准备
1.因为主线程在ActivityThread的main函数中已经做了prepare,并且主线程的MessageQueue是不允许退出的
2.子线程中new 一个handler之前,需要Looper.prepare 这里面会创建一个looper对象,并将这个looper对象和当前子线程绑定
Looper.loop
Looper.quitSafely
子线程中处理完了消息就要调用quit 来跳出loop循环
Loop死循环为什么不会导致应用卡死
应用卡死也就是ANR的原因有两点:5s内没有响应输入的事件,比如按键或者屏幕触摸等,2.广播接收器处理超时 10s内没完成
首先我们需要知道,每一个应用都有一个属于自己的虚拟机,也就是说每个应用都有自己的Main函数,这个main函数就是ActivityThread.java中的main函数
为什么每个应用都有自己的main函数?
当我们在launcher界面开启一个app时,系统会用zygote给我们分配一个虚拟机,应用就运行在这个虚拟机上,之后执行的就是ActivityThread的main函数,在main函数中创建主线程的Looper对象
之后就Looper.loop一直轮寻整个主线程的消息
当MessageQueue中没有消息时,会阻塞在queue.next(),在next()函数中阻塞在nativePollOnce(ptr, nextPollTimeoutMillis)
这时不会导致ANR,这里就涉及到linux pipe/epoll nativePollOnce()被阻塞时,主线程会释放cpu资源,进入休眠状态. 直到下个消息到达或者有事务发生,会通过pipe管道写入数据来唤醒主线程工作,就可以继续工作了.
这里主线程进入休眠状态和死循环是有区别的.
死循环是指主线程死锁在这里,一直执行某一块代码,无法再响应其他事件.
休眠状态是指在内核状态里,主线程被挂起,线程状态转移到休眠状
很多线程有很多Handler往MessagQueue添加数据,MessageQueue如何确保线程安全的
Messagequeue队列入队enqueueMessage和出队next都使用了同步锁 synchronized,并且锁对象是同一个 this也就是当前的MessageQueue对象
所以首先 入队和出队 不会同一时间发生,然后就是多个线程同时enqueueMessage时也不会出问题
使用Message时该如何创建它
使用Message的obtain方法,因为可以检查是否有可以复用的Message,避免过多的创建、销毁Message对象达到优化内存和性能的目的
并且obtain方法内也添加了同步锁,保证了线程安全
HandlerThread存在的意义
HandlerThread是Thread的子类,就是一个线程,只是它在自己的线程里面帮我们创建了Looper
优点:
1.使用方便,方便初始化,方便获取线程looper, 在HandlerThread的run方法中做了prepare, 获取looper对象,和loop()的 *** 作,不需要自己 *** 作
2.保证了线程安全
用法:
public class MainActivity extends AppCompatActivity { private HandlerThread myHandlerThread ; private Handler handler ; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); //创建一个线程,线程名字:handler-thread myHandlerThread = new HandlerThread( "handler-thread") ; //这里就避免自己new thread并且获取looper对象更容易 //开启一个线程 myHandlerThread.start(); //在这个线程中创建一个handler对象 handler = new Handler( myHandlerThread.getLooper() ){ @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时 *** 作 Log.d( "handler " , "消息: " + msg.what + " 线程: " + Thread.currentThread().getname() ) ; } }; //在主线程给handler发送消息 handler.sendEmptyMessage( 1 ) ; new Thread(new Runnable() { @OverrIDe public voID run() { //在子线程给handler发送数据 handler.sendEmptyMessage( 2 ) ; } }).start() ; } @OverrIDe protected voID onDestroy() { super.onDestroy(); //释放资源 myHandlerThread.quit() ; }}
总结
以上是内存溢出为你收集整理的Android Handler整体梳理以及热点问题解析全部内容,希望文章能够帮你解决Android Handler整体梳理以及热点问题解析所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)