Android开发笔记1:Handler、Looper、ThreadLocal的那些事

Handler和Looper的关系

使用过Handler的同学都应该知道,Handler最重要的两个方法就是sendMessage等发消息的方法和handleMessage处理消息的方法,下面就看看线程间是怎么通过Handler进行通信的。

1、Handler的默认构造方法

从中可以看到初始化了成员变量mLooper = Looper.myLooper(),mQueue = mLooper.mQueue

其实就是Looper mLooper 和 MessageQueue mQueue,mQueue就是存放Message的消息队列,同时消息队列是保存在Looper中的,至于为什么要把消息队列保存在Looper中,我们之后会进行说明。

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

2、发送消息

sendMessage等发送消息的方法最终都会执行下面的方法,我们看到其实就是把要发送的消息Message msg和对应的发送时间uptimeMillis放到消息队列mQueue中

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

3、处理消息

那么什么时候才会处理这些发送的消息呢?没错接下来就是Looper登场了。

1)首先代码中初始化Looper me = myLooper();

这已经是第二次调用mLooper()来初始化了,所以这里多说一句,在使用myLooper()初始化前一定要调用Looper.prepare()来把Looper添加到sThreadLocal

  public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
看到没,实际就是从sThreadLocal中取出来的,所以用之前一定得set啊

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

2)在loop方法中我们看到有一行msg.target.dispatchMessage(msg),没错就是这里执行的。

 public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle();
            }
        }
    }       

接着往下看,下面代码就会执行handleMessage来处理消息。并且如果你调用了Handler中的post(Runnable r)等方法,那么msg.callback != null 那么就会执行handleCallback(msg)

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

结论:处理消息必须要执行Looer.loop(),而执行Looper.loop()就需要先Looper.prepare()那么为什么大多数时候我们并没有显示调用Looper.prepare() Looper.loop()呢?


主线程(ActivityThread)

1、首先看下main函数里做了什么

1)从下面的代码中可以看到,在主线程的main()方法中,调用了Looper.prepareMainLooper()和Looper.loop()

是不是很像之前讲的的Looper.prepare(),Looper.loop()?

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

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

        Looper.prepareMainLooper();

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

        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

        thread.detach();
        String name = (thread.mInitialApplication != null)
            ? thread.mInitialApplication.getPackageName()
            : "<unknown>";
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }

2)看看prepareMainLooper()方法不难发现,原来这个方法中就调用了Looper.prepare()方法。

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

结论:主线程里我们并不需要显示调用,但是子线程中一定记得要显示调用Looper.prepare()和Looper.loop()

ThreadLocal

我们还记得在Looper.prepare()中调用过sThreadLocal.set(new Looper()),下面重贴下代码。

注意:每个线程只有一个Looper对象,因为每个Thread只有一个ThreadLocalMap对象并且都以key为ThreadLocal.this存储。那么在一个线程中实现多个Handler就没有意义了,因为他们都使用同一个Looper对象来保存消息。

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

而set方法实际上就是通过当前线程获得ThreadLocalMap,并把Looper放到当前线程的ThreadLocalMap中,并以ThreadLocal.this为key。

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值