首页 > 移动平台 > 详细

Android消息处理机制(源码分析)

时间:2014-08-03 18:08:07      阅读:491      评论:0      收藏:0      [点我收藏+]

前言

虽然一直在做应用层开发,但是我们组是核心系统BSP,了解底层了解Android的运行机制还是很有必要的。就应用程序而言,Android系统中的Java应用程序和其他系统上相同,都是靠消息驱动来工作的,它们大致的工作原理如下:
1. 有一个消息队列,可以往这个消息队列中投递消息。
2. 有一个消息循环,不断从消息队列中取出消息,然后处理 。
为了更深入的理解Android的消息处理机制,这几天空闲时间,我结合《深入理解Android系统》看了Handler、Looper、Message这几个类的源码,这里分享一下学习心得。

Looper类分析

Looper字面意思是“循环”,它被设计用来将一个普通的Thread线程变成Looper Thread线程。所谓Looper线程就是循环工作的线程,在程序开发(尤其是GUI开发)中,我们经常会使用到一个循环执行的线程,有新任务就立刻执行,没有新任务就循环等待。使用Looper创建Looper Thread很简单,示例代码如下:
package com.example.testlibrary;

import android.os.Handler;
import android.os.Looper;

public class LooperTheread extends Thread{
	public Handler mhHandler;

	@Override
	public void run() {
		// 1. 调用Looper
		Looper.prepare();
		
		// ... 其他处理,例如实例化handler
		
		// 2. 进入消息循环
		Looper.loop();
	}
	
}

通过1、2两步核心代码,你的线程就升级为Looper线程了。下面,我们对两个关键调用1、2进行逐一分析。

Looper.prepare()

在调用prepare的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中。而Looper对象内部封装了一个消息队列。
bubuko.com,布布扣
我们来看一下Looper类的源码。第一个调用函数是Looper的prepare函数,它的源码如下:
    // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    public static void prepare() {
        prepare(true);
    }

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

根据上面的源码可知,prepare会在调用线程的局部变量中设置一个Looper对象,并且一个Thread只能有一个Looper对象。这个调用线程就是LooperThread的run线程。来看一下Looper对象的构造源码:
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
通过源码,我们可以轻松了解Looper的工作方式,其核心就是将Looper对象定义为ThreadLocal。如果不理解ThreadLocal,可以参考我这篇文章:正确理解ThreadLocal

Looper循环

调用了Loop方法后,Looper线程就开始真正的工作了,它不断从自己的MessageQueue中取出对头的信息(也叫任务)执行,如图所示:
bubuko.com,布布扣
其实现源码如下所示(这里我做了一些修整,去掉不影响主线的代码):
    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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.");
        }
        // 取出这个Looper的消息队列
        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;
            }

            // 处理消息,Message对象中有一个target,它是Handler类型
            msg.target.dispatchMessage(msg);
            msg.recycle();
        }
    }

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
通过上面的分析会发现,Looper的作用是:
1. 封装了一个消息队列。
2. Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终处理的线程)绑定在一起,通过ThreadLocal机制实现的。
3. 处理线程调用loop函数,处理来自该消息队列的消息。
如何往MessageQueue里添加消息,是由Handler实现的,下面来分析一下Handler。

Handler分析

什么是handler?handler扮演了往MessageQueue里添加消息和处理消息的角色(只处理由自己发出的消息),即通过MessageQueue它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。

初识Handler

Handler中的所包括的成员:
    final MessageQueue mQueue;	// Handler中也有一个消息队列
    final Looper mLooper;	// 也有一个Looper
    final Callback mCallback;	// 有一个回调类
这几个成员变量的使用,需要分析Handler的构造函数。Handler一共有四个构造函数,它们的区别就在于对上面成员变量的初始化上,下面我们来看一下四个构造函数的源码:
    // 构造函数1
    public Handler() {
        // 获得调用线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 得到Looper的消息队列
        mQueue = mLooper.mQueue;
        // 无callback设置
        mCallback = null;
    }

    // 构造函数2
    public Handler(Callback callback) {
        // 获得调用线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 得到Looper的消息队列
        mQueue = mLooper.mQueue;
        // 和构造函数1相比,多了callback设置
        mCallback = callback;
    }

    // 构造函数3
    public Handler(Looper looper) {
    	// looper由外部传入,是哪个线程的Looper不确定
    	mLooper = looper;
    	mQueue = mLooper.mQueue;
    	mCallback = null;
    }

    // 构造函数4
    public Handler(Looper looper, Callback callback) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
    }
通过上面的构造函数,我们可以发现,Handler中的MessageQueue最终都会指向关联Looper的MessageQueue。

Handler真面目

由上面分析可知,Handler中的消息队列实际上就是某个Looper的消息队列,我们可以为之前的LooperThread类增加Handler,代码如下:
public class LooperThread extends Thread{
	public Handler mhHandler;

