Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

异步消息处理机制Handler源码解析

$
0
0

转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/76083113
本文出自:【顾林海的博客】

前言



我们知道在应用启动时会开启一个主线程,也就是UI线程,主线程主要管理与用户交互的UI控件(UI展示,事件分发),如果在主线程中执行耗时操作会触发ANR(Application not responding)异常,意思是程序未响应,这时就需要开启子线程来执行耗时操作,这时又会引发到子线程更新UI的问题,这里要记住Android中主线程是不安全的,为了解决子线程能更新UI状态,因此在Android平台中引入了一个叫做Handler机制,可以通过在子线程通过Handler发送消息给主线程来进行UI的更新,并且在子线程中可以进行耗时操作。总的来说Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。

Handler的使用方法



1. 通过post(runnable)方法进行延迟消息的处理。
2. 通过sendMessage(message)方法进行子线程与主线程之间的消息传递。


首先我们演示第一种方式,通过post(runnable)方法来进行消息的处理,具体代码如下:

package com.glh.intentservice;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView mInfoTextView;
    private Button mDownloadButton;

    private static Handler mHandler = new Handler();


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

    private void initView() {
        mInfoTextView = (TextView) findViewById(R.id.tv_info);
        mDownloadButton = (Button) findViewById(R.id.btn_download);
    }

    private void initEvent() {
        mDownloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mInfoTextView.setText("下载中...");
                new DownloadThread(0).start();
            }
        });
    }

    class DownloadThread extends Thread {

        private int mProcess = 0;

        DownloadThread(int process) {
            mProcess = process;
        }

        @Override
        public void run() {
            while (mProcess < 100) {
                mProcess++;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    mInfoTextView.setText("下载成功");
                }
            };
            mHandler.post(runnable);
        }
    }

}



分析上面代码,我们在创建Handler时,是在主线程中创建的,也就是说这个Handler绑定在主线程中,代码中,创建继承自Thread类的DownloadThread的线程类,并在run方法中执行了一个耗时操作,这里可以看出执行完耗时操作后,创建了一个Runnable对象,并在Runnable中的run方法中进行UI的更新,最后通过Handler的post方法进行消息的处理。


运行效果:

这里写图片描述



最后看看sendMessage(message)方法使用,代码如下:

package com.glh.intentservice;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {

    private TextView mInfoTextView;
    private Button mDownloadButton;

    private MyHandler mHandler = new MyHandler(this);

    static class MyHandler extends Handler {

        private WeakReference<MainActivity> weakReference;

        MyHandler(MainActivity activity) {
            weakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = weakReference.get();
            if (null != activity) {
                int what = msg.what;
                switch (what) {
                    case 1:
                        activity.downloadSuccess((String) msg.obj);
                        break;
                    default:
                        break;
                }

            }
        }
    }

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

    private void initView() {
        mInfoTextView = (TextView) findViewById(R.id.tv_info);
        mDownloadButton = (Button) findViewById(R.id.btn_download);
    }

    private void initEvent() {
        mDownloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mInfoTextView.setText("下载中...");
                new DownloadThread(0).start();
            }
        });
    }

    private void downloadSuccess(String info) {
        mInfoTextView.setText(info);
    }

    class DownloadThread extends Thread {

        private int mProcess = 0;

        DownloadThread(int process) {
            mProcess = process;
        }

        @Override
        public void run() {
            while (mProcess < 100) {
                mProcess++;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message message = Message.obtain();
            message.what = 1;
            message.obj = "下载成功";
            mHandler.sendMessage(message);
        }
    }

}


在代码中创建一个静态内部类MyHandler,为什么要创建静态内部类呢?这是因为,非静态内部类会持有外部类Activity的匿名引用,如果在子线程执行耗时操作,在耗时操作时,Activity退出,通过Handler发送消息,这时由于Activity不存在会引起Activity的内存泄露,因此这里使用静态内部类,并使用WeakReference弱引用。

代码中创建了一个线程,并执行了耗时操作,耗时操作完毕后,创建了Message对象,并给Message的what设置1,obj设置”下载成功”,这里的what属性用于在主线程中Handler的handleMessage方法中进行不同Message的处理,obj属性用于携带相关数据。最后通过Handler的sendMessage(message)方法发送消息给主线程,主线程的Handler的handleMessage方法中通过msg.what识别哪个message,并进行相应的处理。



运行效果:

这里写图片描述

Handler机制

在讲解Handler机制时,先来了解一下Looper、MessageQueue、Message所代表的含义。Handler主要包含两种作用,分别是发送消息到消息队列和处理消息;Looper用于为当前线程生成一个消息队列,并执行一个循环,不停的从消息队列中获取Message消息;MessageQueue就是一个先进先出的消息队列,提供了next方法用于获取Message消息,以及enqueueMessage方法将消息添加到消息队列中;Message代表消息。


在Android中新开启的线程是没有开启消息循环的(主线程除外),如果在子线程中开启消息循环就需要这样做:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}



