Android Handler源码解析

前言

在android开发中,经常会在子线程中进行一些操作,当操作完毕后会通过handler发送一些数据给主线程,通知主
线程做相应的操作。

探索其背后的原理:子线程 handler 主线程 其实构成了线程模型中的经典问题 生产者-消费者模型。 生产者-消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取走数据。

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

源码分析

  1. Handler
  2. Handler机制的相关类
  3. 创建Looper
  4. 创建MessageQueue以及Looper与当前线程的绑定
  5. Looper.loop()
  6. 创建Handler
  7. 创建Message
  8. Message和Handler的绑定
  9. Handler发送消息
  10. Handler处理消息

Handler机制的相关类

Hanlder:发送和接收消息

Looper:用于轮询消息队列,一个线程只能有一个Looper Message: 一个消息实体MessageQueue: 消息队列用于存储消息和管理消息

创建Looper

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

主线程中不用调用Looper.prepare()方法,是因为在ActivityThread.java的main方法中,系统在启动进程的时候自动执行了Looper的prepareMainLooper()方法

//ActivityThread.java
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.java
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时标识消息队列是否可以销毁, 主线程的MessageQueue不可被销毁 下面有介绍

//Looper.java
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与当前线程的绑定

//Looper.java
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) {
      //如果Looper为空则会抛出异常
      throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this
thread.");
    }
    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就是绑定的Handler,详见后面Message的部分,Handler开始后面代码省略.....
      msg.target.dispatchMessage(msg);
      msg.recycleUnchecked();
   }
 }

创建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().
//这句话告诉我们get可能返回null 除非先调用prepare()方法创建Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

 创建Message

可以直接new Message 但是有更好的方式 Message.obtain。因为可以检查是否有可以复用的Message,用过复用避免过多的创建、销毁Message对象达到优化内存和性能的目地,用Hanlder的obtainMessage()就是调用的Message的obtain方法,所以在创建Message的时候,请尽量使用Message.obtain或者handler.obtainMessage方法。

//Message.java
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();
}

//Handler.java
public final Message obtainMessage()
{
    return Message.obtain(this);
}

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. 消息机制之同步屏障

同步屏障,view绘制中用 <https://juejin.im/post/6844903910113705998
同步屏障的概念,在Android开发中非常容易被人忽略,因为这个概念在我们普通的开发中太少见了,很容易被忽略。
大家经过上面的学习应该知道,线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息,而且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序,那么问题来了,同一个时间范围内的消息,如果它是需要立刻执行的,那我们怎么办,按照常规的办法,我们需要等到队列轮询到我自己的时候才能执行
哦,那岂不是黄花菜都凉了。所以,我们需要给紧急需要执行的消息一个绿色通道,这个绿色通道就是同步屏障的概念。
同步屏障是什么?
屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过(一般我们sendMessage,在子线程中发送的消息都是同步消息)。如何开启同步屏障呢?如下而已:

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() 中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。

在使用同步屏障时,要先通过postSyncBarrier()方法,给MessageQueue中插入一个同步屏障,紧接着再给MessageQueue中插入一个异步消息(msg.setAsynchronous(true);),之后在loop()方法取消息的时候,MessaqgeQueue.next方法就会优先返回队列中的异步消息。

HandlerThread

如果需要在handleMessage方法中处理耗时操作,那么就需要在子线程中创建自己的Handler对象,那么建议使用HandlerThread

HandlerThread和Thread对比:

如果在Thread中创建Handler,需要在run()方法中先执行Looper.prepare方法,创建Looper对象,然后处理业务,最后在调用Looper.loop()方法轮询消息,如下:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
  
        Looper.loop();
    }
}

Looper.loop()之后的代码不会执行,因为当没有消息的时候,loop方法会Block,导致线程挂起,后续代码不会执行,除非调用quit方法之后退出loop循环,写在Looper.loop()后面的代码才会被执行。

而如果使用HandlerThread就简单很多了,不用去关心Looper的相关操作,因为HandlerThread都帮我们封装好了,一版调用如下:

myHandlerThread = new HandlerThread( "handler-thread") ;
//开启一个线程
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() ;

然后,我们只需要在必要的时候执行myHandlerThread.quit() ;方法,及时释放资源就OK了。

看源码:

