您的位置:首页 > 产品设计 > UI/UE

源码分析looper,messagequeue及handler的创建调用过程

2015-08-13 23:14 176 查看
首先我从ui线程的调用过程中开始分析。

(1) 在ui线程中系统会初始化一个looper对象,源码中在activityThread中的main方法中调用了Looper.prepareMainLooper()方法对looper进行初始化,并且调用loop方法开始执行无限循环的中messagequeue中获取msg并利用handler发送(这个源码下面会说)。

public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new AndroidKeyStoreProvider());

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper(); //这也是为什么ui线程中不需要执行prepare及loop方法的理由了

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        AsyncTask.init();

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }


(2)在调用prepareMainLooper的时候会调用prepare方法:

sMainLooper的声明为private static Looper sMainLooper; 即当前线程的looper对象,从static 和下面贴出的looper的私有构造函数可以知道looper是单例的。

sThreadLocal声明为static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();可以理解为用来保存looper的对象。

mylooper()是调用的sThreadLocal.get(),来获取保存的looper对象

public static void prepareMainLooper() {
        prepare(false);      //创建looper对象及MessageQueue
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper(); //获取looper对象
        }
    }
prepare方法中进行了对looper的判断,如果当looper存在的时候继续调用prepare方法时就会抛出Only one Looper may be created per thread的运行异常,这就是为什么一个线程只能拥有一个looper的原因了。

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {  //使其保证一个线程只拥有一个looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));  
    }


而在looper的构造函数之中,我们会创建一个MessageQueue对象,因为上面的代码保证了sThreadLocal即looper的唯一性,所以MessageQueue也是唯一的。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();  //保存当前线程,在使用时可以用来获取或者判断是否为当前本地线程
 }


那么我们现在得到了用来管理message的messagequeue,和用来取出message的looper对象。

(3)调用loop方法,利用死循环来不断获取messagequeue中的message



public static void loop() {
        final Looper me = myLooper();  //获取当前线程的looper对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //获取当前线程的消息队列

        
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // 获取消息队列的下一个消息,如果没有消息,将会阻塞
            if (msg == null) {
                //如果消息为null,直接退出循环,表示消息队列正在退出。
                return;
            }

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

            msg.target.dispatchMessage(msg);  //点过去就知道了msg.target为handler对象,下面会讲此方法。

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            //保证在分发消息的过程中线程标识符不会被修改
            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();  //释放资源
        }
    }


(4)上面的可以简单的理解为,ui线程会自己调用looper.prepare方法来初始化looper及messagequeue,然后调用loop方法,通过无限循环来利用looper对象取出保存在messagequeue中的message对象,然后利用handler处理消息。

那么现在看下msg.target.dispatchMessage(msg);是怎么运行的,首先知道msg.target即为一个handler对象,dispatchMessage方法点进去之后会调用handleMessage

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


handleMessage在源码中就是个空方法,每次我们新建handler对象的时候都会重写此方法,进行操作。这就是完整的消息机制。

而在handler.sendMessage方法中会执行一步一步的执行到enqueueMessage方法,在这个方法中会间msg.target赋值为handler,之后将其中的message保存到messagequeue之中。然后通过loop的无限循环可以使其执行dispatchMessage方法,取出队列中的对象,进行处理。

那么在不是ui线程中想要利用handler消息机制的话,那么就必须自己调用prepare和loop方法了,如下:

public void looper()
	{
		new Thread()
		{
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				//非主线程中默认没有创建looper对象
                                //Toast.makeText(mContext, "不可以打toast", 0).show();
				Looper.prepare();
				Toast.makeText(mContext, "可以打toast", 0).show();
				Looper.loop();
			}
		}.start();
	}


总结下: 1.调用prepare()方法创建looper对象及其对应的messagequeue

2.创建handler对象来通过其sendMessage方法发送message到messagequeue中。

3.通过loop方法中的无限循环以先进先出的原则来通过handler对象的handleMessage方法来处理消息
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: