Handler 十连问

1. 说说对Handler 的理解

arduino 复制代码
//\frameworks\base\core\java\android\os\Handler.java
//\frameworks\base\core\java\android\os\Looper.java
//\frameworks\base\core\java\android\os\Message.java
//\frameworks\base\core\java\android\os\MessageQueue.java
​
  1. Android Handler 是一套 异步消息传递机制(异步通信机制)。主要适用于同一个组件(或者说是同一个文件中)不同线程之间的信息传递。

  2. 有时候需要在子线程中进行耗时的 IO 操作,这可能是读取文件或者访问网络等,当耗时操作完成以后可能需要在 UI上做一些改变,由于 Android 开发规范的限制,我们并不能在子线程中访问 UI 控件,否则就会触发程序异常,这个时候通过 Handler 就可以将更新 UI 的操作切换到主线程中执行。

  3. Handler机制 由Handler,Message,MessageQueue和Looper四个组件组成

Message 是线程之间传递的信息,它可以在内部携带少量的信息,用于在不同线程之间交换数据;

Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用 Handler.sendMessage(),而发出的消息经过一系列地辗转处理后,最终会传递到Handler.handleMessage()

MessageQueue 是消息队列的意思,它主要用于存放所有通过 Handler 发送的消息。这部分消息会一直存在于消息队列中,等待被处理,其本质上是一个按时间排序的单向链表;

Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 loop() 方法后,就会进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将它取出,并传递到 Handler.handleMessage() 方法中。

  1. Handler的使用方法

    scala 复制代码
      * <p>This is a typical example of the implementation of a Looper thread,
      * using the separation of {@link #prepare} and {@link #loop} to create an
      * initial Handler to communicate with the Looper.
      *
      * <pre>
      *  class LooperThread extends Thread {
      *      public Handler mHandler;
      *
      *      public void run() {
      *          Looper.prepare();
      *
      *          mHandler = new Handler(Looper.myLooper()) {
      *              public void handleMessage(Message msg) {
      *                  // process incoming messages here
      *              }
      *          };
      *
      *          Looper.loop();
      *      }
      *  }</pre>
      */

    自己对Handler机制使用的理解:

    a. Looper.prepare() 本质是去创建Looper, 而创建Looper的核心要义就是创建一个MessageQueue。可以将Looper看成是流水线的引擎,而MessageQueue就是流水线皮带上的托盘;

    csharp 复制代码
    public static void prepare() {
        prepare(true);
    }
    ​
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set( new Looper(quitAllowed) );
    }
    ​
    private Looper(boolean quitAllowed) {
        mQueue =  new MessageQueue(quitAllowed) ;
        mThread = Thread.currentThread();
    }

    b. new Handler(Looper.myLooper())就是告诉Handler(工人)该去处理哪条流水线的活儿,Handler.sendMessage() 就是让Handler(工人)往流水线皮带上的托盘放需要处理的零件(message),而Handler.dispatchMessage() 就是将流水线末端的托盘上的零件(message)取出来处理;

    c. Looper.loop() 就是启动流水线引擎Looper,让流水线运转起来;


    a. 传送带上的零件(Message)是先进先出的。每次工人往传送带(MessageQueue)上的托盘放零件(Message)时,会考虑下哪个零件需要处理的优先级,按照需要处理的优先级排序放入到传送带MessageQueue中;

    b. 每次工人(Handler)处理完零件(message)后,会将托盘收集起来扔进托盘堆里(sPool),这个托盘堆最多能放50个,然后后面工人需要放零件使用托盘的时候,直接从这里面拿就可以了,节省时间;

    c. 当流水线皮带上的托盘里面的零件(message)还没有到达需要处理的时间时,Looper会停止传送,进入待机状态;

    d. 一条完整的流水线只有一个引擎(Looper)和一条传送带(MessageQueue),而工人(Handler)可以有多个。工人在往传送带上放置零件(Message)的时候,会在上面添加工人(Handler)的个人信息(msg.target),然后再流水线终端处理的工人会根据这个(msg.target)将其派发给指定的工人处理(dispatchMessage(msg));

    e. 当传送带上(MessageQueue)中没有零件(Message)时,引擎(Looper)不再运转进入待机状态,整个系统阻塞在准备取下一个零件的状态中(MessageQueue.next()),实际上是调用的nativePollOnce 的底层方法,一旦传送带上又有了新的零件(Message),流水线系统就会继续运转;

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

