Android Handler机制
2015-09-11 16:31
513 查看
转载请注明出处:http://blog.csdn.net/mr_liabill/article/details/48373779 来自《LiaBin的博客》
Handler使用子线程looper
如果此时main线程想给这个线程发消息
此时注意:myThread线程一直在run,除非调用quit方法
另一方面:
handler = new Handler(Looper.getMainLooper());//获取main线程的looper
handler.sendEmptyMessage(0);//消息被发送到main线程中,然后处理
此时实际上handler是在main线程中创建,默认使用的是main线程的looper,所以消息在main线程中处理
activity是运行在main线程中的,启动任何一个activity,首先都会调用ActivityThread的main方法,可以看到调用了prepareMainLooper为mian线程创建了looper,之后Looper.loop()。
总结
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)最终调用的方法。
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发消息是接收不到的
activity,sercice等默认都是运行在main线程中的
Activit,service默认运行于main主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件
Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的
所以如果以下代码
此时handler发送的消息是在main线程中处理的,handler没有参数的构造函数默认调用如下方法
mLooper = Looper.myLooper();//获取当前线程的looper,因为当前线程是main线程,所以handler发送的消息是在main线程中处理的
注意区分,handler在activity,service中创建还是在Thread内部创建的区别
sendMessageDelayed(msg1,5000);
sendMessage(msg2);
此时msg1和msg2谁先进消息队列,这个时候我就犯难了,直觉告诉我,应该是msg2先进队列,因为最后肯定是msg2先被处理的,先进先出嘛。。
面试官再问真是这样么,如果我此时再sendMessageDelayed(msg3,2000);那么msg3在队列的什么位置,msg1,msg3怎么先后处理,然后就GG了
SystemClock.uptimeMillis() + delayMillis
这段代码是关键,把系统当前时间+delay的时间作为参数传递下去
继续往下分析,调用了queue.enqueueMessage
进入MessageQueue类,注意when参数,就是上面的SystemClock.uptimeMillis() + delayMillis
这下终于豁然开朗了
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方法,下面抽取关键代码段
如果队列头结点的when比now当前时间还大,说明还没到delay的时间,return null,否则才处理这个message
总结
最后总结一下:
1. MessageQueue 通过链表方式实现
2. message插入到队列的哪个位置,是通过时间点来判断,SystemClock.uptimeMillis() + delayMillis(当前时间+delay时间),这个时间点越大越在队列的后部,越晚handler
3. 严格来说MessageQueue先进先出并不准确,因为并不是简单的插入到队列尾
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先进先出并不准确,因为并不是简单的插入到队列尾
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories