Android中Handler的相关面试问题

1.Message是Handler接收和处理消息的对象。

2.每一个线程只能有一个Looper。

3.Looper中有一个方法可以读取MessageQueue消息队列中的消息。

4.Looper会将消息取出交给Handler来进行消息的处理。

5.消息队列MessageQueue采用先进先出的方式来管理我们的Message。

6.MessageQueue对象的创建在Looper的构造方法中。

7.Handler的作用有两个,一个是发送消息,一个是处理消息。

8.由于Handler发送消息必须发送到指定的MessageQueue消息队列当中,而消息队列MessageQueue又由Looper进行创建和管理。所以如果你想让Handler正常工作,就要在当前线程有一个Looper对象。

9.Handler整体工作机制

首先要在主线程创建的时候为主线程创建一个Looper。而在创建Looper对象的同时,又会在Looper内部创建一个消息队列MessageQueue这个对象。而创建Handler的时候会取出当前线程的Looper。然后通过这个Looper不断地去轮询消息队列中的Message。然后Handler在子线程中发送消息其实就是在消息队列MessageQueue中添加一条Message。最后通过Looper当中的消息循环取得消息队列中的Message,然后交给我们的Handler去进行处理。

10.Handler的构造方法中传入一个Callback和一个布尔类型的值。

11.Handler构造方法内部会创建一个Looper对象

mLooper = Looper.myLooper();

12.Handler构造方法解读

Handler构造方法源码如下

    public Handler(@Nullable 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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

<1>Handler构造方法中传入一个Callback和一个布尔类型的值。

<2>Handler构造方法内部会创建一个Looper对象。

mLooper = Looper.myLooper();

<3>Handler构造方法中,获取到Looper对象后,会对Looper对象进行判断

if (mLooper == null) 

如果为空则会抛出异常并说明Looper对象为空是因为没有调用Looper.prepare()方法。 

<4>Handler构造方法中,将Looper的消息队列取出赋值给Handler中一个消息队列的对象

 mQueue = mLooper.mQueue;

<5>Handler构造方法中,Handler、Looper、MessageQueue三者捆绑逻辑如下:

Handler构造方法中,通过Looper.myLooper()获取与Handler相关的Looper对象,然后通过该Looper对象获取到Looper中的MessageQueue。

13.Handler中的sendEmptyMessage,sendEmptyMessageDelayed这些方法最终都会走到enqueueMessage方法内部,而该方法实质就是将Handler发送的消息添加到消息队列当中。

14.在enqueueMessage方法内部有如下代码

msg.target = this;

这个this就是我们的Handler对象。这个操作就是将handler对象绑定到Message内部的一个target变量当中了。

15.消息对列包括两种操作:插入和读取。而读取操作本身也会伴随删除操作。

16.在MessageQueue中,有如下两个方法:

  • enqueueMessage:向消息队列中插入一条消息。
  • next:从队列中读取并删除消息。

17.MessageQueue内部通过单链表数据结构来维护消息列表的。因为单链表在插入和删除上比队列有优势。

18.Looper的创建在Looper的perpare()方法中进行。

19.Looper类中通过sThreadLocal容器存放Looper对象本身。

20.使用sThreadLocal容器可以保证每一个线程获取到的Looper都是唯一的。

21.sThreadLocal如何保证Looper的唯一性?

sThreadLocal在很多地方被叫做线程本地变量,它能为每个变量在每个线程都创建一个副本。每个线程都可以访问自己内部副本的变量,这样就不用每一个线程去取别的线程的变量,而导致不同线程的变量是不一致的这种现象。所以它最大的好处就是:每一个线程它的变量是独立的。你取该线程就是相应的变量,而不会改变其它线程变量的值。

22.Looper.prepare()源码分析

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

我们可以看到,在Looper的prepare方法中,我们先判断了sThreadLocal中是否可以获取到Looper,如果可以则抛出异常。所以当我们Looper重复调用prepare()方法会抛异常。如果为空就要new一个Looper对象放入sThreadLocal中。 

23.Looper的构造方法源码分析

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

首先Looper的构造方法是私有的,所以不能直接进行创建。

Looper在创建时,MessageQueue也同时被创建了。

24.Looper.myLooper()源码分析

在第12项中我们了解到,Handler构造方法中,通过mLooper = Looper.myLooper();语句获取到对应的Looper对象。这里我们就看一下Looper.myLooper()源码,源码如下

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

可以看到,创建Looper就是通过sThreadLocal这个容器获取。sThreadLocal这个容器是线程所持有的,所以说它能保证我们这个Looper是和每一个其它的线程所独立开来的。通过调用sThreadLocal的get()方法就可以获取到相应的Looper对象。

25.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;
        
                                        ......
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

                                        ......
            
        try {
            msg.target.dispatchMessage(msg);

                                        ......

        } catch (Exception exception) {
                
                                        ......

        } finally {

                                        ......

        }

                                        ......

    }
}

<1>首先它会使用myLooper()方法获取一个Looper对象。

final Looper me = myLooper();

<2>接着会判断获取到的Looper对象是否为空,为空说明没有调用Looper.prepare()方法,抛出一个异常。

if (me == null) {
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}

<3>接着获取到Looper中的消息队列。

final MessageQueue queue = me.mQueue;

<4>接下来,我们开启一个死循环

for (;;) {
            ......
}

在死循环中,取出消息队列中的下一条消息。

Message msg = queue.next(); // might block

如果msg为空,直接return

if (msg == null) {
    // No message indicates that the message queue is quitting.
    return;
}

如果不为空,则会调用

 msg.target.dispatchMessage(msg);

target就是我们的Handler对象。即如果消息不为空,则将消息交给Handler处理。

26.Handler的dispatchMessage方法分析

源码如下

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

<1>首先它会判断msg.callback是否为空,如果不为空则调用handleCallback(msg);方法处理消息

if (msg.callback != null) {
    handleCallback(msg);
}

<2>这里的callback就是我们熟悉的Runnable

<3>handleCallback源码如下

private static void handleCallback(Message message) {
    message.callback.run();
}

可以看到就是调用内部传进来的Runnable的run方法。表示在子线程做一些操作。 

<4>当msg.callback为空的时候,会调用mCallback的handleMessage方法,这个就是我们在主线程中创建Handler时重写的那个handleMessage方法。

27.Handler整体流程回顾

<1>创建一个Looper对象

Looper的作用是不断从MessageQueue中获取Message

<2>Message哪里来的?

Hander发送的

<3>Handler有两个作用,一个是将消息发送,插入到MessageQueue中,另一个是Looper不断轮询取出消息后,将消息交给Handler,由Handler进行处理。

28.Handler总结

<1>Looper类主要是为每一个线程开启的单独的消息循环,主线程可以不用我们操作Looper,因为主线程已经自动为我们启动Looper了。但子线程要在Handler创建前调用Looper.prepare(),在Handler创建后调用Looper.loop()。

<2>Handler可以看做是一个接口,用来向指定的Looper中的MessageQueue发送消息。

<3>非主线程中直接new Handler()是不可以的,必须在该线程中创建好Looper。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值