android源码阅读--线程间通信Handler消息机制

来源:互联网 发布:淘宝网店怎么改价格 编辑:程序博客网 时间:2024/05/16 05:38


线程状态


/**
 * A representation of a thread's state.A given thread may only be in one
 * state at a time.
 */
public enum State{
   
/**
     * The thread has been created, buthas never been started.
     */
   
NEW
,
   
/**
     * The thread may be run.
     */
   
RUNNABLE
,
   
/**
     * The thread is blocked and waitingfor a lock.
     */
   
BLOCKED
,
   
/**
     * The thread is waiting.
     */
   
WAITING
,
   
/**
     * The thread is waiting for aspecified amount of time.
     */
   
TIMED_WAITING
,
   
/**
     * The thread has been terminated.
     */
   
TERMINATED
}

 

所谓的阻塞,就是线程能够运行,但是某个条件阻止它的运行,当线程处于阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间,直到线程重新进入就绪状态,它才有可能执行操作。就绪并代表是在运行啊,所谓的就绪,就是可运行也可不运行,只要调度器分配时间片给线程,线程就可以运行,因为我们都知道,调度器是如何分配线程,是不确定的。为什么任务会进入阻塞的状态,一般有以下几个原因:

        1.通过调用sleep(milliseconds)使任务进入休眠状态,在这种情况下,任务在指定的时间内不会运行;

        2.通过调用wait()使线程挂起,直到线程得到了notify()notifyAll()消息(或者java SE5java.util.concurrent类库中等价的signal()signalAll()),线程才会进入就绪状态;

        3.任务在等到某个输入或输出完成;

        4.任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取这个锁;

Android线程间通信(目的)

主要方式handler消息机制和线程间同步

线程间可以共享地址空间,线程间通信主要使用共享变量, handler消息机制也是在两个线程间共享了消息队列,asyncTask内部实现也使用了Handler机制

特殊线程UI线程(主要任务实现工作线程与主线程通信)

所有页面更新要在主线程完成?ANR

ActivityThread与主线程

每一个运行的App对应一个进程,应用进程启动后会创建一个主线程运行,ActivityThread中的main函数是app的入口函数

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER
,"ActivityThreadMain");
   
SamplingProfilerIntegration.start();

   
//CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectivelyenable it later (via
    // StrictMode) on debug builds, butusing DropBox, not logs.
   
CloseGuard.setEnabled(false);

   
Environment.initForCurrentUser();

   
//Set the reporter for event logging in libcore
   
EventLogger.setReporter(newEventLoggingReporter());

   
//Make sure TrustedCertificateStore looks in the right place for CA certificates
   
finalFile configDir =Environment.getUserConfigDirectory(UserHandle.myUserId());
   
TrustedCertificateStore.setDefaultUserDirectory(configDir);

   
Process.setArgV0("<pre-initialized>");

   
Looper.prepareMainLooper();

   
ActivityThreadthread = newActivityThread();
   
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");
}

可以看到main函数中,通过Looper.prepareMainLooper()和Looper.loop()为主线程创建了消息循环,所以默认创建的线程对象是没有消息循环的,工作线程需要我们自己创建消息循环。

final HandlergetHandler() {
   
returnmH;
}

final HmH =newH();

