Android学习之HandlerThread使用场景和源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36391075/article/details/82560338

为了避免ANR,我们常常需要在线程中做耗时操作,然后把结果抛到主线程进行处理。
在Android中为了提供了很多方式,其中一种就是HandlerThread。

先看看官网对它的介绍:

Handy class for starting a new thread that has a looper. The looper can then be
used to create handler classes.

一个用于启动具有looper的新线程的类,这个looper可以用于创建Handler。

我们先看一个基本的使用,模拟下载的过程:

这里写图片描述

代码如下:

public class DownLoadThread extends HandlerThread implements Handler.Callback {

    private final String TAG = this.getClass().getSimpleName();
    private final String KEY_URL = "url";
    public static final int TYPE_START = 1;
    public static final int TYPE_FINISHED = 2;

    private Handler mWorkHandler;
    private Handler mUIHandler;

    private List<String> mDownLoadUrlList;

    public DownLoadThread(String name) {
        super(name);
    }

    @Override
    protected void onLooperPrepared() {
        super.onLooperPrepared();
        mWorkHandler = new Handler(getLooper(),this);
        if (mUIHandler == null) {
            throw new IllegalArgumentException("Not set UIHandler!");
        }

        for(String url:mDownLoadUrlList){
            Message message = mWorkHandler.obtainMessage();
            Bundle bundle = new Bundle();
            bundle.putString(KEY_URL,url);
            message.setData(bundle);
            mWorkHandler.sendMessage(message);
        }
    }

    public void setDownloadUrls(String... urls) {
        mDownLoadUrlList = Arrays.asList(urls);
    }

    public Handler getUIHandler() {
        return mUIHandler;
    }

    //注入主线程 Handler
    public DownLoadThread setUIHandler(final Handler UIHandler) {
        mUIHandler = UIHandler;
        return this;
    }

    @Override
    public boolean handleMessage(Message msg) {
        if(msg == null || msg.getData() == null){
            return false;
        }

        String url = (String)msg.getData().get(KEY_URL);

        //开始下载

        Message startMessage = mUIHandler.obtainMessage(TYPE_START,
                "\n 开始下载 @"+System.currentTimeMillis()+"\n"+url);
        mUIHandler.sendMessage(startMessage);

        SystemClock.sleep(2000);

        Message finishMessage = mUIHandler.obtainMessage(TYPE_FINISHED,
                "\n 下载完成 @"+System.currentTimeMillis()+"\n"+url);
        mUIHandler.sendMessage(finishMessage);

        return true;

    }

    @Override
    public boolean quitSafely() {
        mUIHandler = null;
        return super.quitSafely();
    }


}

在Activity中:

public class MainActivity extends AppCompatActivity implements Handler.Callback {


    private TextView mStart;
    private TextView mFinish;
    private Button mDownLoad;
    private DownLoadThread mDownLoadThread;
    private Handler mUIHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mStart = findViewById(R.id.tv_start);
        mFinish = findViewById(R.id.tv_finish);
        mDownLoad = findViewById(R.id.btn_start);
        mDownLoadThread = new DownLoadThread("DownLoad");
        mUIHandler = new Handler(this);
        mDownLoadThread.setUIHandler(mUIHandler);
        mDownLoadThread.setDownloadUrls("http://pan.baidu.com/s/1qYc3EDQ",
                "http://bbs.005.tv/thread-589833-1-1.html", "http://list.youku.com/show/id_zc51e1d547a5b11e2a19e.html?");
        mDownLoad.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mDownLoadThread.start();
                mDownLoad.setText("正在下载");
                mDownLoad.setEnabled(false);
            }
        });

    }


    @Override
    public boolean handleMessage(Message msg) {

        if(msg == null ){
            return false;
        }

        switch (msg.what){
            case DownLoadThread.TYPE_START:
                mStart.setText(mStart.getText().toString()+
                "\n"+msg.obj);
                break;

            case DownLoadThread.TYPE_FINISHED:
                mFinish.setText(mFinish.getText().toString()+
                        "\n"+msg.obj);
                break;
        }

        return true;
    }
}

源码分析:

首先我们先看看HandlerThread的构造方法:

public class HandlerThread extends Thread {
    int mPriority;//线程优先级
    int mTid = -1;
    Looper mLooper;//当前线程持有的Looper
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

我们可以看到HandlerThread继承自Thread,它有两个构造方法,name表示线程的名字,priority表示线程的优先级。
既然是Thread,那么我一定会调用它的start方法,start方法就一定会调用run方法,那么我们就看看run方法:

 @Override
    public void run() {
        mTid = Process.myTid();
        //这个方法会为这个线程新建一个Looper,也是唯一一个Looper
        Looper.prepare();//初始化Looper
        synchronized (this) {
            //得到该线程的Looper
            mLooper = Looper.myLooper();
            //唤醒线程
            notifyAll();
        }
        //设置优先级
        Process.setThreadPriority(mPriority);
        //这个方法需要我们自己实现
        onLooperPrepared();
        //让Looper工作起来
        Looper.loop();
        mTid = -1;
    }
protected void onLooperPrepared() {
    }

我们可以看到,其实HandlerThread的源码没有多少,哪里需要唤醒呢?

 public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

没什么好说的,就是等待Looper有值。

其实HandlerThread还是一个Handler的消息机制的操作。
我们使用的时候,通过HandlerThread的Handler将消息投递进来,处理完后,然后通过UI的Handler将消息投递到UI线程。

所以我们还是回顾一下Handler的消息机制:
https://blog.csdn.net/qq_36391075/article/details/80720519
https://blog.csdn.net/qq_36391075/article/details/80637473

HandlerThread中好还有两个值得注意的方法:


public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

其实这两个方法都调用了Looper的方法,最后都会调用Looper中:

 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

当我们调用quit方法的时候,实际上是执行了MessageQueue中的removeAllMessagesLocked(),该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息,还是非延迟消息。

当我们调用quitSafely方法时,实际上是执行了MessageQueue中的removeAllFutureMessagesLocked();,该方法只会清空MessageQueue消息池中所有延迟消息,并将消息池中所有非延迟消息派发出去让Handler去处理。

两个方法都有一个共同点:Looper不再接受新的消息了,消息循坏就此结束了。

猜你喜欢

转载自blog.csdn.net/qq_36391075/article/details/82560338