一个线程只有一个 Looper对象和一个 MessageQueue 对象。

在创建 Handler 之前,需要调用 Looper.prepare() ,该函数保证了每个线程只有一个 Looper 对象。

typescript 复制代码
/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}
​
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

而 MessageQueue 又是在 Looper 的构造函数中创建的,保证了一个 Looper 对应一个 MessageQueue

ini 复制代码
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

3. 一个线程可以有几个handler?那如何保证message能分配给正确的handler 处理呢?

因为Handler可以在Activity里new,在Service里面也可以new,而Activity全部都跑在了主线程里面,这就证明了主线程中可以有多个Handler。

Handler 在 sendMessageAtTime() 时,会把自身填入 msg.target

ini 复制代码
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
         msg.target = this; 
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    else {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
    }
    return sent;
}

然后在 Looper.loop() 不断从 MessageQueue 中获取 Message 处理的时候,会根据 msg.target 去调用对应的 dispatchMessage , 这边的 msg.target 就是前面的 handler

java 复制代码
public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;
    while (true) {
        Message msg = queue.next(); // might block
        if (msg != null) {
            if (msg.target == null) {
                return;
            }
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
             msg.target.dispatchMessage(msg); 
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);
            msg.recycle();
        }
    }
}

4. MessageQueue 的数据结构?多个线程往MessageQueue添加数据,如何保证线程安全?

MessageQueue 是一种先进先出的数据结构,底层实现是按时排序的单向链表,当有 Message 入队时,按照 Message 的 when 值排序插入,然后出队时则去表头的 Message;

MessageQueue.enqueueMessage()MessageQueue.next() 的时候都会用到同步锁synchronized保证线程安全

ini 复制代码
final boolean enqueueMessage(Message msg, long when) {
    if (msg.when != 0) {
        throw new AndroidRuntimeException(msg + " This message is already in use.");
    }
    if (msg.target == null && !mQuitAllowed) {
        throw new RuntimeException("Main thread not allowed to quit");
    }
      synchronized (this) { 
        if (mQuiting) {
            RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
        } else if (msg.target == null) {
            mQuiting = true;
        }
        msg.when = when;
        Message p = mMessages;
         if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            this.notify();
        } else {
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg; 
            this.notify();
        }
    }
    return true;
}
​
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
​
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    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;
            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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
        ...
    }
}
​

5. 为什么主线程可以new Handler?如果子线程想要new handler需要做哪些工作?

因为在主线程中已经提前添加了 Looper.prepareMainLooper()Looper.loop(),如果子线程想要调用new handler,需要调用 Looper.prepare()Looper.loop()

ini 复制代码
//\frameworks\base\core\java\android\app\ActivityThread.java

