HandlerThread类应用

Posted xuguoli_beyondboy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HandlerThread类应用相关的知识,希望对你有一定的参考价值。

通常AsyncTask和Handler都是为异步更新UI而诞生的两个类,只是AsyncTask是一个封装后的后台任务类,是方便大家简单处理异步任务后更新UI的操作(如短时间异步任务操作),但它并不适合处理所有的后台异步任务操作,它也存在一下几点问题:

  • 当它在Activity或Fragment作为非静态内部类,一旦它创建了实例,它就会引用了外部类的Activity或Fragment实例,如果这个AsyncTask实例生命周期较长,即使Activity或Fragment 调用了destroyed()方法也不会销毁掉让出内存,从而引起了内存泄漏。
  • 如果它在Activity或Fragment作为静态内部类或单独的一个类,因其它一般需要引用Context去异步更新UI视图,所以它也避免不了对Context判断(以防Context被销毁转为空了)。
  • 它只能一次行执行任务,而不能重复执行使用,并且当一个执行任务消耗太长时间时,否则容易引起ANR异常。
    后面我将会专门写一篇博客介绍这个AsyncTask类的实现原理。
    这时我们可以很好用子线程Handler来去执行消耗时间任务,而让主线程Handler实现异步更新UI,这种方式实现异步更新其实较复杂的,同时需要自己创建一条线程绑定handler,并创建对应的Looper和MessageQueue来实现,如官方给出的实例:
class LooperThread extends Thread 
      public Handler mHandler;

      public void run() 
          //创建Looper和MessageQueue实例
          Looper.prepare();

          mHandler = new Handler() 
          //通过当前的子线程处理消耗时间消息任务
              public void handleMessage(Message msg) 
                  // process incoming messages here
              
          ;
        //启动Looper循环,因此loop()方法的后面代码将不会被执行
          Looper.loop();          
      
  

至于为何需要这些代码创建一个用子线程handler来执行消耗时间I的线程类,可以参考我下面这篇博客,它涉及了handler,Looper,MessageQueue三者联系分析:
http://blog.csdn.net/xuguoli_beyondboy/article/details/50396439
谷歌为了方便我们创建一个用子线程handler来执行消耗时间I的线程类来去协作主线程Handler异步更新UI,推出了一个HandlerThread类,它是Thread的子类,但它已经在run方法中创建了Looper和MessageQueue实例,代码如下:

 @Override
    public void run() 
        mTid = Process.myTid();
        //创建绑定了该子线程的Looper和MessageQueue实例
        Looper.prepare();
        synchronized (this) 
            mLooper = Looper.myLooper();
            notifyAll();
        
        Process.setThreadPriority(mPriority);        
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    

HandlerThread类的Demo:
layout/activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center">
  <ProgressBar
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/progressBar"/>
  <LinearLayout
      android:orientation="horizontal"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:id="@+id/leftSideLayout">
    </LinearLayout>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:id="@+id/rightSideLayout">
    </LinearLayout>
  </LinearLayout>
</LinearLayout>

为了通过单独的线程Handler来处理耗时而不是涉及UI更新的操作,故创建一个继承HandlerThread类的子类:

package com.scau.beyondboy.handlerdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v4.util.ArrayMap;
import android.util.Log;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 15-12-27
 * Time: 下午6:44
 * 创建HandlerThread子类,单独处理某些耗时的操作
 * HandlerThread其实是一个Thread的子类,并在
 * run方法中创建绑定自己线程的Looper,MessageQueue实例
 */
