零基础学Android源码之Handler机制
来源:互联网 发布:mysql safe error 编辑:程序博客网 时间:2024/05/23 20:45
前言
当我们在非主线程想操作UI时,其中一个方法便是使用Handler。Handler作为Android中的异步消息处理机制,学会使用它是十分重要的,其次也是面试中的常客。对于Android新手来说,在不理解源码的情况下使用起来也是云里雾里,今天就和大家一起探讨一下Handler的实现原理,也让大家有一个更清晰的理解。
代码示例
首先我们看下handler是怎么使用的
private TextView mTextView;private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { mTextView.setText(msg.obj.toString()); }};@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.text); new Thread(new Runnable() { @Override public void run() { //耗时操作获取数据 String data = "test data"; Message msg = Message.obtain(); msg.obj = data; mHandler.sendMessage(msg); } }).start();}
代码看起来很简单,创建一个handler,之后使用handler发送message,从这两个点我们去分析下代码。
源码分析
看源码之前,我们先对以下几个类有个初步映像,这几个类就是支撑起handler的关键
+ Handler
+ Looper
+ MessageQueue
+ Message
接下来我会把handler机制分为两部分来讲
+ handler的准备
+ handler发送消息
1. handler的准备
有人可能会说,handler的准备不就是new handler的时候吗?这个其实只是准备的其中一个步骤,一切的准备则是要从main函数说起。
我们都知道,java的入口函数是main函数,新手可能会认为Android的入口是主Activity,实际上也是main函数,这个函数的实现是在ThreadActivity
这个类中,我们一起看下
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");}
找一下我们之前说到的关键字,可以看到有一个Looper,Looper用来干什么的呢?如其字面意思环,有了它线程就可以循环的去读取数据。我们先看下两个关键的方法Looper.prepareMainLooper()
与Looper.loop()
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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(); }}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();}
在Looper.prepareMainLooper()
方法中,调用了prepare()
方法,这个方法给sThreadLocal
赋值,sThreadLocal
是一个ThreadLocal<Looper>
对象,那么ThreadLocal
又是什么呢,我们可以把它理解为线程中的一个容器,这里new 了一个Looper并把它放在当前线程的容器里,代码中做了判断如果不为空就抛出一个异常,说明Looper对象只有一个。此时,当前线程就和Looper有了关联,一旦线程从自己的容器中取出Looper时,就可以使用Looper来循环读取消息。因为是在main函数中调用的,所以是把Looper放在了主线程即UI线程的容器里,这里一定要记住。
再来看看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 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); } } ...... msg.recycleUnchecked(); }}
在loop()
方法中,先从当前线程的容器内把Looper对象取出来,没错,当前线程要开始取数据啦,但是数据从哪里来呢?Looper对象里有一个MessageQueue,这里简单介绍下MessageQueue,MessageQueue是一个单向链表,链表可以简单的理解为一个队列,但是这个队列是单向的,每一个对象有一个变量指向了下一个对象。源码中拿到MessageQueue后,便进入了一个死循环,不断的调用next()
方法,取出MessageQueue中的Message。好了关键点来了,拿到的messgae调用了msg.target.dispatchMessage(msg)
而这个target在Message的源码中是这样的Handler target;
也就是说target就是一个handler,我们再看下dispatchMessage
这个方法干了什么。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}
首先判断有没有回调,没有则调用handleMessage(msg);
是不是很熟悉,这个不就是我们在new handler的时候重写的方法吗?没错,走到这一步的时候handler就会回调我们重写的方法,此时handler就准备完成啦。(这里在简单提一下handleCallback
,这个是在handler.post()时回调的,原理和本文讲的类似,就不在累述,有兴趣的童鞋可以自己去看下源码)那又有一个疑问了,源码中最后调用了msg.target.dispatchMessage(msg)
,但是messgae怎么会有target呢,这个就是我们要分析的第二个过程,handler发送消息。
2. handler发送消息
当我们想使用handler的时候,会调用handler.sendMessage(msg);
看下源码
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; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
最终代码会走到enqueueMessage
方法中,这个方法第一句代码便是msg.target = this;
this对象便是我们new出来的handler,此时messgae便拿到了handler,有handler的message便可以顺利调用msg.target.dispatchMessage(msg)
至此,handler的整个流程就走完了。
总结
最后再简单理一遍思路
- 在调用prepare时,Looper中有一个ThreadLocal对象,这个对象能够把Looper这个对象保存在当前线程中,且只会保存一次。
- 在调用loop的时候,Looper会从当前线程中把Looper对象取出来,并从这个对象中取出MessageQueue,接下来会进入一个死循环,不断的从这个MessageQueue中读取Message并调用
msg.target.dispatchMessage(msg)
该方法最终会调用我们重写的handlerMessage()
。 - 这个msg.target就是一个handler,而这个handler则是在用户发送messgae时传递进来的,即:handler.sendMessage(msg),在send的时候msg.target=this此时便把handler传递进来了。
- 零基础学Android源码之Handler机制
- Android源码分析之Handler机制
- Android源码剖析之-------Handler机制详解
- Android源码分析之Handler消息机制
- Android基础之Handler机制学习
- <java基础>零起点学Android(五)之应用窗口
- Android之handler机制
- Android之Handler机制
- Android之Handler机制
- Android之Handler机制
- Android Handler机制 源码解析
- android Handler机制源码详解
- Android Handler机制源码分析
- Android Handler机制源码解析
- android handler机制源码解析
- Android单排上王者系列之Handler机制源码分析
- 《android framework常用api源码分析》之handler消息机制
- android 机制之handler机制
- 模拟斗地主洗牌和发牌,牌没有排序 看牌时有顺序
- Zabbix-windows
- ListView的多条目展示
- 关于HTTP协议,一篇就够了
- Dell PowerEdge RAID控制器存在一个潜在问题
- 零基础学Android源码之Handler机制
- jsp-七大动作
- 05-mysql中的查询(第一章)
- finally的特点
- error C4772: #import 引用了缺少的类型库中的类型;“__missing_type__”用作占位符 解决办法
- Linux下的串口编程
- hibernate多表查询
- EDID使用说明
- socket recv()函数返回0的一种情况