美文网首页Android面试相关
Android小白带你走进Handler

Android小白带你走进Handler

作者: 呱呱_ | 来源:发表于2018-03-30 18:05 被阅读0次

写在前面:

每每看到大佬,默默留下没技术的眼泪。

好在最近比较清闲,打算写几篇基础类的文章;弥补一些知识点的不足,也希望可以帮助到其他小伙伴。

文章将以小白的视角,对自己所理解的内容进行解读,统称为小白系列吧。
至于内容,多半是东拼西凑;至于错误及不足,希望大家予以指出;参考博文在文末予以奉上(比心)。

一,我是谁:Handler是什么?为什么要有Handler?

Handler是什么?
  • 简单说,Handler就是解决线程和线程之间的通信的一种机制。
为什么需要Handler呢?
  • 因为Android是单线程模型,只允许在UI线程更新UI操作。
  • 而一些耗时操作(如访问网络、数据库等操作),是需要交由子线程去执行的。
  • 但子线程并不能进行UI的更新,需要使用Handler来解决线程与线程之间的通讯。

至于为什么Android使用单线程模型:
因为如果任意线程都可以更新UI的话,在多线程并发访问情况下,线程安全问题处理起来会相当麻烦复杂。虽然可以通过加锁进行解决,但降低 UI 访问的效率。

二,到哪里去:Handler的基本使用

  • Handler的基本使用可以分为两大类:在主线程的使用,和在子线程的使用。

这里先介绍Handler的正确使用,至于为什么这么使用,以及细节需要注意,我们将在后面的源码解析部分进行探讨。

在主线程使用Handler,示例如下:
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "testHandler ---->";

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //获得刚才发送的Message对象,然后在这里进行UI操作
            Log.e(TAG, "msg.what = " + msg.what);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testHandler();
    }

    private void testHandler() {
        //开启一个线程模拟处理耗时的操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(2000);
                //通过Handler发送一个消息切换回主线程(mHandler所在的线程)
                mHandler.sendEmptyMessage(0);
            }
        }).start();
    }
}

执行结果:

E/testHandler ---->: msg.what = 0

在主线程使用handler很简单,只需在主线程创建一个handler对象,在子线程通过在主线程创建的handler对象发送Message,在handleMessage()方法中接受这个Message对象进行处理。通过handler很容易的从子线程切换回主线程了。

在子线程的使用Handler,示例如下:
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "testHandler ---->";

    @SuppressLint("HandlerLeak")
    //主线程中的handler
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //获得刚才发送的Message对象,然后在这里进行UI操作
            Log.e(TAG, "msg.what = " + msg.what);
        }
    };

    //子线程中的handler
    private Handler mHandlerThread = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testHandler();
    }

    private void testHandler() {
        //开启一个线程模拟处理耗时的操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(2000);
                //通过Handler发送一个消息切换回主线程(mHandler所在的线程)
                mHandler.sendEmptyMessage(0);

                //调用Looper.prepare()方法
                Looper.prepare();
                mHandlerThread = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.e("sub thread ---->", "msg.what = " + msg.what);
                    }
                };
                mHandlerThread.sendEmptyMessage(1);
                //调用Looper.loop()方法
                Looper.loop();
            }
        }).start();
    }
}

执行结果:

E/testHandler ---->: msg.what = 0
E/sub thread ---->: msg.what = 1

我们会发现,在子线程使用Handler需要调用Looper.prepare(),以及Looper.loop()方法。为何主线程这么流弊,可以省略步骤?答案后面揭晓。。

三,亲友团:Handler和ta的小伙伴们

首先我们先来了解一下Handler有哪些小伙伴,分别有哪些作用。

Handler的消息处理主要有五个部分组成:Message,Handler,Message Queue,Looper和ThreadLocal。

我们可以先看看图,对ta们做出一些简单的了解:


主线程的Handler关系图.png
  • Handler:主要是用于发送和处理消息的发送消息。一般使用sendMessage()方法,还有其他的一系列sendXxx的方法,但最终都是调用了sendMessageAtTime()方法,除了sendMessageAtFrontOfQueue()这个方法。
    而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage()方法中。

  • Message:是在线程之间传递的消息,它可以在内部携带少量的数据,用于线程之间交换数据。Message有四个常用的字段,what字段,arg1字段,arg2字段,obj字段。what,arg1,arg2可以携带整型数据,obj可以携带object对象。

  • Message Queue:是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分的消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

  • Looper:每个线程通过Handler发送的消息都保存在,MessageQueue中,Looper通过调用loop()的方法,就会进入到一个无限循环当中,然后每当发现Message Queue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。

  • ThreadLocal:MessageQueue对象,和Looper对象在每个线程中都只会有一个对象,怎么能保证它只有一个对象,就通过ThreadLocal来保存。Thread Local是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。

了解了这些基本概念后,我们深入源码来了解Handler的工作机制。

四,从哪里来:Handler的源码解析

Looper篇

