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

来源:互联网 发布:windows消息机制详解 编辑:程序博客网 时间:2024/03/29 13:51

知识点

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的。
  


参考文章

  • Android 多线程之HandlerThread 完全详解

  • Android HandlerThread 完全解析

0 0
原创粉丝点击