Handler总结

目录

简介

Handler机制的相关类

创建Looper

创建MessageQueue以及Looper与当前线程的绑定

创建Handler

创建Message

Message和Handler的绑定

Handler发送消息

Handler处理消息

小结

难点问题

1. 线程同步问题

2. 消息机制之同步屏障

同步消息的应用场景

Handler常问面试题

源码应用

HandlerThread是什么?为什么它会存在?

ThreadHandler

IntentService

FragmentPagerAdapter

Glide生命周期原理


简介

Handler源码分析(超详细的)

Handler完全解读——Handler的使用

Android进阶——深入浅出Handler(一)

Android开发——避免内存泄露

在android开发中,经常会在子线程中进行一些操作,当操作完毕后会通过handler发送一些数据给主线程,通知主 线程做相应的操作。 探索其背后的原理:子线程 handler 主线程 其实构成了线程模型中的经典问题 生产者-消费者 模型。 生产者-消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据, 消费者从存储空间中取走数据。

 

好处: - 保证数据生产消费的顺序(通过MessageQueue,先进先出) - 不管是生产者(子线程)还是消费者(主线程) 都只依赖缓冲区(handler),生产者消费者之间不会相互持有,使他们之间没有任何耦合

源码分析:
Handler

Handler机制的相关类

创建Looper

创建MessageQueue以及Looper与当前线程的绑定 Looper.loop()

创建Handler

创建Message Message和Handler的绑定 Handler发送消息 Handler处理消息

 

Handler机制的相关类

 

Hanlder:发送和接收消息 Looper:用于轮询消息队列,一个线程只能有一个Looper Message: 消息实体

MessageQueue: 消息队列用于存储消息和管理消息

 

创建Looper

 

创建Looper的方法是调用Looper.prepare() 方法

 

在ActivityThread中的main方法中为我们prepare了

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        //其他代码省略...
        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"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop(); //开始轮循操作

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

 

Looper.prepareMainLooper();

 public static void prepareMainLooper() {
        prepare(false);//消息队列不可以quit
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

 

prepare有两个重载的方法,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在创建MessageQueue时标识消息队列是否可以销毁, 主线程不可被销毁 下面有介绍

  public static void prepare() {
        prepare(true);//消息队列可以quit
    }
    //quitAllowed 主要
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//不为空表示当前线程已经创建了Looper
            throw new RuntimeException("Only one Looper may be created per thread");
            //每个线程只能创建一个Looper
        }
        sThreadLocal.set(new Looper(quitAllowed));//创建Looper并设置给sThreadLocal,这样get的 时候就不会为null了
    }

 

创建MessageQueue以及Looper与当前线程的绑定

   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
        mThread = Thread.currentThread(); //当前线程的绑定
   }

 

MessageQueue的构造方法

 MessageQueue(boolean quitAllowed) {
 //mQuitAllowed决定队列是否可以销毁 主线程的队列不可以被销毁需要传入false, 在MessageQueue的quit()方法 就不贴源码了
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

 

Looper.loop()

 

同时是在main方法中 Looper.prepareMainLooper() 后Looper.loop(); 开始轮询

public static void loop() {
        final Looper me = myLooper();//里面调用了sThreadLocal.get()获得刚才创建的Looper对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }//如果Looper为空则会抛出异常
        final MessageQueue queue = me.mQueue;

        // 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
            if (msg == null) {
                //由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage enqueueMessage后
                //队列里才有消息
                // No message indicates that the message queue is quitting.
                return;
            }

            // 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,详见后面Message的部 分,Handler开始
            //后面代码省略. ....

            msg.recycleUnchecked();
        }
    }

 

创建Handler

 

最常见的创建handler

        Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

 

在内部调用 this(null, false);

public Handler(Callback callback, boolean async) {
        //前面省略
        mLooper = Looper.myLooper();//获取Looper,**注意不是创建Looper**!
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//创建消息队列MessageQueue
        mCallback = callback; //初始化了回调接口
        mAsynchronous = async;
    }

 

Looper.myLooper();

    //这是Handler中定义的ThreadLocal  ThreadLocal主要解多线程并发的问题
  // sThreadLocal.get() will return null unless you've called prepare().
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

 

sThreadLocal.get() will return null unless you’ve called prepare(). 这句话告诉我们get可能返回null 除非先调用 prepare()方法创建Looper。在前面已经介绍了

 