public static void main(String[] args) {
    ...
     Looper.prepareMainLooper(); 

    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

     Looper.loop(); 
}
csharp 复制代码
public static void prepareMainLooper() {
     prepare(false) ;
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

6. Handler.postDelayed() 消息时间准确吗?实现原理?

实际上,这个问题与线程安全性为同一个问题,多线程中线程一旦安全,时间就不能准确;时间一旦准确,线程就一定不安全。 因为多个线程去访问这个队列的时候,在放入队列和取出消息的时候都会加锁,当第一个线程还没有访问完成的时候,第二个线程就无法使用,所以他实际的时间会被延迟。所以,Handler所发送的Delayed消息时间基本准确,但不完全准确。

实现原理postDelayed最终会调用一个带延迟参数的 sendMessageAtTime,然后通过MessageQueue.enqueueMessage将带延迟时间参数的msg按照时间排序插入到MessageQueue。MessageQueue是一个按时间排序的单向链表,Looper 从 MessageQueue取msg的时候,会判断当前时间是否到达链表头第一个msg 的延迟时间,如果还没到,就会通过比较延迟时间和当前时间计算出还需要等待的时间,然后通过native函数nativePollOnce进行一个阻塞等待,直到等待时间到达再唤醒线程执行msg;

7. MessageQueue中没有消息的时候会发生什么?为什么Looper.loop不会阻塞主线程?

MessageQueue 队列为空时,Looper.loop() 的死循环不会退出也不会执行,而是阻塞在MessageQueue.next() 中的 nativePollOnce() 方法中,进入休眠状态,等待新消息到来重新唤醒。这边会涉及到底层linux 的 pipe 和 epoll 机制实现。

8. 为什么Handler死循环不会卡死?

应用出现ANR卡死和Looper的死循环其实是没有关系的。应用没有消息需要处理的时候,它是在休眠,释放线程了;而ANR是指消息没来得及处理,比如按键和触摸事件在5s内没有处理掉,或者前台广播在10s内没有处理掉等,导致卡死;

9. IdleHandler 了解么?

IdelHandler是MessageQueue中的一个静态内部接口,当 Looper 从 MessageQueue中获取的msg为空,或者执行时间未到时,也就是 MessageQueue空闲时就会去回调 IdleHandler.queueIdle()

如果 queueIdle() 返回 false,则执行完后,该idlehandler会被剔除,也就是只执行一次,如果返回true,则保留,下次MessageQueue进入空闲状态继续执行;

scss 复制代码
//\frameworks\base\core\java\android\os\MessageQueue.java

/**
 * Callback interface for discovering when a thread is going to block
 * waiting for more messages.
 */
public static interface IdleHandler {
  /**
   * Called when the message queue has run out of messages and will now
   * wait for more.  Return true to keep your idle handler active, false
   * to have it removed.  This may be called if there are still messages
   * pending in the queue, but they are all scheduled to be dispatched
   * after the current time.
   */
  boolean queueIdle();
}

Message next() {
    ......
    for (;;) {
        ......
        synchronized (this) {
        // 此处为正常消息队列的处理
            ......
            if (mQuitting) {
                dispose();
                return null;
            }
            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)];
            }
            //mIdleHandlers 数组,赋值给 mPendingIdleHandlers
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        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);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

系统源码中使用例子ActivityThread.handleResumeActivity()中,在 onResume 方法执行完毕后,调用 Looper.myQueue().addIdleHandler(new Idler()) ,去执行一些资源回收,日志打印等不那么着急的任务。除此之外,在做项目 性能优化 的时候也可以使用 IdleHandler,它在主线程空闲时执行任务,而不影响其他任务的执行。

java 复制代码
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;
        ···
        //该方法最终会执行 onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    if (r == null) {
        // We didn't actually resume the activity, so skipping any follow-up actions.
        return;
    }
        ··· 
        ···
    r.nextIdle = mNewActivities;
    mNewActivities = r;
    if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
     Looper.myQueue().addIdleHandler(new Idler()); 
}

private class Idler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        ActivityClientRecord a = mNewActivities;
            ···
        if (a != null) {
            mNewActivities = null;
            IActivityManager am = ActivityManager.getService();
            ActivityClientRecord prev;
            do {
                //打印一些日志
                if (localLOGV) Slog.v(
                    TAG, "Reporting idle of " + a +
                    " finished=" +
                    (a.activity != null && a.activity.mFinished));
                if (a.activity != null && !a.activity.mFinished) {
                    try {
                        //AMS 进行一些资源的回收
                        am.activityIdle(a.token, a.createdConfig, stopProfiling);
                        a.createdConfig = null;
                    } catch (RemoteException ex) {
                        throw ex.rethrowFromSystemServer();
                    }
                }
                prev = a;
                a = a.nextIdle;
                prev.nextIdle = null;
            } while (a != null);
        }
        if (stopProfiling) {
            mProfiler.stopProfiling();
        }
        
        //确认Jit 可以使用,否则抛出异常
        ensureJitEnabled();
        return false;
    }
}

