深入理解HandlerThread

1.HandlerThread 是什么?

英文解释:

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。HandlerThread其本质还是一个普通的Thread,其内部创建了一个looper。

即:它就是一个帮我们创建 Looper 的线程,让我们可以直接在子线程中使用 Handler 来处理异步任务。

 

2.HandlerThread的起源

某些场景需要我们在子线程创建handler

class LooperThread extends Thread {
    public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // 这里处理消息
              }
          };

          Looper.loop();
      }

此种写法对开发者要求高。HandlerThread集handler与Thread所长。简化此步骤。

3.HandlerThread的特点

  • HandlerThread将loop转到子线程中处理,将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
  • 开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
    相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
    但是由于每一个任务都将以队列的方式逐个被执行到(会把所有消息放入messageQuene,随后才执行handleMessage),一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

  • HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
  • 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化
  • HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。

    HandlerThread 所做的就是在新开的子线程中创建了 Looper,那它的使用场景就是 Thread + Looper 使用场景的结合,即:在子线程中执行耗时的、可能有多个任务的操作。比如说多个网络请求操作,或者多文件 I/O 等等。使用 HandlerThread 的典型例子就是 IntentService。

4.案例 模拟下载多个文件

DownLoadThread.class
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 mWorkerHandler;
    private Handler mUIHandler;
    private List<String> mDownloadUrlList;


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

    public DownLoadThread(String name, int priority) {
        super(name, priority);
    }


    @Override
    protected void onLooperPrepared() {
        super.onLooperPrepared();

        mWorkerHandler = new Handler(getLooper(), this);
        if (mUIHandler == null) {
            throw new IllegalArgumentException("Not set UIHandler!");
        }

        for (String url : mDownloadUrlList) {
            Message message = mWorkerHandler.obtainMessage();
            Bundle bundle = new Bundle();
            bundle.putString(KEY_URL, url);
            message.setData(bundle);
            mWorkerHandler.sendMessage(message);
            Log.i("onion", "sendMessage....");
        }

    }

    public void setDownloadUrlList(String... urls) {
        mDownloadUrlList = Arrays.asList(urls);
    }

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

    public Handler getUIHandler() {
        return mUIHandler;
    }


    @Override
    public boolean quitSafely() {
        Log.i("onion", "quitSafely....");
        mUIHandler = null;
        mWorkerHandler.removeCallbacksAndMessages(null);//添加后台终止下载,不添加继续运行
        return super.quitSafely();

    }

    @Override
    public boolean handleMessage(Message msg) {
    //这个方法运行在子线程中
        if (msg == null || msg.getData() == null) {
            return false;
        }
        Log.i("onion", "mWorkerHandler begin  handleMessage....");
        String url = (String) msg.getData().getString(KEY_URL);
        if (mUIHandler != null) {
            //下载开始,通知主线程
            Message startMsg = mUIHandler.obtainMessage(TYPE_START, "\n 开始下载 @" + System.currentTimeMillis() + "\n" + url);
            mUIHandler.sendMessage(startMsg);
        }

        SystemClock.sleep(4000);    //模拟下载

        if (mUIHandler != null) {
            //下载完成,通知主线程
            Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, "\n 下载完成 @" + System.currentTimeMillis() + "\n" + url);
            mUIHandler.sendMessage(finishMsg);
        }

        return true;
    }
}

 

MainActivity.class
public class MainActivity extends RxAppCompatActivity implements View.OnClickListener, Handler.Callback {
    TextView mTvStartMsg;
    TextView mTvFinishMsg;
    Button mBtnStartDownload;

    private Handler mUIHandler;
    private DownLoadThread mDownloadThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvStartMsg = (TextView) findViewById(R.id.tv_start_msg);
        mTvFinishMsg = (TextView) findViewById(R.id.tv_finish_msg);
        mBtnStartDownload = (Button) findViewById(R.id.btn_start_download);

        mBtnStartDownload.setOnClickListener(this);

        mUIHandler = new Handler(this);

        mDownloadThread = new DownLoadThread("下载线程");
        mDownloadThread.setUIHandler(mUIHandler);
        mDownloadThread.setDownloadUrlList("http://pan.baidu.com/s/11111",
                "http://bbs.005.tv/22222", "http://list.youku.com/show/33333", "http://list.youku.com/show/33333"
                , "http://list.youku.com/show/33333"
                , "http://list.youku.com/show/33333"
        );


    }

    @Override
    protected void onDestroy() {
        mDownloadThread.quitSafely();
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_start_msg:

                break;
            case R.id.tv_finish_msg:
                break;
            case R.id.btn_start_download:
                mDownloadThread.start();
                mBtnStartDownload.setText("正在下载");
                mBtnStartDownload.setEnabled(false);

                break;
        }

    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DownLoadThread.TYPE_FINISHED:
                mTvFinishMsg.setText(mTvFinishMsg.getText().toString() + "\n " + msg.obj);
                break;
            case DownLoadThread.TYPE_START:
                mTvStartMsg.setText(mTvStartMsg.getText().toString() + "\n " + msg.obj);
                break;
        }

        return true;
    }
}

xml:

<?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:orientation="vertical"
              android:paddingBottom="@dimen/activity_vertical_margin"
              android:paddingLeft="@dimen/activity_horizontal_margin"
              android:paddingRight="@dimen/activity_horizontal_margin"
              android:paddingTop="@dimen/activity_vertical_margin"
              tools:context="lib.com.myapplication.MainActivity">

    <TextView
        android:id="@+id/tv_start_msg"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:text="下载开始信息"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/colorAccent"/>

    <TextView
        android:id="@+id/tv_finish_msg"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginTop="8dp"
        android:text="下载完成信息"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/colorAccent"/>

    <Button
        android:id="@+id/btn_start_download"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="开始下载"/>
</LinearLayout>

其他常规用法:

mThread = new HandlerThread("handler_thread"); 
mThread.start();
mWorkHandler = new Handler(mThread.getLooper());
mUIHandler = new Handler();

源码:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

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

    //也可以指定线程的优先级,注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级!
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    // 子类需要重写的方法,在这里做一些执行前的初始化工作
    protected void onLooperPrepared() {
    }

    //获取当前线程的 Looper
    //如果线程不是正常运行的就返回 null
    //如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

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

    //调用 start() 后就会执行的 run()
    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();            //帮我们创建了 Looepr
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();    //Looper 已经创建,唤醒阻塞在获取 Looper 的线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();    
        Looper.loop();        //开始循环
        mTid = -1;
    }

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

    public int getThreadId() {
        return mTid;
    }
}

 è¿éåå¾çæè¿°

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值