	@Override
	public void run() {
		// 1. 调用Looper
		Looper.prepare();
		
		// ... 其他处理,例如实例化handler
		Handler handler = new Handler();
		
		// 2. 进入消息循环
		Looper.loop();
	}
	
}
加入Handler的效果图如下所示:
bubuko.com,布布扣
问一个问题,假设没有Handler,我们该如何往Looper的MessageQueue里插入消息呢?这里我说一个原始的思路:
1. 调用Looper的myQueue,它将返回消息队列对象MessageQueue。
2. 构造一个Message,填充它的成员,尤其是target对象。
3. 调用MessageQueue的enqueueMessage,将消息插入到消息队列中。
上面的方法虽然能工作,但是非常原始,有了Handler以后,它像一个辅助类,提供了一系列API调用,帮我们简化编程工作。常用API如下:
1. post(Runnable)
2. postAtTime(Runnable, long)
3. postDelayed(Runnable, long)
4. sendEmptyMessage(int)
5. sendMessage(Message)
6. sendMessageAtTime(Message, long)
7. sendMessageDelayed(Message, long)
光看以上的API,你会认为handler可能会发送两种信息,一种是Runnable对象,一种是Message对象,这是主观的理解,但是从源码中我们可以看到,post发出的Runnable对象最后都被封装成了Message对象,源码如下:
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();	// 得到空的message
        m.callback = r;	// 将runnable设置为message的callback
        return m;
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
最终发送消息都会调用sendMessageAtTime函数,我们看一下它的源码实现:
    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;	// 将Message的target设置为自己,然后加到消息队列中
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler处理消息

讲完了消息发送,再看一下Handler是如何处理消息的。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,源码如下:
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
dispatchMessage定义了一套消息处理的优先级机制,它们分别是:
1. 如果Message自带了callback处理,则交给callback处理。
2. 如果Handler设置了全局的mCallback,则交给mCallback处理。
3. 如果上述都没有,该消息会被交给Handler子类实现的handlerMessage(Message msg)来处理。当然,这需要从Handler派生并重写HandlerMessage函数。
在通过情况下,我们一般都是采用第三种方法,即在子类中通过重载handlerMessage来完成处理工作。

Handler的用处

看完了Handler的发送消息和处理消息,我们来学习一下Handler被称为异步处理大师的真正牛逼之处。Hanlder有两个重要的特点:
1. handler可以在任意线程上发送消息,这些消息会被添加到Handler中Looper的消息队列里。
bubuko.com,布布扣
2. handler是在它关联的looper所在线程中处理消息的。
bubuko.com,布布扣
这解决了Android经典的不能在非主线程中更新UI的问题。Android的主线程也是一个Looper线程,我们在其中创建的Handler将默认关联主线程Looper的消息队列。因此,我们可以在主线程创建Handler对象,在耗时的子线程获取UI信息后,通过主线程的Handler对象引用来发生消息给主线程,通知修改UI,当然消息了还可以包含具体的UI数据。

Message

在整个消息处理机制中,Message又叫做task,封装了任务携带的消息和处理该任务的handler。Message的源码比较简单,这里简单说明几点注意事项:
1. 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源,源码如下:
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }
2. 如果你的Message只需要携带简单的int信息,应该优先使用Message.arg1和Message.arg2来传递信息,这比使用Bundler节省内存。
    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1; 

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;
    
    /**
     * Sets a Bundle of arbitrary data values. Use arg1 and arg1 members 
     * as a lower cost way to send a few simple integer values, if you can.
     * @see #getData() 
     * @see #peekData()
     */
    public void setData(Bundle data) {
        this.data = data;
    }
3. 用Message.what来标识信息,以便用不同方式处理message。

示例代码

写了一个子线程利用主线程Handler更新UI的示例代码,如下:
public class MainActivity extends Activity {
	TextView mTextView;
	MyHandler mHandler = new MyHandler();
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mTextView = (TextView)findViewById(R.id.test1);
		new Thread(new UpdateTitleTask(mHandler)).start();
	}

	private class MyHandler extends Handler {

		@Override
		public void handleMessage(Message msg) {
			Bundle bundle = msg.getData();
			mTextView.setText(bundle.getString("title", ""));
		}
		
	}
}
public class UpdateTitleTask implements Runnable{
	private Handler handler;
	
	public UpdateTitleTask(Handler handler) {
		this.handler = handler;
	}
	
	private Message prepareMsg() {
		Message msg = Message.obtain();
		Bundle bundle = new Bundle();
		bundle.putString("title", "From Update Task");;
		msg.setData(bundle);
		return msg;
	}
	
	@Override
	public void run() {
		try {
			Thread.sleep(2000);
			Message msg = prepareMsg();
			handler.sendMessage(msg);
		} catch (InterruptedException e) {
			
		}
	}

}

参考文献

1. 《深入理解Android 卷一》
2.  http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html



Android消息处理机制(源码分析),布布扣,bubuko.com

Android消息处理机制(源码分析)

原文:http://blog.csdn.net/wzy_1988/article/details/38346637

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!