10. 同步屏障了解么?

可以理解为同步屏障 是为Handler消息机制提供的一种 优先级策略,能提高异步消息的优先级

Handler机制中有三种消息:同步消息,异步消息和屏障消息,我们正常使用的消息都是同步消息,异步消息可以在Handler构造时设置,也可以通过setAsynchronous进行设置,而屏障消息跟同步消息的区别是target属性为null。Looper从MessageQueue中取消息时,如果没有遇到屏障消息,那么同步消息和异步消息是一样的,如果遇到屏障消息,则会屏蔽掉该消息之后的所有同步消息,只执行异步消息。

UI绘制流程中,就会使用同步屏障和异步消息,保证在Vsync信号过来时,异步任务能被优先处理,从而让绘制任务被及时执行,避免界面卡顿。

csharp 复制代码
@UnsupportedAppUsage
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            Message msg = mMessages;
             //如果msg.target为空,也就是说是一个同步屏障消息,则进入这个判断里面
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                //在这个while循环中,找到最近的一个异步消息
                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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    //返回异步消息
                    return msg;
                }
            } else {
                // No more messages.
                //没有找到异步消息则进入阻塞状态,等待被唤醒
                nextPollTimeoutMillis = -1;
            }
            ...
}

另外需要注意的是:同步屏障不会自动移除,使用完成之后需要手动移除,不然会造成同步消息无法处理。也就是上边提到的,通过removeSyncBarrier(int token) 方法进行移除,token就是之前添加屏障时返回的token。

arduino 复制代码
public void removeSyncBarrier(int token){
}

11. Handler为什么内存泄漏?如何解决?

原因

Handler的Message被存储在MessageQueue中,有些Message并不能马上被处理,它们在MessageQueue中存在的时间会很长,这就会导致Handler无法被回收。因为Handler是非静态的匿名内部类的实例,它会隐形的持有外部类Activity,从而导致Activity不能被回收,导致Activity泄漏内存

解决方法

  1. 使用static 修饰的handler,使用弱引用activity对象,因为要使用activity对象中的成员(因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露);
  2. 单独定义handler,同样可以弱引用activity;
  3. 使用内部类的handler,在onDestroy方法中removeCallbacksAndMessages;
  4. 还有一种方法是直接使用避免内存泄漏的Handler开源库WeakHandler;

12. 如何创建Message?为什么?

创建 Message 建议使用Message.obtain(),不要使用new message()。因为 Message 类里维护了一个 sPool 的对象,可以理解为一个 Message 链表,这个链表的默认最大长度为 50。在 Android 消息机制中,每当一个 Message 对象被处理完成之后,就会被放入这个池中,为我们提供了复用。当我们调用 Message.obtain()方法时,如果复用池中存在的 Message 对象,我们就不会去创建一个新的 Message 对象。这样就避免频繁创建和销毁 Message 对象带来的性能开销。减小内存的抖动和OOM。

csharp 复制代码
  /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
  */
  public 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 flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

13. HandlerThread

Handlerthread 是继承于Thread的一个类,用于开启一个带有looper的新线程,可以使用这个looper来创建Handler,需要注意的是必须调用start() 方法开启线程。因为 start() 可以调用线程的 run() 方法,而 HandlerThread.run()中会调用 Looper.prepare()Looper.loop(),从而为子线程绑定 Looper,相当于做了一层封装,方便用户使用Handler。

scala 复制代码
//frameworks\base\core\java\android\os\HandlerThread.java

/*
Handy class for starting a new thread that has a looper.
The looper can then be used to create handler classes.
Note that start() must still be called.
*/

public class HandlerThread extends Thread {
  ...