创建Message

 

可以直接new Message 但是有更好的方式 Message.obtain。因为可以检查是否有可以复用的Message,用过复用避 免过多的创建、销毁Message对象达到优化内存和性能的目地

public static Message obtain(Handler h) {
        Message m = obtain();//调用重载的obtain方法
        m.target = h;//并绑定的创建Message对象的handler

        return m;
    }

public static Message obtain() {
        synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全
            if (sPool != null) {//sPool是就是handler dispatchMessage 后 通过recycleUnchecked 回收用以复用的Message 
                Message m = sPool;
                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; //绑定
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

 

Handler发送消息

 

Handler发送消息的重载方法很多,但是主要只有2种。 sendMessage(Message) sendMessage方法通过一系列重 载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用 enqueueMessage,继续调用messageQueue的enqueueMessage方法,将消息保存在了消息队列中,而最终由 Looper取出,交给Handler的dispatchMessage进行处理

我们可以看到在dispatchMessage方法中,message中callback是一个Runnable对象,如果callback不为空,则直接 调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接 通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。

public void dispatchMessage(Message msg) { 
    if (msg.callback != null) {//callback在message的构造方法中初始化或者使用 handler.post(Runnable)时候才不为空 
        handleCallback(msg); 
    } else { 
        if (mCallback != null) {//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler, 该属性为null,此段不执行 
            if (mCallback.handleMessage(msg)) { 
                return; 
            } 
        } 
        handleMessage(msg);//最终执行handleMessage方法 
    } 
} 
 
 private static void handleCallback(Message message) { 
        message.callback.run(); 
    } 

 

Handler处理消息

 

在handleMessage(Message)方法中,我们可以拿到message对象,根据不同的需求进行处理,整个Handler机制的 流程就结束了。

 

小结

 

handler.sendMessage 发送消息到消息队列MessageQueue,然后looper调用自己的loop()函数带动 MessageQueue从而轮询messageQueue里面的每个Message,当Message达到了可以执行的时间的时候开始执 行,执行后就会调用message绑定的Handler来处理消息。大致的过程如下图所示

 

handler机制就是一个传送带的运转机制。

1)MessageQueue就像履带。

2)Thread就像背后的动力,就是我们通信都是基于线程而来的。

3)传送带的滚动需要一个开关给电机通电,那么就相当于我们的loop函数,而这个loop里面的for循环就会带着不断 的滚动,去轮询messageQueue

4)Message就是 我们的货物了。

 

难点问题

 

1. 线程同步问题

 

Handler是用于线程间通信的,但是它产生的根本并不只是用于UI处理,而更多的是handler是整个app通信的框架, 大家可以在ActivityThread里面感受到,整个App都是用它来进行线程间的协调。Handler既然这么重要,那么它的 线程安全就至关重要了,那么它是如何保证自己的线程安全呢?

Handler机制里面最主要的类MessageQueue,这个类就是所有消息的存储仓库,在这个仓库中,我们如何的管理好 消息,这个就是一个关键点了。消息管理就2点:1)消息入库(enqueueMessage),2)消息出库(next),所以 这两个接口是确保线程安全的主要档口。
enqueueMessage 源码如下:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    // 锁开始的地方
    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, 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);
        }
    }
    //锁结束的地方

 

synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机的。在多线程的课程中我们有详细分析过, 有问题的同学可以去研究一下。

 

synchronized (this)

 

这个锁,说明的是对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的,然而,在我们的Handler里 面,一个线程是对应着一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue(这个在上文中也有 介绍)。所以,我们主线程就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候, 主线程一次都只会处理一个消息,其他的都需要等待,那么这个时候消息队列就不会出现混乱。

 

另外,在看next函数

Message next() {

    ....
        
    for (;;) {
        ....

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
                   ...
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

             ...
        }//synchronized 结束之处

        // 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;
    }
}

 

next函数很多同学会有疑问:我从线程里面取消息,而且每次都是队列的头部取,那么它加锁是不是没有意义呢?答 案是否定的,我们必须要在next里面加锁,因为,这样由于synchronized(this)作用范围是所有 this正在访问的代 码块都会有保护作用,也就是它可以保证 next函数和 enqueueMessage函数能够实现互斥。这样才能真正的保证多 线程访问的时候messagequeue的有序进行。

小结: 这个地方是面试官经常问的点,而且他们会基于这个点来拓展问你多线程,所以,这个地方请大家重视。

 

