/**
* 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 SE5的java.util.concurrent类库中等价的signal()或signalAll()),线程才会进入就绪状态;
3.任务在等到某个输入或输出完成;
4.任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取这个锁;
主要方式handler消息机制和线程间同步
线程间可以共享地址空间,线程间通信主要使用共享变量, handler消息机制也是在两个线程间共享了消息队列,asyncTask内部实现也使用了Handler机制
所有页面更新要在主线程完成?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("
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; } }