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


线程状态


/**
 * 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值