一、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、处理耗时任务)
效果如下:
进入主界面后如下:
点击按钮后,星形进度条和文本按每秒增加10的速率,一直增加到100。
(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); } }