Android Handler 源码流程分析

用了handler这么久,一直没有深入了解过,用了些时间认真看了一下,决定写下来,对需要的人是一种分享,更是对自己的记录吧,先看一段平时handler处理异步消息的代码,应该都很熟悉,如下:

public class MainActivity extends Activity {
	TextView mTextView = null;

	Handler handler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			mTextView.setText("get msg arg1 value is " + msg.arg1);
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mTextView = (TextView) findViewById(R.id.textview);
		new Thread(new Runnable() {

			@Override
			public void run() {
				Message msg = new Message();
				msg.arg1 = 1;
				handler.sendMessage(msg);
			}
		}).start();
	}

}

很简单,运行一下程序,会得到mTextView控件的内容显示"get msg arg1 value is 1", 那么下面我们就来从源码角度来看一下这个简单的例子的调用流程。

首先我们要知道android程序启动时,系统自动为我们在主线程当中调用了Looper类的prepare()方法和loop()方法,具体参考源码中android.app包下的ActivityThread.java中的
main()方法,那么首先先从Looper类的prepare()方法入手,源码位置在android.os包下面,包括下文涉及到的Handler.java, Message.java, MessageQueue.java等类都在这个包下面。

Looper的prepare()方法源码如下:

    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

使用sThreadLocal对象中来set,get一个Looper对象, 方法中调用get()方法时不为空则抛出异常,可见每个线程只能set()一次Looper对象,由此可知每个线程最多且只有一个Looper对象。
我们看到set()方法中new了一个Looper对象,看下Looper的构造函数的源码,如下:

    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

可见调用Looper的构造方法时,会new一个消息队列对象mQueue,所以可以看到一个Looper对象对应一个MessageQueue对象,此mQueue对象会在下面new Handler()时用到,prepare()方法分析到这先。

下面来看Looper类的loop()方法,源码如下:

    public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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();
        
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // 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);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }

                msg.target.dispatchMessage(msg);

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // 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.recycle();
            }
        }
    }

loop()方法中首先调用了myLooper()方法,先来看下myLooper()方法:

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


可见myLooper()方法就是此线程对应的那个Looper对象,也就是调用在prepare()方法时new出来的那个Looper对象,接着继续看loop()方法,其中有一个死循环while(true), 在死循环中使用局部变量queue的next()方法来一个一个的取出Message对象,queue其实就是Looper对象对应的那个MessageQueue对象,也就是在new Looper对象时new 出来的那个MessageQueue对象,然后接着向下看,有一句msg.target.dispatchMessage(msg); 这里其实就是将取出来的Message对象分发消息到handleMessge()方法,此处后面再说(to be continued...)

有了上面的关于Looper的铺垫,下面我们来看文章开头例子程序的子线程中代码,关注在子线程中sendMessage()怎么样最终执行了主线程中handler对象的handleMessage()方法。

首先来看new Handler()中做了什么,像分析Looper时一样,来看Handler()的构造函数,源码如下:

    public Handler() {
        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 = null;
    }

同样调用了Looper的myLooper()方法来获取Looper在prepare()时new 出来的那个Looper对象,然后同样得到 new Looper时new 出来的 mQueue这个消息队列对象,上面已经说到mQueue这个对象会在new Handler()时用到,Handler对象和Looper对象中保持的是一个mQueue引用, 也就是说一个线程对应一个消息队列对象。

接下里我们来看程序例子子线程当中做了事情,首先 Message msg = new Message() 这一句,Message构造函数的源码如下:

    public Message() {
    }

可见是空的,简单,但我们此时要注意一点,Message类中有声明了一个Handler 类型的成员变量叫做 target,后面在Looper中会用到这个target来分发消息,后面会写到。

接着程序往下走,下一句执行了msg.arg1 = 1 这一句是将msg对象的arg1变量设为 1,

然后程序中执行了 handler.sendMessage()方法,sendMessage源码如下:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }


接着看sendMessageDelayed((Message msg, long delayMillis)方法,如下:

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }


接着看sendMessageAtTime(Message msg, long uptimeMillis)方法,如下:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }


此方法中重要的一句为 msg.target = this,  上面在new Message()对象的时候已经提醒大家注意Message类中target这个成员变量,

继续向下调用了MessageQueue的enqueueMessage(Message msg, long when)方法,现在去看看这个方法,如下:

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }


代码比较长,主要的工作就是将消息对象按照时间顺序入列到消息队列里面,综合上一步来讲,就是加入到消息队列当中的这个消息对象的target属性是上文中的handler对象。

现在将消息加到消息队列以后,消息队列有了消息,那么我们回过头接着看上面的to be continued处的loop()方法,loop()方法会将此消息对象取出,在上面分析到了loop()方法中执行的这一句代码msg.target.dispatchMessage(msg), 那么由以上的分析可知,此处的msg.target即为上文的handler对象,那么此处即是调用handler对象的dispatchMessage(Message msg)方法,那来看一下Handler类中的这个方法源码做了什么事情:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


第一个if判断是使用post()方法发送消息请求时会用到的,由于我们new Handler()对象时将mCallback初始化为空,所以第二个if判断也用不到,所以可见最后终于调用了这个代码块中最后一句handleMessage(msg)方法,这个方法则是我们例子程序中在new Handler时重写的方法,所以执行了此方法后例子程序中的mTextView才得以显示"get msg arg1 value is 1", 到此整个流程得以走通。

第一次写博客,以上整个流程分析难免有些啰嗦或者阐述不清的地方,关于handler这块,建议还是要认真多看两遍源码,比看帖子容易理解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值