用户登录
用户注册

分享至

Android Handler原理详解

  • 作者: 西南地区金融配资总代恒指德指IFIC
  • 来源: 51数据库
  • 2020-08-13

简介

在 Android 中,只有主线程才能操作 UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生 ANR 异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要更新 UI,一般是通过?Handler?发送消息,主线程接受消息并且进行相应的逻辑处理。除了直接使用?Handler,还可以通过 View 的?post?方法以及 Activity 的?runOnUiThread?方法来更新 UI,它们内部也是利用了?Handler。在上一篇文章?Android AsyncTask源码分析?中也讲到,其内部使用了?Handler?把任务的处理结果传回 UI 线程。

本文深入分析 Android 的消息处理机制,了解?Handler?的工作原理。

Handler

先通过一个例子看一下?Handler?的用法。

public class MainActivity extends AppCompatActivity {
    private static final int MESSAGE_TEXT_VIEW = 0;

    private TextView mTextView;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_TEXT_VIEW:
                    mTextView.setText("UI成功更新");
                default:
                    super.handleMessage(msg);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mTextView = (TextView) findViewById(R.id.text_view);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mHandler.obtainMessage(MESSAGE_TEXT_VIEW).sendToTarget();
            }
        }).start();

    }
}

上面的代码先是新建了一个?Handler的实例,并且重写了?handleMessage?方法,在这个方法里,便是根据接受到的消息的类型进行相应的 UI 更新。那么看一下?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;
}

在构造方法中,通过调用?Looper.myLooper()?获得了?Looper?对象。如果?mLooper?为空,那么会抛出异常:”Can’t create handler inside thread that has not called Looper.prepare()”,意思是:不能在未调用?Looper.prepare()?的线程创建?handler。上面的例子并没有调用这个方法,但是却没有抛出异常。其实是因为主线程在启动的时候已经帮我们调用过了,所以可以直接创建?Handler?。如果是在其它子线程,直接创建?Handler?是会导致应用崩溃的。

在得到?Handler?之后,又获取了它的内部变量?mQueue, 这是?MessageQueue?对象,也就是消息队列,用于保存?Handler?发送的消息。

到此,Android 消息机制的三个重要角色全部出现了,分别是?Handler?、Looper?以及?MessageQueue。 一般在代码我们接触比较多的是?Handler?,但?Looper?与?MessageQueue?却是?Handler?运行时不可或缺的。

Looper

上一节分析了?Handler?的构造,其中调用了?Looper.myLooper()?方法,下面是它的源码:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

这个方法的代码很简单,就是从?sThreadLocal?中获取?Looper?对象。sThreadLocal?是?ThreadLocal?对象,这说明?Looper?是线程独立的。

在?Handler?的构造中,从抛出的异常可知,每个线程想要获得?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));
}

同样很简单,就是给?sThreadLocal?设置一个?Looper。不过需要注意的是如果?sThreadLocal?已经设置过了,那么会抛出异常,也就是说一个线程只会有一个?Looper。创建?Looper?的时候,内部会创建一个消息队列:

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

现在的问题是,?Looper看上去很重要的样子,它到底是干嘛的?
回答:?Looper?开启消息循环系统,不断从消息队列?MessageQueue?取出消息交由?Handler?处理。

为什么这样说呢,看一下?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();
    }
}

这个方法的代码有点长,不去追究细节,只看整体逻辑。可以看出,在这个方法内部有个死循环,里面通过?MessageQueue?的next()?方法获取下一条消息,没有获取到会阻塞。如果成功获取新消息,便调用msg.target.dispatchMessage(msg)msg.target是?Handler?对象(下一节会看到),dispatchMessage?则是分发消息(此时已经运行在 UI 线程),下面分析消息的发送及处理流程。

消息发送与处理

在子线程发送消息时,是调用一系列的?sendMessagesendMessageDelayed?以及?sendMessageAtTime?等方法,最终会辗转调用sendMessageAtTime(Message msg, long 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);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

这个方法就是调用?enqueueMessage?在消息队列中插入一条消息,在?enqueueMessage总中,会把?msg.target?设置为当前的Handler?对象。

消息插入消息队列后,?Looper?负责从队列中取出,然后调用?Handler?的?dispatchMessage?方法。接下来看看这个方法是怎么处理消息的:

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

首先,如果消息的?callback?不是空,便调用?handleCallback?处理。否则判断?Handler?的?mCallback?是否为空,不为空则调用它的?handleMessage方法。如果仍然为空,才调用?Handler?自身的?handleMessage,也就是我们创建?Handler?时重写的方法。

如果发送消息时调用?Handler?的?post(Runnable r)方法,会把?Runnable封装到消息对象的?callback,然后调用sendMessageDelayed,相关代码如下:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

此时在?dispatchMessage中便会调用?handleCallback进行处理:

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

可以看到是直接调用了?run?方法处理消息。

如果在创建?Handler时,直接提供一个?Callback?对象,消息就交给这个对象的?handleMessage?方法处理。Callback?是?Handler内部的一个接口:

public interface Callback {
    public boolean handleMessage(Message msg);
}

以上便是消息发送与处理的流程,发送时是在子线程,但处理时?dispatchMessage?方法运行在主线程。

总结

至此,Android消息处理机制的原理就分析结束了。现在可以知道,消息处理是通过?Handler?、Looper?以及?MessageQueue共同完成。?Handler?负责发送以及处理消息,Looper?创建消息队列并不断从队列中取出消息交给?Handler,?MessageQueue?则用于保存消息。

软件
前端设计
程序设计
Java相关