HandlerThread(子线程也可以有消息传递机制)

知识点

HandlerThread,顾名思义,Handler+Thread,就是让子线程也能有一套和MainThread一样的Handler消息机制。

  • HandlerThread本质上是一个线程类,它继承了Thread
  • HandlerThread有自己的内部Looper对象,可以进行looper循环
  • 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
  • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象

MainThread vs HandlerThread

MainThreadHandler的消息传递机制
  1. 用户启动一个应用,系统内部建立一个进程。
  2. 进程启动主线程Main Thread。
  3. Main Thread通过Looper建立一个消息队列Message Queue。
  4. 消息队列是存在于主线程中的,在主线程中开始无限循环。
  5. 每当有新的Message进来,消息队列就开始处理,如果没有,就执行return继续等待。
HandlerThread的消息传递机制
  1. 创建实例对象HandlerThread
  2. 启动HandlerThread的主线程Thread()
  3. 创建一个Handler,该Handler使用的是HandlerThread的looper,并传入一个自定义的的消息处理机制来处理
  4. 处理完毕调用MainThread创建的另外一个Handler去处理结果

区别:

  • 平时使用Handler的时候系统内部已经帮我们创建好主线程和启动主线程了,而使用HandlerThread需要我们手动去创建一个属于HandlerThread的主线程并且去启动它

  • 使用Handler的时候使用的是HandlerThread的looper而不是MainThread的looper

  • 不使用handleMessage来处理,而是使用一个实现了Handler.Callback接口的自定义的处理方法

  • HandlerThread不能操作UI,所以处理完毕后还是需要使用主线程的Handler去处理UI

这个和直接在MainThread中new Thread的作用是一样的,只不过是HandlerThread比Thread多了一套和MainThread一样的消息处理机制,有利于串行地处理消息,不会造成并发。

如图所示:

这里写图片描述


一个点击下载图片的例子

一个点击下载图片的例子

package com.bourne.android_common.ServiceDemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

import com.bourne.android_common.R;
import com.bourne.common_library.utils.Logout;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class HandlerThreadActivity extends AppCompatActivity {
    class ImageBean {
        private String url;
        private Bitmap bitmap;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public Bitmap getBitmap() {
            return bitmap;
        }

        public void setBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
        }
    }

    /**
     * 图片地址集合
     */
    private String url[] = {
            "http://img0.imgtn.bdimg.com/it/u=1597254274,1405139366&fm=23&gp=0.jpg",
            "http://img0.imgtn.bdimg.com/it/u=3901634069,2243065451&fm=23&gp=0.jpg",
            "http://img4.imgtn.bdimg.com/it/u=1800624712,2677106110&fm=23&gp=0.jpg",
            "http://img0.imgtn.bdimg.com/it/u=2456066925,446683653&fm=23&gp=0.jpg",
            "http://img0.imgtn.bdimg.com/it/u=565155430,1247415230&fm=23&gp=0.jpg",
            "http://img4.imgtn.bdimg.com/it/u=2845715753,1348257911&fm=23&gp=0.jpg",
            "http://img3.imgtn.bdimg.com/it/u=3634032659,2514353810&fm=23&gp=0.jpg"
    };

    private ImageView imageView;
    private HandlerThread handlerThread;
    private Thread loadImageThread;
    private int count = 0;

    /**
     * 处理UI
     */
    Handler mainThreadHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Logout.e("次数:" + msg.what);
            ImageBean imageBean = (ImageBean) msg.obj;
            imageView.setImageBitmap(imageBean.getBitmap());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        imageView = (ImageView) findViewById(R.id.imageView);
        createHandlerThread();
    }

    /**
     * 通过HandlerThread的方式加载
     * @param view
     */
    public void loadByHandlerThread(View view) {
        Handler.Callback callBack = new loadImageCallBack();
        Handler handlerThreadHandler = new Handler(handlerThread.getLooper(), callBack);
        for (int i = 0; i < url.length; i++) {
            handlerThreadHandler.sendEmptyMessageDelayed(i, 1000 * i);
        }
    }

    /**
     * 通过Thread的方式加载
     * @param view
     */
    public void loadByThread(View view) {
        if (loadImageThread != null && !loadImageThread.isInterrupted()) {
            loadImageThread.interrupt();
        }
        count = 0;
        loadImageThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        Thread.sleep(1000);
                        //在子线程中进行网络请求
                        Bitmap bitmap = downloadUrlBitmap(url[count]);
                        ImageBean imageBean = new ImageBean();
                        imageBean.setBitmap(bitmap);
                        imageBean.setUrl(url[count]);

                        Message message = new Message();
                        message.what = count;
                        message.obj = imageBean;
                        count++;

                        mainThreadHandler.sendMessage(message);
                        //最后一张时停止加载
                        if (count >= url.length) {
                            loadImageThread.interrupt();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Logout.e("加载完毕,停止线程");
                }
            }
        });

        loadImageThread.start();
    }

    /**
     * 处理下载图片
     */
    class loadImageCallBack implements Handler.Callback {

        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行网络请求
            Bitmap bitmap = downloadUrlBitmap(url[msg.what]);
            ImageBean imageBean = new ImageBean();
            imageBean.setBitmap(bitmap);
            imageBean.setUrl(url[msg.what]);

            Message message = new Message();
            message.what = msg.what;
            message.obj = imageBean;

            mainThreadHandler.sendMessage(message);
            return false;
        }
    }

    /**
     * 创建一个HandlerThread
     */

    private void createHandlerThread() {
        //创建实例对象
        handlerThread = new HandlerThread("downloadImage");
        handlerThread.start();
    }

    /**
     * 下载图片的网络请求
     *
     * @param urlString
     * @return
     */
    private Bitmap downloadUrlBitmap(String urlString) {
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        Bitmap bitmap = null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bitmap = BitmapFactory.decodeStream(in);
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
         //释放资源
        handlerThread.quit();
    }
}