在run方法中主要三件事:

  • 通过Looper.prepare实例化Looper对象,并生成一个消息队列。
  • 创建Handler,并与Looper相关联。
  • 通过Looper.loop开启消息循环,并处理消息。


    先查看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));
}



在prepare方法中sThreadLocal是ThreadLocal对象,在创建Handler时通过sThreadLocal获取当前线程的Looper,这个在后面会提到,上面代码中会先判断sThreadLocal是否为null,如果不为null,就抛出异常,说明Looper.prepare()方法不能执行两次以上,也就是说一个线程中只有一个Looper实例,接着看Looper的构造器。

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



在Looper构造器中创建了MessageQueue对象,mThread是指当前的线程。到这里Looper.prepare()方法执行完毕,总结Looper.prepare方法做了如下工作:


- 一个线程只能有一个Looper实例。
- 创建Looper时,会关联一个MessageQueue。


接着看Looper.loop()方法:

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;

    for (; ; ) {
        //没有消息时阻塞
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        try {
            //通过Handler方法消息
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //消息的回收
        msg.recycleUnchecked();
    }
}



上面的代码进行了局部精简,在loop()方法中,先是通过myLooper()方法获取当前线程的Looper对象,在myLooper()方法中通过sThreadLocal.get()方法获取Looper对象,这里的sThreadLocal在讲解Looper.prepare()方法时已经讲过了。接着获取当前Looper的MessageQueue消息队列,并通过一个无限循环遍历消息队列,当消息队列中没有消息时处于阻塞状态,当消息队列中有消息时,取出消息,通过msg.target的dispatchMessage方法发送消息,这里的msg.target就是Handler,最后进行消息的回收。总结Looper.loop()方法做了如下工作:


- 获取当前线程Looper对象。
- 从当前线程中的Looper对象中获取消息队列。
- 开启无限循环,遍历消息队列。
- 在循环中通过msg.target进行消息的发送。


最后看看Handler的源码:

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


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


mLooper是之前在Loop.prepare()方法执行时关联的Looper对象,这这段代码中会去判断mLooper是否空,如果为空抛出异常,说明创建Handler之前需要通过Looper.prepare()方法创建当前线程的Looper对象并创建一个MessageQueue消息队列,这样的话Handler才能与MessageQueue进行关联,这里面mQueue就是Looper创建的MessageQueue对象。


Handler创建完毕后就需要我们进行发送消息,这里主要分析前面两种Handler使用方法:


1. 通过post(runnable)方法进行延迟消息的处理。
2. 通过sendMessage(message)方法进行子线程与主线程之间的消息传递。

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



通过post(runnable)方法进行消息的处理时,Handler内部会调用sendMessageDelayed方法,而sendMessageDelayed方法的第一个参数就是Message对象,通过getPostMessage(runnable)方法进行包装,在getPostMessage方法中获取Message对象,并将Runnable对象赋值给Message的callback属性。


public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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方法中,将当前的Handler赋值给Message的target属性,这里面的msg.target是不是很熟悉,在之前Looper.loop()方法中进行消息队列的遍历,最终就是通过它来发送消息的。这里的queue就是与当前线程关联的Looper对象的MessageQueue,执行到最后通过MessageQueue的enqueueMessage方法将Message放入消息队列中。


接着查看sendMessage(message)方法的实现原理:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}



查看到这里,大家就会察觉到,这里与上面通过post(runnable)方法的实现逻辑是一样,都是通过MessageQueue的enqueueMessage方法将Message放入消息队列中。当消息放入消息队列MessageQueue后,是如何获取Message并进行处理的呢?这里又回到了上面Looper.loop()方法中的无限循环遍历消息队列中的msg.target的dispatchMessage方法,从上面存放消息队列的分析中,我们已经知道了msg.target就是一个Handler,因此,我们查看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就是Message中的Runnable对象,我们在上面讲解Handler的使用方法中讲的第一种方法,通过post(runnable)方法,这里面的runnable在post(runnable)源码中封装Message时,已经赋值给了Message的callback属性,回到上面的代码中,第一步判断Message的callback属性是否为空,不为空执行handleCallback(msg)方法,如果为空,判断当前Handler的mCallback属性是否为空,如果不为空,执行Callback 的handleMessage的方法,如果Handler的mCallback属性为空或者是Callback的handleMessage方法返回false,执行Handler中的方法handleMessage(msg),这里面的mCallback是一个内部Callback接口:

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



handleMessage方法是不是很熟悉,没错,它是在创建Handler时,如果实现了handleMessage方法,也就是说交由Handler来处理消息时,会执行这个方法。如果只是new Handler(),那么这个mCallback为空。

如果通过post(runnable)方法处理消息,这时会执行handleCallback(msg)方法:

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



很简单,就是调用Runnable的run()方法,准确来说,post(runnable)并没有开启线程,只是纯粹的调用Runnable的run方法,这也说明了为什么通过Handler可以更新UI。

到此Handler机制原理解析完毕。

作者:GULINHAI12 发表于2017/7/25 16:31:19 原文链接
阅读:51 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>