2. 消息机制之同步屏障

 

同步屏障的概念,在Android开发中非常容易被人忽略,因为这个概念在我们普通的开发中太少见了,很容易被忽 略。

大家经过上面的学习应该知道,线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息,而 且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序,那么问题来了,同一个时间范围内的消 息,如果它是需要立刻执行的,那我们怎么办,按照常规的办法,我们需要等到队列轮询到我自己的时候才能执行 哦,那岂不是黄花菜都凉了。所以,我们需要给紧急需要执行的消息一个绿色通道,这个绿色通道就是同步屏障的概 念。

同步屏障是什么?

屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。如何开启同步屏障呢?如下而 已:

 

MessageQueue#postSyncBarrier()

 

我们看看它的源码

   /**
     *
   @hide
   **/
      public int postSyncBarrier() {
           return postSyncBarrier(SystemClock.uptimeMillis());
      } private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token
    synchronized (this) {
        final int token = mNextBarrierToken++;
        //从消息池中获取Message
        final Message msg = Message.obtain();
        msg.markInUse();
        
        //就是这里!!!初始化Message对象的时候,并没有给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;
            }
        }
        //根据prev是不是为null,将 msg 按照时间顺序插入到 消息队列(链表)的合适位置
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

 

可以看到,Message 对象初始化的时候并没有给target 赋值,因此,target == null 的 来源就找到了。上面消息的插入也做了相应的注释。这样,一条target == null的消息就进入了消息队列。

那么,开启同步屏障后,所谓的异步消息又是如何被处理的呢?

如果对消息机制有所了解的话,应该知道消息的最终处理是在消息轮询器Looper#loop() 中,而loop()循环中会调用MessageQueue#next()从消息队列中进行取消息。

//MessageQueue.java

Message next()

    . ....//省略一些代码
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    // 1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
    // 2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
    // 3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时)
    //   如果期间有程序唤醒会立即返回。
    int nextPollTimeoutMillis = 0;
    //next()也是一个无限循环
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            //获取系统开机到现在的时间
            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) {
                   //计算出离执行时间还有多久赋值给nextPollTimeoutMillis,
                   //表示nativePollOnce方法要等待nextPollTimeoutMillis时长后返回
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 获取到消息
                    mBlocked = false;
                   //链表操作,获取msg并且删除该节点 
                    if (prevMsg != null) 
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    //返回拿到的消息
                    return msg;
                }
            } else {
                //没有消息,nextPollTimeoutMillis复位
                nextPollTimeoutMillis = -1;
            }
            . ....//省略

}

 

从上面可以看出,当消息队列开启同步屏障的时候(即标识为msg.target == null),消息机制在处理消息的时候,优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。

下面用示意图简单说明:

 

 

如上图所示,在消息队列中有同步消息和异步消息(黄色部分)以及一道墙----同步屏障(红色部分)。有了同步屏 障的存在,msg_2 和 msg_M 这两个异步消息可以被优先处理,而后面的 msg_3 等同步消息则不会被处理。那么这 些同步消息什么时候可以被处理呢?那就需要先移除这个同步屏障,即调用removeSyncBarrier

 

同步消息的应用场景

似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中的 UI 更新相关的消息即为异步消息,需要优先处理。

比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了 ViewRootImpl#scheduleTraversals() 

如下:

 

//ViewRootImpl.java

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //开启同步屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //发送异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

 

postCallback()最终走到了ChoreographerpostCallbackDelayedInternal()

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

 

这里就开启了同步屏障,并发送异步消息,由于 UI 更新相关的消息是优先级最高的,这样系统就会优先处理这些异 步消息。

最后,当要移除同步屏障的时候需要调用ViewRootImpl#unscheduleTraversals()

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //移除同步屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

 

小结

同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用 Handler.getLooper().getQueue().postSyncBarrier() 并设置消息的setAsynchronous(true)时,target 即为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在loop() 中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。

 

Handler常问面试题

 

 

1. 一个线程有几个 Handler?

2.一个线程有几个 Looper?如何保证?

https://blog.csdn.net/ly502541243/article/details/87475229

可以创建无数个Handler,但是他们使用的消息队列都是同一个,也就是同一个Looper

hander初始化的时候会创建Looper,Looper的获取和当前线程绑定,内部是一个ThreadLocal的集合。使用当前线程作为key,所以一个线程对应一个looper,一个looper存储着MessageQueue的队列,进行存储和取出的操作

主线程默认在MainThread里帮我们生成了Looper,在子线程我们需要自己looper.prepare和looper.loop进行初始化和loop操作

3.Handler内存泄漏原因? 为什么其他的内部类没有说过有这个问题?

https://blog.csdn.net/alex01550/article/details/82744191

匿名内部类会主动持有外部类的引用,静态内部类不会持有外部类的对象

4.为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?

5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

https://blog.csdn.net/yichen97/article/details/106107874/

6.既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它 内部是如何确保线程安全的?取消息呢?

synchronized (this)加锁

7.我们使用 Message 时应该如何创建它?

obtain

享元模式

直接new的话有内存抖动,产生内存碎片,这个东西是重复的生成,然后销毁

8.Looper死循环为什么不会导致应用卡死

https://www.jianshu.com/p/06ba04884558

msg消息5s没有处理完毕,会使用handler发出anr的消息,那这个就会出发弹出弹窗,所以anr本身和looper的无关

为什么looper的block不会导致anr呢,这个block只是线程没事做了,交出cpu

源码应用

HandlerThread是什么?为什么它会存在?

作为一个Android开发,Handler机制是一定要了解的。发现很多人对Handler和Looper机制非常 了解,对答如流,但是却不知道何为HandlerThread

HandlerThread是Thread的子类,严格意义上来说就是一个线程,只是它在自己的线程里面帮我们创建了Looper HandlerThread 存在的意义如下:

 

1) 方便使用:a. 方便初始化,b,方便获取线程looper

2)保证了线程安全

我们一般在Thread里面 线程Looper进行初始化的代码里面,必须要对Looper.prepare(),同时要调用Loop. loop();

@Override public void run() {
    Looper.prepare();
    Looper.loop(); }

 

而我们要使用子线程中的Looper的方式是怎样的呢?看下面的代码

Thread thread = new Thread(new Runnable() {
    Looper looper;
    @Override
    public void run() {
      //  Log.d(TAG, "click2: " + Thread.currentThread().getName());
        Looper.prepare();
        looper =Looper.myLooper();
        Looper.loop();
    }

    public Looper getLooper() {
        return looper;
    }
}); 
thread.start(); 
Handler handler = new Handler(thread.getLooper());

 

上面这段代码有没有问题呢?

1)在初始化子线程的handler的时候,我们无法将子线程的looper传值给Handler,解决办法有如下办法:

a. 可以将Handler的初始化放到 Thread里面进行

b. 可以创建一个独立的类继承Thread,然后,通过类的对象获取。

这两种办法都可以,但是,这个工作 HandlerThread帮我们完成了

2)依据多线程的工作原理,我们在上面的代码中,调用 thread.getLooper()的时候,此时的looper可能还没有初 始化,此时是不是可能会挂掉呢?

以上问题

HandlerThread 已经帮我们完美的解决了,这就是 handlerThread存在的必要性了。 我们再看 HandlerThread源码

public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();  //此时唤醒其他等待的锁,但是
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

 

它的优点就在于它的多线程操作,可以帮我们保证使用Thread的handler时一定是安全的。

 

ThreadHandler

是Thread的子类、就是一个线程,只是他在自己的线程里面帮我们创建了Looper

存在意义:

1、方便使用:a、方便初始化 b、方便获取线程looper

2、保证了线程安全,如果自己定义looper,无法保证获取的looper,怎么解决异步问题、、、、多线程的锁机制

   @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

发现looper==null 这个时候会wait,

wait的时候:?会把锁释放给下面继续执行的代码

notifyAll:?不会释放锁,会唤醒锁、等锁里面

Android HandlerThread 的使用

Java并发编程系列之八:wait()、notify()和notifyAll()

 

IntentService

IntentService存在的意义

应用需求:一项任务分成好几个子任务、子任务按顺序先后执行、子任务全部执行完后,这项任务才算成功

这个需求可以用多个线程来处理,一个线程处理完-》下一个线程-》下一个线程

IntentService就可以帮助我们完成这个工作。而且,能够很好的管理线程,保证只有一个子线程处理工作。而且是一个一个的完成任务,有条不紊的进行

 

IntentService的用法

handelr多线程的应用

Service适用于处理后台应用

IntentSevice内部创建了HandlerThread