//HandlerThread.java
@Override
    public void run() {
        mTid = Process.myTid();
        //已经帮用户处理好Looper的prepare操作
        Looper.prepare();
        //同步块,保证getLooper方法不会为null
        synchronized (this) {
            mLooper = Looper.myLooper();
            //Looper对象已经创建完成,通知getLooper方法,可以返回了
            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 {
                    //如果mLooper为null,那么getLooper方法会阻塞,直到run方法中,执行了Looper.prepare方法创建好Looper对象,
                    //这样能够保证getLooper返回的mLooper一定不为null,保证了线程安全
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

也就是说,通过handler = new Handler( myHandlerThread.getLooper() )生成Handler对象的时候,能够保证myHandlerThread.getLooper()一定不会为null,使用HandlerThread能够很好的帮我们省去处理Looper的过程。

IntentService

与Service的不同

  1. Service中的程序仍然运行于主线程中,而在IntentService中的程序运行在我们的异步后台线程中。在接触到IntentService之前,我在项目中大多是自己写一个Service,在Service中自己写一个后台线程去处理相关事务,而这些工作Android其实早已经帮我们封装的很好。
  2. 在Service中当我的后台服务执行完毕之后需要在外部组件中调用stopService方法销毁服务,而IntentService并不需要,它会在工作执行完毕后自动销毁。

看源码:

package android.app;

import android.annotation.WorkerThread;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

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

        @Override
        public void handleMessage(Message msg) {
            //我们使用IntentService的时候需要重写onHandleIntent方法,可在该方法中执行耗时操作
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

   
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        
        super.onCreate();
        //创建HandleThread并启动
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

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

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //发送消息,handleMessage就会处理onHandleIntent方法
        mServiceHandler.sendMessage(msg);
    }

    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

   
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     */
    @WorkerThread
    protected abstract void onHandleIntent(Intent intent);
}

所以IntentService是一个利用HandlerThread,可以帮我们很好执行后台操作的Service,并且它执行完任务后自动退出,不用我们再手动去执行stopService方法去结束Service。 


Handler常问面试题
1. 一个线程有几个 Handler?

一个线程中可以有n个Handler,Handler的数量没有限制。
2.一个线程有几个 Looper?如何保证?

一个线程只能有一个Looper,在Looper.java中,是通过ThreadLocal保证线程之间数据的隔离的,通过Looper.myLooper()方法获取Looper,实际上就是通过ThreadLocal的get方法,获取的之前prepare方法创建的Looper对象。
3.Handler内存泄漏原因? 为什么其他的内部类没有说过有这个问题?

在子线程中,Handler的handleMessage方法可能会持有Activity,Fragment的Context对象,当Activity或者Fragment的生命周期结束时,有可能MSG还处于MessageQueue排队状态,那么由于JVM GC的不可达分析,MSG还持有Activity和Fragment的Context,从而导致Activity和Fragment无法被及时释放,造成内存泄漏。
4.为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?

主线程可以直接new Handler是因为在ActivityThread.java的main方法中,启动进程时就调用了Looper.prepareMainLooper()方法和Looper.loop()方法,所以可以直接new Handler;在子线程中new Handler需要在new之前调用Looper.prepare()方法创建该线程对应的唯一Looper对象,在new完之后,调用Looper.loop()方法开始轮询消息。
5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

子线程中的Looper,消息对列无消息的时候,应该调用Looper的quit方法,这样在MessageQueue中没有消息的时候,next方法就会退出循环,进而Looper的loop方法就会退出。
6.既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?取消息呢?

一个线程只有一个Looper,而一个Looper对象只唯一对应一个MessageQueue,所以一个线程中的多个handler对应的是同一个Looper的唯一 一个MessageQueue,在MessageQueue中,enqueueMessage方法和next方法通过synchronize(this)来确保入队和出队的同步,从而保证了线程安全,读写消息同步。
7.我们使用 Message 时应该如何创建它?

使用Message.obtain或者handler.obtainMessage方法,能够复用已经创建的但是还没有被销毁的Message,不用通过new Message()每次都需要重新申请内存空间,避免造成内存碎片化,进而导致OOM问题。
8.Looper死循环为什么不会导致应用卡死?

这里首先说明一下,卡死表示应用处在正在运行状态,然后有问题导致进程卡住,导致ANR或者其他问题,卡死的前提是进程处于正在运行的状态,也就是进程正在占用CPU资源;

为什么死循环不会导致应用卡死? 因为Looper中获取消息的时候,在MessageQueue的next方法中,nativePollOnce在jni中调用的是Linux的epoll机制,在MessageQueue中没有消息的时候,系统会挂起,让出CPU资源,当前进程就处于休眠状态,没有在运行态,没有占用CPU资源,所以不会导致卡死,整个Android系统都是通过消息机制驱动的,Activity的生命周期,在ActivityThread中,也是通过mH这个成员变量来处理消息,维护生命周期函数的;在enqueueMessage的时候,如果当前进程处于休眠状态,那么调用nativeWake(mPtr)方法唤醒当前线程,继续执行,之前在nativePollOnce之后处于休眠的线程,就被唤醒继续执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值