HandlerThread

本文围绕以下两个部分展开:

一、HandlerThread
案例一:通过HandlerThread进行线程间通信(非 UI进程更新 UI、处理耗时任务)





一、HandlerThread

        1. new Thread(){...}.start()启动线程 和 使用 Handler 启动线程的不好之处:

        (1)new Thread(){...}.start()启动线程:

                多次使用这种方式,会创建多个匿名线程。使得程序运行起来越来越慢。

        (2)使用 Handler 启动线程:

                使用一个Handler来启动一个线程,当该线程不再使用就删除,保证线程不会重复创建。

                但是,这样创建的handler是在主线程即UI线程下的Handler,即这个Handler是与UI线程下的默认Looper绑定的。因此,如果是默认创建Handler,那么如果线程是做一些耗时操作如网络获取数据等操作,这样创建Handler是不行的。

        2. 使用 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实际上就一个Thread,只不过它比普通的Thread多了一个Looper。

        (2)创建的Handler对象是与HandlerThread这个线程绑定了(这时就不再是与UI线程绑定了,这样它处理耗时操作将不会阻塞UI)。


案例一:通过HandlerThread进行线程间通信(非 UI进程更新 UI、处理耗时任务)

        效果如下:

        进入主界面后如下:

HandlerThread


        点击按钮后,星形进度条和文本按每秒增加10的速率,一直增加到100。

HandlerThread


HandlerThread


HandlerThread


HandlerThread


HandlerThread


        (1)strings.xml

<string name="btn_start">Start</string>


        (2)activity_main.xml。布局:一个开始按钮,一个星形进度条,一个文本进度。

<RelativeLayout 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: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=".MainActivity">

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="@string/btn_start" />

    <RatingBar
        android:id="@+id/ratingBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnStart"
        android:visibility="gone" />

    <TextView
        android:id="@+id/tvScale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/ratingBar"
        android:visibility="gone" />

</RelativeLayout>

<!-- 1. -->
<!-- RatingBar 和 TextView 是不可见的,
当点击 Start 按钮的时候,才设置为可见。 -->


        (3)MainActivity。通过HandlerThread进行线程间通信(非 UI进程更新 UI、处理耗时任务)

package com.android.handlerthread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.RatingBar;
import android.widget.TextView;


public class MainActivity extends Activity {
    // 3.1 要用到主界面中的 RatingBar 和 TextView,因此要先声明并初始化。
    // 声明两个控件(RatingBar 和 TextView)
    private RatingBar ratingBar;
    private TextView tvScale;
    // 5.1
    private MyHandlerThread handlerThread;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 3.2 初始化两个控件(RatingBar 和 TextView)
        ratingBar = (RatingBar) findViewById(R.id.ratingBar);
        tvScale = (TextView) findViewById(R.id.tvScale);

        // 5.2 实例化 HandlerThread 的子类:MyHandlerThread,
        //     并启动线程(调用start方法),线程就绪
        //     "HandlerThread"是名称,随便写字符串都可以。
        handlerThread = new MyHandlerThread("HandlerThread");
        handlerThread.start();

        // 6. 必须用到 handler 构造器:
        // 原因:需要把callback传入,
        // 从而使自己的 HandlerThread 的 handleMessage 来替换掉 Handle原生的 handleMessage

        // 参数一:If this thread has been started,
        // this method will block until the looper has been initialized.
        //        若线程已启动,则锁定此方法直到 Looper 初始化完成
        // 参数二:handlerThread是实现了 Handler 回调接口(Handler.Callback)的线程对象,
        //        从而 handler 可运行 handleMessage() 方法
        handler = new Handler(handlerThread.getLooper(), handlerThread);
    }

    /**
     * 2.
     *
     * @param view
     */
    public void onClick(View view) {
        // 7. 当点击Start按钮的时候,设置控件可见,并让 handler 发送空消息
        ratingBar.setVisibility(View.VISIBLE);
        tvScale.setVisibility(View.VISIBLE);
        handler.sendEmptyMessage(1);
    }

    /**
     * 4. 自定义 HandlerThread 的子类
     *
     */
    /**
     * Handler.Callback:回调接口
     * <p/>
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     * <p/>
     * 如果Callback不为空,则消息就在它实现的这个接口的 handleMessage()方法里处理。
     */
    private class MyHandlerThread extends HandlerThread
            implements Handler.Callback {
        /**
         * 4.1 必须初始化父类(HandlerThread)的构造方法(先构造出父类,才能产生子类)
         * <p/>
         * 因为 HandlerThread 父类,只带有一个含参的构造方法,没有无参构造方法,
         * 因此,要产生子类,必须先构造父类。
         * 构造父类需要含参构造方法,因此子类也必须实现含参的构造方法。
         */
        public MyHandlerThread(String name) {
            super(name);
        }

        /**
         * 4.2实现接口(Handler的回调接口)的方法
         *
         * @param msg
         * @return
         */
        @Override
        public boolean handleMessage(Message msg) {
            // 进度条总数为 100
            final int total = 100;
            // 初始进度为 0
            int current = 0;
            while (current <= total) {
                try {
                    // 可以在此处完成具体的任务
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 4.2.1 计算进度刻度
                // 之所以定义成 final,是因为下面的 内部类 new Runnable里面要用
                // (防止内部类里面对它进行修改)

                // 5颗 RatingBar的星星,要运行完 100进度,因此每次填充星星的速率是:0.05
                final float rating = (float) (current * 0.05);
                final int innerCurrent = current;
                // 让当前进度每次增加 10,那么 rating 每次就增加 0.5个星星
                current += 10;

                // 4.2.2 加入消息队列,运行 UI 线程更新界面
                ratingBar.post(new Runnable() {
                    // new Runnable中,run方法运行完,线程就销毁掉了
                    @Override
                    public void run() {
                        // 更新界面中的 ratingBar,更新进度条刻度
                        ratingBar.setRating(rating);
                    }
                });
                tvScale.post(new Runnable() {
                    @Override
                    public void run() {
                        // 更新进度文本 (当前进度 / 进度条总数)
                        tvScale.setText(String.format("%d / %d", innerCurrent, total));
                    }
                });
            }
            // True if no further handling is desired
            return true;
        }
    }

    // ---------------------------------------------------------------
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

你可能感兴趣的:(handler)