带你从源码看Android Handler 异步消息处理机制完全解析
来源:互联网 发布:淘宝卖的苍蝇水叫什么 编辑:程序博客网 时间:2024/05/26 12:55
带你从源码看Android Handler 异步消息处理机制完全解析
android studio 2.2.2 SDK版本 25.0.3
在2.2.2的版本中修改代码调试 真的是超级快,特别爽 两三秒就运行了。
写了一个小demo 分别在主线程 子线程中都创建了handler 最后都实现textView 内容的更新。
上代码:
import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private static final int UPDATE_TEXT = 1; private static final int UPDATE_TEXT_2 = 2; private static final String TAG = "MainActivity"; private TextView mTextContentTextView; private TextView mSecondTextContent; private int anInt = 0; private int mRes = 0; public Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextContentTextView = (TextView) findViewById(R.id.tv_content); mSecondTextContent = (TextView) findViewById(R.id.tv_content_2); Log.d(TAG, "onCreate thread id :" + Thread.currentThread().getId()); changeContent(); } /** * handler */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what){ case UPDATE_TEXT: Log.d(TAG, "handler thread id :" + Thread.currentThread().getId()); mTextContentTextView.setText(String.valueOf(anInt)); break; case UPDATE_TEXT_2: mSecondTextContent.setText(String.valueOf(mRes)); break; default: break; } } }; private void changeContent() { new Thread() { @Override public void run() { super.run(); while (true){ Log.d(TAG, "changeContent thread id :" + Thread.currentThread().getId()); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (anInt == 1000) { anInt = 0; } anInt++; Message message = new Message(); message.what = UPDATE_TEXT; handler.sendMessage(message); } private void getContent(){ new Thread(new Runnable() { private int aa = 0; @Override public void run() { while (true){ Log.d(TAG, "getContent thread id :" + Thread.currentThread().getId()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (aa >= 100){ aa = 0; } aa+=2; Message mMessage = new Message(); mMessage.what=1; mMessage.arg1=aa; mHandler.sendMessage(mMessage); } } }).start(); }}
Log 输出结果:
MainActivity: onCreate thread id :1 主线程id
D/MainActivity: changeContent thread id :814
D/MainActivity: changeContent anotherThread thread id :815
D/MainActivity: getContent thread id :816
D/MainActivity: getContent thread id :816
D/MainActivity: changeContent mHandler thread id :815 处理结果在子线程中运行
D/MainActivity: changeContent thread id :814
D/MainActivity: handler thread id :1 处理结果在主线程中运行
D/MainActivity: getContent thread id :816
D/MainActivity: changeContent mHandler thread id :815
D/MainActivity: getContent thread id :816
D/MainActivity: changeContent mHandler thread id :815
D/MainActivity: changeContent thread id :814
D/MainActivity: handler thread id :1
在看源码之前要知道,handler 是用于线程之间传递数据的。
首先看demo 我在主线程和子线程都创建了handler。 仔细观察会发现稍有不同,在子线程创建handler的时候会先执行Looper.prepare(); 如果不写这段这段代码就会提示Can’t create handler inside thread that has not called Looper.prepare()。 主线程中创建的时候并不要啊。为什么呢,那我们就去看看handler的源码,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对象 如果为空就会抛出异常。那具体是怎么为空的呢,继续往下看:
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
那这个sThreadLocal 是什么,,,继续看:
static final ThreadLocal sThreadLocal = new ThreadLocal();
ThreadLocal 是什么,进去看看,注释提示该类提供线程的一些值的存储。可以在指定线程内存储数据,存储以后,只有指定线程可以得到存储数据。那就要找在哪儿set数据的。那好肯定是在looper 中的prepare() 中了。代码如下:
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)); }
new Looper()的代码如下:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); //创建消息队列 mThread = Thread.currentThread(); }
Looper中消息怎么进入怎么处理,后面再说,先看看主线程中 创建handler的时候好像没有执行Looper.prepare() 方法啊,怎么没有报错。 其实主线程 Activity的入口在这,在Activity 的源码找到ActivityThread 这里面定义的东西很多,包括 handler消息的处理 ApplicationThread 中的sendMessage。看main() 函数 如下:
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 selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); 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"); }
是不是跟子线程的创建有点差不多,先看这一句:Looper.prepareMainLooper(); 进Looper看
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
prepareMainLooper(),创建当前线程的looper ,它会在应用环境创建的时候就会创建。
sMainThreadHandler = thread.getHandler(); 在 sMainThreadHandler 管理着activity的生命周期,以及UI的一些操作 具体的代码就不贴出来了太长了。
然后代码就运行到了Looper.loop(); 在我这个SDK 版本中的代码如下:(不同的SDK 会有不同)
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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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(); } }
可以看到在for (;;) 这一句程序进入了死循环(永真循环)然后 Message msg = queue.next(); 这个就是将消息从消息队列取出的方法。代码在MessageQueue类中 next(),主要实现的功能就是将当前MessageQueue消息队列中的mMessages 出队 ,然后将下一个消息成为mMessages。取出消息后执行 msg.target.dispatchMessage(msg); 这一句,这里的msg.target 是什么呢,这个要看消息发送的时候代码如下(在Handler类中):
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; 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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //将当前的handler 给msg的target if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
好了知道msg.target 是什么 那就看看dispatchMessage(msg)做了什么。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
在dispatchMessage(Message msg) 中对创建handler时重写的handleMessage(),在这里进行了回调。
最后总结一下:
每个线程最多只能有一个Looper对象,Looper内部有一个消息队列,当一个线程启用Looper后它就变成了一个Looper线程,loop() 方法中会不断从消息队列中取出消息并执行;Handler 线程之间传递数据,实现向MessageQueue中添加消息,在loop()到消息后执行消息,整个过程是异步的,从上面的构造方法中可以看到,handler会关联当前所创建的looper;在一个线程中是可以创建多个Handler的;在Handler中有多种发送消息的方法 比如post(Runnable r ) 但是最终都是调用sendMessageAtTime();
继续深挖 这个线程不是进入了loop()死循环吗,,那这个Handler是怎么运行的,,,这里就涉及到Android中采用Binder作为IPC机制,,,,用于进程间的通信。。。后面在仔细看看吧。就到这里了。
关注我的微信公众号,每天都有优质技术文章,搞笑GIF图片推送哦。
- 带你从源码看Android Handler 异步消息处理机制完全解析
- 【 Android】handler异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解(Handler+Message处理机制)
- handler(7) Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android中handler消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- 【转】Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- *计算机学习笔记杂项
- apache ftp 实现上传下载功能
- Android 6.0获取IMEI号是出错,动态获取权限
- 阶段性纠错邀请
- Android 布局详解 -三表格布局(TableLayout)以及重要属性
- 带你从源码看Android Handler 异步消息处理机制完全解析
- 告诉你1年读100本书的方法
- 移动端meta标签的使用
- 用 AdvStringGrid addbutton 中添加图片(BMP/JPG)
- lua UTF8字符串操作,截取,索引
- Spring__容器启动源码
- 导出excel
- Log4J 配置详解
- impala-shell 命令行选项