可以看到我们在onCreate的时候创建了一个HandlerThread,并开启它的线程。点击按钮的时候创建一个Handler,取HandlerThread的Looper,并完成线程的操作。操作完成之后,发消息给UI线程并改变UI。

布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    tools:context="com.bourne.android_common.ServiceDemo.HandlerThreadActivity">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="loadByHandlerThread"
        android:text="使用HandlerThread下载图片"
        android:textAllCaps="false"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="loadByThread"
        android:text="使用Thread下载图片"
        android:textAllCaps="false"/>
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

源码解析

完整代码:

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

/**
 * 
 */
public class HandlerThread extends Thread {
    /**
     * 线程优先级
     */
    int mPriority;

    int mTid = -1;

    /**
     * 当前线程持有的Looper对象
     */
    Looper mLooper;

    public MyHandlerThread(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 MyHandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     *必要时可以自己去重写
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            //Looper对象将被创建
            mLooper = Looper.myLooper();
            //唤醒等待线程
            notifyAll();
        }
        //设置进程优先级
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //开启looper循环语句
        Looper.loop();
        mTid = -1;
    }

    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.
     * @return The looper.
     */
    public Looper getLooper() {
        //  判断当前线程是否启动了
        if (!isAlive()) {
            return null;
        }


        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    //等待唤醒
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

    /**
     * 线程优先级
     */
    int mPriority;

    int mTid = -1;

    /**
     * 当前线程持有的Looper对象
     */
    Looper mLooper;

    public MyHandlerThread(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 MyHandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     *必要时可以自己去重写
     */
    protected void onLooperPrepared() {
    }

这里初始化了Handler的对象,设置了优先级,mLooper是持有的Looper对象,onLooperPrepared是一个空实现,必要是可以自己重写。


@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            //Looper对象将被创建
            mLooper = Looper.myLooper();
            //唤醒等待线程
            notifyAll();
        }
        //设置进程优先级
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //开启looper循环语句
        Looper.loop();
        mTid = -1;
    }

run()方法中调用了Looper.prepare(),Loop.loop(),prepare()负责创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。

loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。



    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.
     * @return The looper.
     */
    public Looper getLooper() {
        //  判断当前线程是否启动了
        if (!isAlive()) {
            return null;
        }


        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    //等待唤醒
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

Looper的创建是在子线程中执行的,而Handler通过getLooper去获取mLooper的时候又是在主线程,怎么保证它们可以同步呢,原因是通过唤醒机制:

getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果启动了会判断Looper对象有没有被创建,如果都都没有满足则会继续等待,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,返回mLooper对象给Handler使用。


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

  从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
  
  当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。
  


参考文章

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值