Android多线程--HandlerThread的使用以及原理

来源:互联网 发布:360软件管理下载 编辑:程序博客网 时间:2024/05/22 04:57

        如果已经理解了Android的消息机制(Handler、Looper、Message、MessageQueue),再来看HandlerThread就很容易了,参考如下:

http://blog.csdn.net/kitty_landon/article/details/53410183

源码解析

        最直观的方法就是分析源码,先把源码贴出来。

/** * Handy class for starting a new thread that has a looper. The looper can then be  * used to create handler classes. Note that start() must still be called.HandlerThread可以创建拥有Looper的线程,这个Looper能够创建Handler类 */public class HandlerThread extends Thread {    int mPriority;//线程优先级    int mTid = -1;    Looper mLooper;//当前线程持有的Looper对象    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;    }        /**     * Call back method that can be explicitly overridden if needed to execute some     * setup before Looper loops.可以重写,但是在Looper循环之前。     */    protected void onLooperPrepared() {    }    @Override    public void run() {//创建HandlerThread对象后,必须调用start()方法,调用start()方法后,表示线程启动了,也就会调用线程的run()方法;        mTid = Process.myTid();        Looper.prepare();//创建Looper对象,并绑定当前线程        synchronized (this) {            mLooper = Looper.myLooper();//获取Looper对象            notifyAll();//唤醒等待线程        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();//循环        mTid = -1;    }        /**     * This method returns the Looper associated with this thread. 返回与该线程相关联的Looper对象。If this thread not been started     * or for any reason is isAlive() returns false, this method will return null. (如果线程没有开启,或者isAlive()返回false,getLooper()返        回null)。If this thread      * has been started, this method will block until the looper has been initialized. 开启了线程,这个方法将会阻塞,直到Looper初始化      * @return The looper.     */    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;    }    /**     * 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;    }}

        从getLooper()方法实现源码上可以看出外部在通过getLooper()方法获取Looper对象时,会先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待状态,知道Looper对象被创建并通过notifyAll()方法唤醒等待线程,最后才返回Looper对象。之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。   

        从源码可以看出当我们调用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的。

        从源码中我们可以看出HandlerThread有几个特点:

        (1)、HandlerThread本质上是一个线程类,它继承了Thread;

        (2)、HandlerThread有自己的内部Looper对象,可以进行loop循环;

        (3)、通过获取HandlerThread的Looper对象传递给Handler对象,可以在handleMessage()方法中执行异步任务;

        (4)、创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run()方法,创建Looper对象。

HandlerThread常规使用步骤

方案一:利用Handler(Looper looper, Callback callback)

        1、创建HandlerThread实例对象

HandlerThread mMainJobThread = new HandlerThread("qscan");

        传入参数的作用主要是标记当前线程的名字,可以是任意字符串。

        2、启动HandlerThread线程

mMainJobThread.start();

        到此,我们创建完HandlerThread并启动了线程。那么我们怎么将一个耗时的异步任务投放到HandlerThread线程中去执行呢?接下来看下面步骤:

        3、构建循环消息处理机制

  /**     * 该callback运行于子线程     */    class ChildCallback implements Handler.Callback {        @Override        public boolean handleMessage(Message msg) {            //在子线程中进行相应的网络请求            //通知主线程去更新UI            mUIHandler.sendMessage(msg1);            return false;        }    }

        4、构建异步Handler

Handler mMainJobHandler = new Handler(mMainJobThread.getLooper(), new ChildCallback());

        第3步和第4步是构建一个可以用于异步操作的handler,并将前面创建的HandlerThread的Looper对象以及Callback接口类作为参数传递给当前的handler,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了,这样我们的Looper+Handler+MessageQueue+Thread异步循环机制构建完成,来看看一个完整的使用案例。

方案二:利用Handler(Looper looper)

        步骤一和步骤二是相同的;

        3、构建循环消息处理机制

    private class MainJobHandler extends Handler {        public MainJobHandler(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            Log.e(TAG, "MainJobHandler() -> msg.what = " + msg.what);            ......        }    }

        4、构建异步Handler

MainJobHandler mMainJobHandler = new MainJobHandler(mMainJobThread.getLooper());

使用案例

public class HandlerThreadActivity extends Activity {    /**     * 图片地址集合     */    private String url[]={            "http://img.blog.csdn.net/20160903083245762",            "http://img.blog.csdn.net/20160903083252184",            "http://img.blog.csdn.net/20160903083257871",            "http://img.blog.csdn.net/20160903083257871",            "http://img.blog.csdn.net/20160903083311972",            "http://img.blog.csdn.net/20160903083319668",            "http://img.blog.csdn.net/20160903083326871"    };    private ImageView imageView;    private Handler mUIHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            LogUtils.e("次数:"+msg.what);            ImageModel model = (ImageModel) msg.obj;            imageView.setImageBitmap(model.bitmap);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_handler_thread);        imageView= (ImageView) findViewById(R.id.image);        //创建异步HandlerThread        HandlerThread handlerThread = new HandlerThread("downloadImage");        //必须先开启线程        handlerThread.start();        //子线程Handler        Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());        for(int i=0;i<7;i++){            //每个1秒去更新图片            childHandler.sendEmptyMessageDelayed(i,1000*i);        }    }    /**     * 该callback运行于子线程     */    class ChildCallback implements Handler.Callback {        @Override        public boolean handleMessage(Message msg) {            //在子线程中进行网络请求            Bitmap bitmap=downloadUrlBitmap(url[msg.what]);            ImageModel imageModel=new ImageModel();            imageModel.bitmap=bitmap;            imageModel.url=url[msg.what];            Message msg1 = new Message();            msg1.what = msg.what;            msg1.obj =imageModel;            //通知主线程去更新UI            mUIHandler.sendMessage(msg1);            return false;        }    }    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;    }}

        在这个案例中,我们创建了两个Handler,一个用于更新UI线程的mUIHandler和一个用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。

        实际上在android比较典型的应用是IntentService。

原创粉丝点击