  @Override
  public void run() {
      mTid = Process.myTid();
      Looper.prepare();// HandlerThread在start()的时候执行run()方法,而Looper就是在这里被创建的
      synchronized (this) {
          mLooper = Looper.myLooper();
          notifyAll();
      }
      Process.setThreadPriority(mPriority);
      onLooperPrepared();
      Looper.loop();
      mTid = -1;
  }
}

Handler 和 HandlerThread 的配合使用方式

java 复制代码
HandlerThread thread1 = new HandlerThread("test1");
thread1.start();

Handler mHandler = new Handler(thread1.getLooper()){
  @Override
  public void handleMessage(Message msg) {
      switch (msg.what) {
          case DownloadThread.TYPE_START:
              Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");
              break;
          default:
              break;
      }
      super.handleMessage(msg);
  }
}

14. IntentService

IntentService 是继承于 Service 的基础类, 本质上是一个 Service。主要是用于响应基于 Intent 的异步请求。客户端通过 startService(Intent) 发送请求,IntentService 接收到请求后开启,并在新建的子线程中按序处理异步的 Intent 请求,在同一时间只有一个请求会被处理。当完成所有请求后,IntentService 会自行关闭。

为什么官方提供了 Service 之后,又提供 IntentService呢?

Service 默认是运行在主线程的,如果我们需要在 Service 中处理一些耗时任务,那么我们还需要去手动的创建线程或者使用线程池去处理耗时任务(否则会出现ANR),然后在处理完以后手动关闭Service,而 IntentService 已经帮我们做好了这些工作,我们只需要在 onHandleIntent中写上耗时任务的代码,就可以在子线程中去执行,因为 onHandleIntent是运行在子线程中的,并且在任务执行完以后,IntentService 会自己执行stopSelf(startId)方法,自行关闭。

用 IntentService 有什么好处呢?

首先,我们省去了在 Service 中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止 Service。

ServiceIntent 的使用例子,可参考 源码;执行效果如下图:

java 复制代码
//\frameworks\base\core\java\android\app\IntentService.java

/**
 * IntentService is an extension of the {@link Service} component class that
 * handles asynchronous requests (expressed as {@link Intent}s) on demand.
 * Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.
 */
@Deprecated
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    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) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }
    ...

    @Override
    public void onCreate() {
        super.onCreate();
        //这边就使用到了 HandlerThread 的工具类
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        // mServiceHandler 本质是 Handler,使用了 mServiceLooper 去创建
        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);
    }

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

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

    /**
     * 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)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
  • linux的epoll机制

    • epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
kotlin 复制代码
/**
 * A Handler allows you to send and process Message and Runnable
 * objects associated with a thread's MessageQueue. Each Handler
 * instance is associated with a single thread and that thread's message
 * queue. When you create a new Handler it is bound to a Looper.
 * It will deliver messages and runnables to that Looper's message
 * queue and execute them on that Looper's thread.
 *
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed at some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 *
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */
public class Handler {

}

参考资料

  1. Android异步消息处理机制完全解析,带你从源码的角度彻底理解
  2. Android O Handler/HandlerThread/AsyncTask/IntentService
  3. Handler.postDelayed的原理
  4. IdleHandler 的原理分析和妙用
  5. 什么是Handler的同步屏障机制?
  6. 【面试题】handler内存泄露的根本原因是?
  7. 在Android中为啥建议你用Message.obtain()方法获取Message对象,而不是使用new关键字?
  8. HandlerThread的使用场景和用法
  9. 【视频:Android开发】handler源码解析,从原理到面试
  10. Android全面解析之由浅及深Handler消息机制
相关推荐
数据猎手小k3 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小104 小时前
JavaWeb项目-----博客系统
android
风和先行4 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.5 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰6 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶6 小时前
Android——网络请求
android
干一行,爱一行6 小时前
android camera data -> surface 显示
android
断墨先生6 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员8 小时前
PHP常量
android·ide·android studio
萌面小侠Plus9 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机