private class HextendsHandler {
   
publicstatic final int LAUNCH_ACTIVITY         =100;
    public static final int
PAUSE_ACTIVITY          =101;
    public static final int
PAUSE_ACTIVITY_FINISHING=102;
    public static final int
STOP_ACTIVITY_SHOW      =103;
    public static final int
STOP_ACTIVITY_HIDE      =104;
    public static final int
SHOW_WINDOW             =105;
    public static final int
HIDE_WINDOW             =106;
    public static final int
RESUME_ACTIVITY         =107;
    public static final int
SEND_RESULT             =108;
    public static final int
DESTROY_ACTIVITY        =109;
    public static final int
BIND_APPLICATION        =110;
    public static final int
EXIT_APPLICATION        =111;

 

 

ActivityThread持有一个H对象mH,H继承自Handler,这里mH创建于主线程,持有的消息循环也就是主线程的消息循环,H的handleMessage主要处理ApplicationThread与AMS通信的一些消息处理。

Looper运行在对应的线程中,调用对应的Handler的handleMessage方法也是在Looper对应的线程执行,所以当我们在工作线程通过主线程handler发送消息是,handleMessage实际是在主线程执行的,界面更新操作也就不会有问题。

实现机制

Handler消息机制包括发送接收者handler,消息队列MessageQueue ,消息循环 Looper几个部分  

Handler向队列入队消息,Looper循环从队列取出消息交给对应的Handler处理,消息队列为空的话如果   Looper仍不停轮询队列是否有消息的话是比较耗性能的(有点low);handler机制使用了epoll机制,通知Looper消息队列可读

管道与epoll

epoll是一种同步阻塞IO,用于多路复用监听IO设备事件,可以监听多路输入(可以理解为多个消息队列),这里对单独的Handler机制有点大材小用,只监听一路;总之,epoll提供给Handler一种通知机制触发Looper读消息

而epoll监听的并不是消息队列这个对象,而是底层的管道(管道是进程间通信的一种方式,内核空间中的一块缓冲区,有读端和写端),新的android版本使用了eventfd,另一种进程间通信方式

在Java层,Looper循环从MessageQueue取消息,触发底层epoll阻塞等待可读事件,handler在java层向MessageQueue插入消息,同时在底层管道写入事件,当底层可读消息触发epoll处理返回,Looper调用结束阻塞,从消息队列取出消息处理

MessageQueue是连接Java层与JNI层的通道,我们通过handler发送和接收的消息其实只在Java层,JNI层只负责消息循环的读取控制操作

MessageQueue使用链表来存储所有消息,所有的Looper使用ThreadLocal存储,可以通过Looper类的静态方法myLooper获取当前线程对应的Looper

代码分析

创建Looper对象,并存储到ThreadLocal,创建MessageQueue

/** Initialize the current threadas a looper.
  * This gives you a chance to createhandlers that then reference
  * this looper, before actually startingthe loop. Be sure to call
  * {@link #loop()} after callingthis method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(
true);
}

private static void prepare(booleanquitAllowed){
   
if(sThreadLocal.get() !=null){
       
thrownew RuntimeException("Only one Looper may be created per thread");
   
}
   
sThreadLocal.set(newLooper(quitAllowed));
}

 

启动循环

/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */public static void loop() {    final Looper me = myLooper();    if (me == null) {        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) {            // 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);        if (logging != null) {            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);        }        // Make sure that during the course of dispatching the        // identity of the thread wasn't corrupted.        final long newIdent = Binder.clearCallingIdentity();        if (ident != newIdent) {            Log.wtf(TAG, "Thread identity changed from 0x"                    + Long.toHexString(ident) + " to 0x"                    + Long.toHexString(newIdent) + " while dispatching to "                    + msg.target.getClass().getName() + " "                    + msg.callback + " what=" + msg.what);        }        msg.recycleUnchecked();    }}

 

 

Message next() {    // Return here if the message loop has already quit and been disposed.    // This can happen if the application tries to restart a looper after quit    // which is not supported.    final long ptr = mPtr;    if (ptr == 0) {        return null;    }    int pendingIdleHandlerCount = -1; // -1 only during first iteration    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;            }            // Process the quit message now that all pending messages have been handled.            if (mQuitting) {                dispose();                return null;            }            // If first time idle, then get the number of idlers to run.            // Idle handles only run if the queue is empty or if the first message            // in the queue (possibly a barrier) is due to be handled in the future.            if (pendingIdleHandlerCount < 0                    && (mMessages == null || now < mMessages.when)) {                pendingIdleHandlerCount = mIdleHandlers.size();            }            if (pendingIdleHandlerCount <= 0) {                // No idle handlers to run.  Loop and wait some more.                mBlocked = true;                continue;            }            if (mPendingIdleHandlers == null) {                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];            }            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);        }        // Run the idle handlers.        // We only ever reach this code block during the first iteration.        for (int i = 0; i < pendingIdleHandlerCount; i++) {            final IdleHandler idler = mPendingIdleHandlers[i];            mPendingIdleHandlers[i] = null; // release the reference to the handler            boolean keep = false;            try {                keep = idler.queueIdle();            } catch (Throwable t) {                Log.wtf(TAG, "IdleHandler threw exception", t);            }            if (!keep) {                synchronized (this) {                    mIdleHandlers.remove(idler);                }            }        }        // Reset the idle handler count to 0 so we do not run them again.        pendingIdleHandlerCount = 0;        // While calling an idle handler, a new message could have been delivered        // so go back and look again for a pending message without waiting.        nextPollTimeoutMillis = 0;    }}

 

阅读全文
1 0