public class MyWorkerThread extends HandlerThread

    /**单独处理自己消息池的线程Handler,一般来说此线程不是UI线程,故在处理消息过程中不能更新UI等操作*/
    private Handler mWorkerHandler;
    /*×UI线程的Handler处理UI更新操作*/
    private Handler mResponseHandler;
    private static final String TAG = MyWorkerThread.class.getSimpleName();
    /**用数组Map减少内存开销*/
    private Map<ImageView, String> mRequestMap = new ArrayMap<>();
    private Callback mCallback;
    /*×用于更新ImageView的回调接口,方法会在主线程里面执行*/
    public interface Callback
    
         void onImageDownloaded(ImageView imageView, Bitmap bitmap, int side);
    

    /**
     * @param responseHandler 设置更新UI的handler
     * @param callback 回调实例
     */
    public MyWorkerThread(Handler responseHandler, Callback callback)
    
        super(TAG);
        mResponseHandler = responseHandler;
        mCallback = callback;
    

    /*×插入执行的任务*/
    public void queueTask(String url, int side, ImageView imageView)
    
        mRequestMap.put(imageView, url);
        Log.i(TAG, url + " added to the queue:  "+Thread.currentThread().getId());
        //发送消息绑定的MessageQueue
        mWorkerHandler.obtainMessage(side, imageView)
                .sendToTarget();
    

    /**创建和当前线程Looper,MessageQueue绑定的Handler实例*/
    public void prepareHandler()
    
        //创建绑定HandlerThread线程的Looper对象和处理消息的Handler实例
        mWorkerHandler = new Handler(getLooper(), new Handler.Callback()
        
            //会在HandlerThread子线程中执行,而不是UI线程,返回true,handler重写的handleMessage方法将不会被执行
            @Override
            public boolean handleMessage(Message msg) 
                try 
                    Log.i(TAG,"模拟操作:  "+Thread.currentThread().getId());
                    //模拟操作
                    TimeUnit.SECONDS.sleep(2);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
                ImageView imageView = (ImageView) msg.obj;
                String side = msg.what ==MainActivity.LEFT_SIDE ? "left side" : "right side";
                Log.i(TAG, String.format("Processing %s, %s", mRequestMap.get(imageView), side));
                handleRequest(imageView, msg.what);
                //不能调用下面方法,否则会抛异常
               //msg.recycle();
                return true;
            
        );
    
    /**处理图片请求,该方法还是在HandlerThread子线程中执行*/
    private void handleRequest(final ImageView imageView, final int side) 
        String url = mRequestMap.get(imageView);
        try 
            HttpURLConnection connection =
                    (HttpURLConnection) new URL(url).openConnection();
            final Bitmap bitmap = BitmapFactory
                    .decodeStream((InputStream) connection.getContent());
            mRequestMap.remove(imageView);            
            //发送到UI线程绑定的消息池中,以在UI线程中执行
            mResponseHandler.post(new Runnable()
            
                @Override
                public void run()
                
                    mCallback.onImageDownloaded(imageView, bitmap, side);
                
            );
         catch (IOException e) 
            e.printStackTrace();
        
       

MainActivity类:

package com.scau.beyondboy.handlerdemo;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.Random;
/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 15-12-27
 * Time: 下午6:44
 * Handler,Looper,MessageQueue实战
 */
public class MainActivity extends AppCompatActivity implements MyWorkerThread.Callback


    private static final String TAG = MainActivity.class.getName();
    private static boolean isVisible;
    //添加左边和右边布局的标记
    public static final int LEFT_SIDE = 0;
    public static final int RIGHT_SIDE = 1;
    private LinearLayout mLeftSideLayout;
    private LinearLayout mRightSideLayout;
    //自定义的处理消息线程,实际为HandlerThread子类
    private MyWorkerThread mWorkerThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        isVisible = true;
        mLeftSideLayout = (LinearLayout) findViewById(R.id.leftSideLayout);
        mRightSideLayout = (LinearLayout) findViewById(R.id.rightSideLayout);
        String[] urls = new String[]"http://avatar.csdn.net/D/D/4/1_xuguoli_beyondboy.jpg",
                "http://avatar.csdn.net/D/D/4/1_xuguoli_beyondboy.jpg",
                "http://avatar.csdn.net/D/D/4/1_xuguoli_beyondboy.jpg",
                "http://avatar.csdn.net/D/D/4/1_xuguoli_beyondboy.jpg";
        //传进用来发送消息到主线程的MessageQueue和处理主线程消息的handler
        mWorkerThread = new MyWorkerThread(new Handler(), this);
        //自定义处理消息线程启动
        mWorkerThread.start();
        //创建绑定当前线程Handler实例
        mWorkerThread.prepareHandler();
        Random random = new Random();
        //执行任务
        for (String url : urls)
        
            //queueTask还是在主线程执行而非HandlerThread线程
            mWorkerThread.queueTask(url, random.nextInt(2), new ImageView(this));
        
    

    @Override
    protected void onPause()
    
        isVisible = false;
        super.onPause();
    

    @Override
    protected void onDestroy()
    
        //退出当前线程的Looper循环
        mWorkerThread.quit();
        super.onDestroy();
    

    /**实现callback的回调方法,该方法在主线程执行*/
    @Override
    public void onImageDownloaded(ImageView imageView, Bitmap bitmap, int side)
    
        Log.i(TAG, "threadid更新视图:   " + Thread.currentThread().getId());
        imageView.setImageBitmap(bitmap);
        if (isVisible && side == LEFT_SIDE)
            mLeftSideLayout.addView(imageView);
         else if (isVisible && side == RIGHT_SIDE)
            mRightSideLayout.addView(imageView);
               
    

运行结果如图:

以上是关于HandlerThread类应用的主要内容,如果未能解决你的问题,请参考以下文章

Android源码学习-----HandlerThread

Handlerthread使用

HandlerThread原理与应用

android源码解析之-->HandlerThread

Android HandlerThread 消息循环机制之源代码解析

HandlerThread 的使用及其源码完全解析