每个应用程序都有一个入口,而Android程序是基于Java的,Java的程序入口是静态的main函数,因此Android程序的入口也应该为静态的main函数。
在Android程序中这个静态的main在ActivityThread类中。Java层的启动就是从这里开始的,我们来一起看一下:

public static void main(String[] args) {

    。。。迷人的省略号。。。。

    //有点儿类似prepare的方法
    Looper.prepareMainLooper();

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

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

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

    //这里也出现了loop的方法了
    Looper.loop();

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

既然我们觉得有点像,那我们就来来看一下Looper.prepareMainLooper(),与Looper.prepare()方法:

public static void prepareMainLooper() {
    //这里是false。说明该线程中的messageQueue是不可以被销毁的。
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        //prepare里面执行set方法。这里执行get方法获取Looper
        sMainLooper = myLooper();
    }
 }

public static void prepare() {
    //这里是ture。说明该线程中的messageQueue可以被销毁的。
    prepare(true);
}

通过对比,我们发现他们调用了prepare的重载方法。所以我们明白了,子线程使用Handler需要调用Looper.prepare(),以及Looper.loop()方法,而主线程中使用不需要,因为主线程在执行的时候已经调用。

然后我们看一下prepare的重载方法,Looper.prepare(boolean quitAllowed):

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

在这里可以看出,sThreadLocal对象保存了一个Looper对象,首先判断是否已经存在Looper对象了,以防止被调用两次。sThreadLocal对象是ThreadLocal类型,因此保证了每个线程中只有一个Looper对象。Looper对象是什么创建的,我们进入看看,如下:

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

可以看出,这里在Looper构造函数中创建出了一个MessageQueue对象和保存了当前线程。从上面可以看出一个线程中只有一个Looper对象,而Message Queue对象是在Looper构造函数创建出来的,因此每一个线程也只会有一个MessageQueue对象。

然后我们再来看一下Looper.loop()都干了些什么:

public static void loop() {
    //获取Looper
    final Looper me = myLooper();
    if (me == null) {
        //如果me为null则抛出异常,也就是说loop方法必须在prepare方法之后运行。
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取该Looper下的queue(消息队列)
    final MessageQueue queue = me.mQueue;

    。。。迷人的省略号。。。。

    //死循环。据说之前是while(true),反正就是不让你跳出去
    for (;;) {
        //取出一条消息,如果没有消息则阻塞。
        Message msg = queue.next(); // might block  
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        。。。迷人的省略号。。。。

        //这个方法很重要,其实就是把消息交给msg的handler对象去处理
        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();
    }
}

所以说,Looper的主要作用:
1,与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2,loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

于是,我们有了消息队列MessageQueue,也有了在死循环中取消息的Looper,现在还缺少的就是谁去发送消息,于是乎今天的主角登场啦~

Handler篇

首先,我们先来了解一下Handler的构造方法:

public Handler() {  
    this(null, false);  
} 

public Handler(Callback callback, boolean async) {

     。。。又是迷人的省略号。。。。

    //这里获取一个mLooper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
    //如果mLooper为null抛出异常
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //获取mLooper当中的消息队列
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

我们可以看出,在使用new Handler()之前必须要有一个mLooper这个对象。不然的话会抛出异常。
如果有Looper实例的话,将获取了当前线程保存的Looper实例,并且获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

Handler的工作主要包含发送和接收过程。消息的发送主要通过post和send的一系列方法,而post的一系列方法是最终是通过send的一系列方法来实现的,我们来一起看一下常用的send方法:

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}  
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
          this + " sendMessageAtTime() called with no mQueue");
          Log.w("Looper", e.getMessage(), e);
          return false;
       }
       return enqueueMessage(queue, msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
         delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, 0);    
}

通过观察,除了sendMessageAtFrontOfQueue(),其余的方法最终调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法,我们再来看看此方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //将当前的Handler作为msg的target属性
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //将消息插入到消息队列当中
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看出,handler将自身作为mag的target属性,并且将消息插入到消息队列当中。
在前面说到的Looper.loop()方法中,有个重要的msg.target.dispatchMessage(msg)方法,由于target就是当前的handler,所以处理消息还是交给msg的handler对象去处理。

现在已经清楚Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法,下面我们赶快去看一看这个方法:

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

而handleMessage(msg)方法是个空方法:

public void handleMessage(Message msg) {  
} 

因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。如:

private Handler mHandler = new Handler()  {  
    public void handleMessage(android.os.Message msg)  {  
        switch (msg.what)  {  
        case value:  
                  
            break;  
        default:  
            break;  
        }  
    };  
}; 

至此,我们再来把流程总结一下(敲黑板)
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

最后奉上的各位大佬的博文(再度比心):
鸿洋大佬的: Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
另一位大佬的:Handler运行机制
还一位大佬的:彻底理解handler的实现原理

相关文章

网友评论

    本文标题:Android小白带你走进Handler

    本文链接:https://www.haomeiwen.com/subject/ksxbcftx.html