onCreate、onStart是由和后台调度的

发送消息之后,会调度onHandleIntent,这个就是实现service的实现类。后台工作在这里执行

 
   private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
 @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
stopSelf(msg.arg1);

处理完service。会自动停止、内存释放

Service

 public final void stopSelf(int startId) {
        if (mActivityManager == null) {
            return;
        }
        try {
            mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
    }

 

 

还有什么地方用呢

 

FragmentPagerAdapter

fragment生命周期管理

Glide生命周期原理

Glide.with(context).from().into(imageView)

context?fragment.getApplicationContext:建议context,会跟随他的生命周期,减少消耗,第二个会消耗内存

尽量不要再子线程用,会自动编程applicaiton的context,

使用handler来移除作为标记的信息。,因为fm.beginTransaction().add并不会马上添加进去,所以使用了pendingRequestManagerFragments来存储标记  

pendingRequestManagerFragments这个是一个map

 
 RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
    
	pendingRequestManagerFragments = new HashMap<Fragment>();
    //尝试根据id去找到此前创建的RequestManagerFragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        //如果没有找到,那么从临时存储中寻找
        current = pendingRequestManagerFragments.get(fm);
        if (current == null) {
            //如果仍然没有找到,那么新建一个RequestManagerFragment,并添加到临时存储中。
            //然后开启事务绑定fragment并使用handler发送消息来将临时存储的fragment移除。
            current = new RequestManagerFragment();
            pendingRequestManagerFragments.put(fm, current);
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    
	}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Vue中的handler通常指的是事件处理函数,也可以称为事件处理器。在Vue中,我们可以通过v-on指令来绑定事件处理函数,例如: ``` <template> <button v-on:click="handleClick">点击我</button> </template> <script> export default { methods: { handleClick() { console.log('按钮被点击了') } } } </script> ``` 在上面的例子中,我们为按钮绑定了一个click事件的处理函数handleClick。当按钮被点击时,控制台会输出"按钮被点击了"。这就是一个简单的事件处理函数。 ### 回答2: Vue的handler(处理器)是Vue框架中的一个概念。在Vue中,handler是一种用于处理事件或响应数据的函数或方法。 在Vue中,常见的使用handler的场景包括: 1. 事件处理:Vue中的事件处理使用handler来处理触发的事件。通过在Vue组件中定义事件处理函数,并将其作为handler绑定到对应的DOM元素上,当触发事件时,handler将会被自动调用。 2. 数据响应:在Vue中,可以使用handler来处理数据的响应。通过在Vue实例中定义响应式的数据,并使用handler来监听这些数据的变化,当数据发生改变时,handler会被自动触发,从而更新相关的视图或执行其他操作。 3. 计算属性:在Vue中,计算属性也可以使用handler来实现相应的逻辑。通过定义一个计算属性,并将其handler设置为响应式数据的监听器,当相关的响应式数据发生变化时,handler会被自动调用,返回计算属性的值。 总结来说,Vue中的handler是用于处理事件或响应数据的函数或方法。它可以用于处理事件的触发、数据的响应以及计算属性的实现等场景。通过使用handler,我们可以方便地处理Vue中的各种操作,并实现页面的交互和数据的更新。 ### 回答3: Vue的handler是Vue框架中的一个重要概念,用于处理事件的函数。 在Vue中,我们可以使用v-on指令来监听DOM事件,并将事件的处理程序(handler)定义为Vue实例中的一个方法。这个方法将会在相应的事件被触发时被调用。 handler在Vue中扮演了事件处理的角色,它可以接收事件对象作为参数,用于获取事件的相关信息。在handler中,我们可以编写逻辑代码来响应事件,并对应用状态进行相应的修改。 handler除了可用于监听DOM事件外,还可以用于监听自定义事件或是Vue实例自身的生命周期钩子函数。它可以是一个直接定义在Vue实例中的方法,也可以是一个引用了其他方法的函数。 使用handler的好处在于,它可以将事件处理逻辑与模板代码进行分离,提高代码的可维护性和可读性。同时,handler可以方便地管理和处理多个不同的事件,并且可以实现事件传参和事件修饰符等高级特性。 总之,Vue的handler是用于处理事件的函数,通过定义handler,我们可以监听和响应各种事件,并实现相应的业务逻辑。它是Vue框架中非常重要的一部分,能够帮助我们构建交互丰富的应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值