Handler 源码理解

这篇文章用于分析自己对Handler源码的理解,会不定期把和Handler相关的内容整理到这。

一 使用场景

  主线程中不能执行耗时操作,否则会报not  respond的超时异常。所以耗时操作要放在子线程中去执行。但是子线程不能进行更新UI的操作。所以如果要更新UI,就要切换到主线程去执行。Handler就是用于处理线程的切换。

二 Handler的常用方式

Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

一般常用的Handler的使用就是这样,在主线程中new一个Hander,并重写onHandle()方法。在子线程中调用hander.sendMessage();此时就会切换到主线程成中执行handleMessage()方法。
要明白Handler的原理首先要明白ThreadLocal;

三 ThreadLocal

  1. 什么是ThreadLocal呢?首先看官方的描述:
/**
 * Implements a thread-local storage, that is, a variable for which each thread
 * has its own value. All threads share the same {@code ThreadLocal} object,
 * but each sees a different value when accessing it, and changes made by one
 * thread do not affect the other threads. The implementation supports
 * {@code null} values.
 *
 * @see java.lang.Thread
 * @author Bob Lee
 */

public class ThreadLocal {
首先用自己的话描述一下上述解释:
声明一个ThreadLocal对象,就是在不同的线程中
有各自的值,所有的线程可以共享同一个ThreadLocal对象。
每个线程在使用ThreadLocal时,都会有各自的值,可以在不同的线程中单独设置。
在线程中操作不会对其他线程有影响。
举个例子:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mThreadLocal = new ThreadLocal<>();

        mThread1.start();
        mThread2.start();
        mThread3.start();

        Thread currentThread = Thread.currentThread();

        try {
            currentThread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.i("TAG","value1"+value1);
        Log.i("TAG","value2"+value2);
        Log.i("TAG","value3"+value3);

    }

    Thread mThread1 = new Thread(){
        @Override
        public void run() {
            mThreadLocal.set("德玛西亚之力!");
            value1 = mThreadLocal.get();
        }
    };
    Thread mThread2 = new Thread(){
        @Override
        public void run() {
            mThreadLocal.set("德玛西亚皇子!");
            value2 = mThreadLocal.get();
        }
    };
    Thread mThread3 = new Thread(){
        @Override
        public void run() {
            mThreadLocal.set("德玛西亚之翼!");
            value3 = mThreadLocal.get();
        }
    };

打印结果:

01-06 08:22:26.209 1629-1629/example.com.demo_practice I/TAG: value1德玛西亚之力!
01-06 08:22:26.209 1629-1629/example.com.demo_practice I/TAG: value2德玛西亚皇子!
01-06 08:22:26.209 1629-1629/example.com.demo_practice I/TAG: value3德玛西亚之翼!
在不同的线程中操作同一个ThreadLocal对象,在ThreadLocal对象中获取的值是不同的。所以ThreadLocal在不同的线程中相互没有影响。

四 Handler相关源码分析

Hander的通常的使用就是在主线程更新UI,这里先不举例说明具体的使用例子,下面是简单的使用

Thread mHandlerThread = new Thread() {
        Handler handler;
        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler() {

            };
            Looper.loop();
        }
    };

那么为什么每次new Handler()时候都要先执行Looper.prepare()呢?先看一下Handler的构造方法:

 public Handler(Callback callback, boolean async) {
        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 = callback;
        mAsynchronous = async;
    }
 if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

这里首先判断mLooper是否为空,如果是空直接抛出一个异常Can’t create handler inside thread that has not called Looper.prepare(),意思就是在调用Looper.prepare()方法之前不能创建一个Hander;

再来看Looper.prepare()方法:

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

在此方法中,首先判断

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

也就是说,一个线程中只能调用prepare()方法一次。

从下面的这行代码可以看出ThreadLocal中保存的是Looper对象。

sThreadLocal.set(new Looper(quitAllowed));

先看一下sThreadLocal对象

可以看出sThreadLocal是静态的常量,即所有的线程中操作的ThreadLocal对象都是同一个ThreadLocal对象。

接下来接着分析Looper的构造方法

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

可以看出这里面声明了一个MessageQueue对象和一个Thread对象。也就是说一个Looper对应一个MessageQueue和一个Thread;
Looper.prepare()方法就分析到这,接下来看Looper.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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

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

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

从这段代码中可以看出,当msg==null时死循环就会阻塞,当msg!=nul时,就会执行

 msg.target.dispatchMessage(msg);

这段代码首先是标记msg,然后调用dispatchmessage()方法;
然后看下dispatchmessage方法:

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

这里的CallBack我不理解。回头明白了在补充。这个方法最后调用的handleMessage()方法,然后再来看这个方法:

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

可以看出这个 方法是个空方法,也就是要自己重写的方法。
写到这里,Handler的轮询消息的主线就已经看出来了,下次有时间在把Hanlder取消息再消息分析一下。

5 总结

1 用途: Handler是用于线程间切换处理的框架
2 核心类: Handler持有Looper对象和MessageQueue对象, Looper用于循环遍历MessageQueue中的消息
3 Looper是线程唯一的: Looper对象保存在ThreadLocal中,不同线程中的只能取出唯一的Looper对象
4 任务处理: 当通过Handler#sendMessage()后,Looper在无限循环中从MessageQueue中取出Message,转交到Handler#handleMessage()中处理

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值