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消息机制
相关推荐
Flywith243 小时前
【每日一技】Raycast 实现 scrcpy 的快捷显示隐藏
android·前端
没有了遇见4 小时前
Android(Coil,Glide)大量图片加载缓存清理问题(二 Coil处理)
android
城东米粉儿4 小时前
Android Dagger2笔记
android
没有了遇见4 小时前
Android(Coil,Glide)大量图片加载缓存清理问题(一)
android
恋猫de小郭4 小时前
谷歌 Genkit Dart 正式发布:现在可以使用 Dart 和 Flutter 构建全栈 AI 应用
android·前端·flutter
曾经我也有梦想6 小时前
Day4 Kotlin 高级特性
android
simplepeng6 小时前
Compose Multiplatform 中的 Navigation 3
android
Kapaseker12 小时前
一杯美式讲完 Sealed Class
android·kotlin
冬奇Lab1 天前
PowerManagerService(下):Doze模式与电池优化
android·源码阅读
砖厂小工1 天前
Compose 中函数引用 vs Lambda:到底该用哪个?
android