您的位置:首页 > 移动开发 > Android开发

Android Handler机制

2015-09-11 16:31 513 查看
转载请注明出处:http://blog.csdn.net/mr_liabill/article/details/48373779   来自《LiaBin的博客》

Handler

Handler使用子线程looper

public class MyThread extends Thread {
private Handler handler;

@Override
public void run() {
super.run();
//此时当前线程跟looper绑定,如果没调用这个方法,那么
//handler = new Handler();就会抛出Can't create handler inside thread that has not called Looper.prepare()异常
Looper.prepare();//此时生成的loop对象可以通过Looper.myLooper()获取。实际调用sThreadLocal.set(new Looper(quitAllowed));
//Handler无参数的构造函数内部调用了mLooper= Looper.myLooper();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("LiaBin", "MyThread handleMessage");
}
};
//handler = new Handler(Looper.myLooper());//等价于上面的语法,都是获取当前线程的looper
Looper.loop();//开启循环取消息
}

//提供一个退出方法,否则Loop方法在无限循环,MyThread终止不了
public void quit() {
if (Looper.myLooper() != null) {
Looper.myLooper().quit();
}
}

public Handler getHandler() {
return handler;
}
}


如果此时main线程想给这个线程发消息

MyThread myThread = new MyThread();
myThread.start();
//发送消息到myThread线程对应的looper队列中
myThread.getHandler().sendEmptyMessage(0);


此时注意:myThread线程一直在run,除非调用quit方法

另一方面:

handler = new Handler(Looper.getMainLooper());//获取main线程的looper

handler.sendEmptyMessage(0);//消息被发送到main线程中,然后处理

Handler使用main线程looper

public class TestActivity extends AppCompatActivity {

private Handler handler= new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
handler.sendEmptyMessage(0);
new TestThread(handler).start();
}
}

public class TestThread extends Thread {
private Handler handler;

public TestThread(Handler handler) {
this.handler = handler;
}

@Override
public void run() {
super.run();
//dosomething...
handler.sendEmptyMessage(0);
}
}

此时实际上handler是在main线程中创建,默认使用的是main线程的looper,所以消息在main线程中处理

activity是运行在main线程中的,启动任何一个activity,首先都会调用ActivityThread的main方法,可以看到调用了prepareMainLooper为mian线程创建了looper,之后Looper.loop()。

public static void main(String[] args) {
..............
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
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");
}
}


post方法

public void onClick(View v){
new Thread(new Runnable(){
public void run(){
final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
handler.post(new Runnable(){
public void run(){
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();}
handler的post方法接受一个runnable参数,但是不是runnable实际上是运行handler对应的线程中的,而不是子线程,注意区分Thread和Runnable的区别

总结

1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//mQueue跟上面的looper一一对应
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);//把这个msg放到looper对应的消息队列中了
}


Looper

几个静态方法

Looper.perpare()   Thread中调用,那么就为当前线程绑定了looper,如果在activity,service等中调用,那么就是为main线程绑定looper,此时这样调用是多余了,因为在main线程中早已经执行了prepareMainLooper方法

Looper.myLooper()  Thread中调用,那么获取的就是当前线程perpare创建的looper,如果是在activity,service等中调用,那么其实是等效于getMainLooper的,因为返回的都是main线程的looper

Looper.prepareMainLooper()  赋值main线程的looper,sMainLooper = myLooper();如果在子线程中就不要调用这个方法了,而是调用perpare方法

Looper.getMainLooper()  获取main线程的looper,如果需要在子线程中发送消息到main线程,如果不想把handler参数传进来,那么可以使用Handler hander=new Handler(Looper.getMainLooper())

quit/quitSafely  looper的消息队列不再接收消息,所以此时再用handler发消息是接收不到的

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));
}

public static
@Nullable
Looper myLooper() {
return sThreadLocal.get();
}

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;
for (; ; ) { //不间断的取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
...........
}
}

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

   public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}


activity,sercice等默认都是运行在main线程中的

Activit,service默认运行于main主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件
Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的
public final class ActivityThread {
......

public static final void main(String[] args) {
......

Looper.prepareMainLooper();

......

ActivityThread thread = new ActivityThread();
thread.attach(false);

......

Looper.loop();

......

thread.detach();

......
}
}


所以如果以下代码
public class TestActivity extends AppCompatActivity {

private Handler handler= new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
}


此时handler发送的消息是在main线程中处理的,handler没有参数的构造函数默认调用如下方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

mLooper = Looper.myLooper();//获取当前线程的looper,因为当前线程是main线程,所以handler发送的消息是在main线程中处理的

注意区分,handler在activity,service中创建还是在Thread内部创建的区别

MessageQueue

面试有提到MessageQueue的原理,当即就说,不就是一个简单的队列呗,先进先出,那这个message什么时候进什么时候出队列,比如以下两行伪代码

sendMessageDelayed(msg1,5000);

sendMessage(msg2);

此时msg1和msg2谁先进消息队列,这个时候我就犯难了,直觉告诉我,应该是msg2先进队列,因为最后肯定是msg2先被处理的,先进先出嘛。。

面试官再问真是这样么,如果我此时再sendMessageDelayed(msg3,2000);那么msg3在队列的什么位置,msg1,msg3怎么先后处理,然后就GG了

Handler.MessageQueue源码分析

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
不管是sendMessage,post还是sendMessageDelayed,postDelayed最后都进入sendMessageAtTime方法,

SystemClock.uptimeMillis() + delayMillis

这段代码是关键,把系统当前时间+delay的时间作为参数传递下去

继续往下分析,调用了queue.enqueueMessage

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}


进入MessageQueue类,注意when参数,就是上面的SystemClock.uptimeMillis() + delayMillis

boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue.  Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}


这下终于豁然开朗了

Message p = mMessages;  //mMessages应该就是队列的头节点了

如果刚进来消息的when比头结点的when还小,那么把这个message当作头节点,否则,则在队列中根据when来判断插入合适的位置。并不是按照进来一个消息就放在队列尾这种方式

如此一来,就能解决上面的疑惑了,

sendMessageDelayed(msg1,5000);

sendMessage(msg2);
sendMessageDelayed(msg3,2000);

还是按照send的先后顺序进队列,msg1,msg2,msg3,但是队列中的位置却是

尾 msg1  msg3 msg2 头

所以最后handlemessage的先后顺序是msg2 msg3 msg1

我们都知道Loop.loop()不断的通过messagequeue.next()从消息队列中取消息。在来看看messagequeue的next方法,下面抽取关键代码段

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;
}
final long now = SystemClock.uptimeMillis();表示的是当前时间

如果队列头结点的when比now当前时间还大,说明还没到delay的时间,return null,否则才处理这个message

总结

最后总结一下:

1. MessageQueue 通过链表方式实现

2. message插入到队列的哪个位置,是通过时间点来判断,SystemClock.uptimeMillis() + delayMillis(当前时间+delay时间),这个时间点越大越在队列的后部,越晚handler

3. 严格来说MessageQueue先进先出并不准确,因为并不是简单的插入到队列尾
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android Handler Queue