android那些事--- Handler和Looper的纯洁关系

       很久很久以前我们就知道,android主线程和子线程通过Handler来通讯,比如在主线程里定义Handler,实现handleMessage 接口,子线程里调用sendMessage方法发送消息,然后主线程就知道了,调用handleMessage来处理这个消息。

 

        一来一回这就完事了,看起来是很纯洁很简单的男女关系。但事实上真的这样吗?sendMessage把消息发送到哪里去了?也许你要说,每个成功的男人背后都有一个伟大的女人,一个Handler的背后一定有一个任劳任怨的循环looper!恭喜你,答对了,奖品没有。这个我们从Handler类的构造函数就能看出来,虽然有四个构造函数,但每个构造函数里面都有Looper的身影,而且后面还跟着一个mQueue!到这里我们似乎云开见日了,Handler 内部都有一个Looper一直在运转,Looper里不停的操作一个MessageQueue,Handler的sendMessage方法把消息插入到了这个mQueue,而Looper发现有了新的mQueue,就调用handleMessage接口来处理。

      说到这,我们发现了,looper才是关键,Handler只是looper的消息插入接口罢了,是浮云啊。其实,Looper是android为线程配备的一个工具,用来做线程间通讯,一个线程里只能有一个Looper。而且在activity启动时,android还为进程创建了一个MainLooper,插一句,应用程序的第一个线程,是主线程,其实就是进程,也称为根线程。

在ActivityThread.java里有个main方法,贴下代码,

public static void main(String[] args) {
        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);

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

        Looper.prepareMainLooper();//创建mainlooper
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();//创建mainhandler
        }

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

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

        Looper.loop();//开启mainlooper的循环

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

        大家一看,就真相了,android系统在启动应用时,给主线程配备了一个mainLooper,并且启动mainLooper的循环。

至于应用里面后来创建的子线程,就没这待遇了,谁让你不是爹呢?所以在主线程里面使用Handler的时候,你不需要新建Looper,而在子线程里使用Handler的时候,

你就必须构造一个Looper。

        Handler的构造函数分两种,一种是不含  Looper 参数的,一种是含有Looper参数的。不含参数的构造函数,使用Looper.myLooper()方法获取本线程的Looper。      

mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). 而Looper.getMainLooper() 用于获取主线程的Looper对象。 

        在非主线程中直接new Handler() 会报如下的错误:
E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        原因是非主线程被创建时,android系统不会为他创建Looper对象,所以需要手动先调用Looper.prepare()来创建一个Looper。看下这个方法,

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

sThreadLocal是线程本地存储空间的意思,每个线程有自己的独立空间。前面说了,Looper是线程唯一的,这不是瞎说吹牛逼的,貌似就是因为这里。

我们看到,prepare方法会先从线程本地存储空间里取一下,看有没有创建过Looper。然后就是创建Looper,保存在线程本地存储空间。

而myLooper方法就是取出当前线程的Looper。

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

      关于mainLooper的创建、存储和获取,Looper类单独给了一套接口:

public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        myLooper().mQueue.mQuitAllowed = false;
    }

private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }

    /** Returns the application's main looper, which lives in the main thread of the application.
     */
    public synchronized static Looper getMainLooper() {
        return mMainLooper;
    }

       当当当当! 好戏总是压轴的,最后上台的是Looper的核心,loop循环处理方法!

public static void loop() {
        Looper me = myLooper();//Looper到位
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;//queue到位
       
        // 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(); // 从队列取出一个消息,没消息的时候会阻塞
            if (msg != null) {
                if (msg.target == null) {//每个女人都有个对象,每个消息都有个target,没对象的女人要不得,没target的消息不能要
                    // 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);//分发消息,这个方法是目标Handler的方法,这里面调用了handleMessage!

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

类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接口
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值