写在前面的总结:
Handler其实可以作为一个宏观的消息传递机制来看,平时我们在主线程中实例化一个handler,并重写handleMessage方法,在子线程中完成耗时操作,并拿着主线程中初始化的handler来发送消息给主线程,告诉主线程:你可以更新UI啦。这就是一个完整的消息传递流程。这个流程其实也就是最常用的子线程拿着主线程的handler发送消息给主线程,这个handler就像是一个信使。
主线程handler,handleMessage() <-- 子线程拿着主线程handler.sendMessage()就完成啦。
明白了这个原理,其实反过来就可以完成主线程发送消息给子线程了。其实也就是主线程拿着子线程中初始化的handler发送消息给子线程,handler充当这个传话的中间人。流程如下:子线程中初始handler,子线程重写handleMessage方法,当主线程想要发送消息给子线程了就拿着子线程中初始化好的handler对象给子线程发消息就好了。
子线程handler,handleMessage() <-- 主线程拿着子线程的handler.sendMessage()就完成啦。
只是子线程中初始化的这个handler需要我们自己获取looper罢了。当然也有Android为我们实现好的现成的HandlerThread这个类供我们使用。
下面的具体用法是从网上找的其他人的博客,摘抄出来的例子。
一、Handler的两种使用场景,
其实就是子线程完成任务后发送消息给主线程,让主线程来进行界面的更新:
(1)异步更新UI -- sendMessage和post两种方式
(2)延时任务
(1)异步更新UI操作的两种使用sendMessage或者post
private TextView tv_up;
private String new_str = "";
/*post方法解决UI更新问题handler创建方式*/
private Handler handler_post = new Handler();
/*sendMessage方法解决UI更新问题handler创建方式*/
Handler handler_senM = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 1) {
/*sendMessage方法更新UI的操作必须在handler的handleMessage回调中完成*/
tv_up.setText(new_str);
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
new_str = "更新UI";
/*sendMessage方法解决UI更新发送消息给handler(主线程中的handler)*/
handler_senM.sendEmptyMessage(1);
/*post方法解决UI更新,直接在runnable里面完成更新操作,这个任务会被添加到handler所在线程的消息队列中,即主线程的消息队列中*/
handler_post.post(new Runnable() {
@Override
public void run() {
tv_up.setText(new_str);
}
});
}
}).start();
}
(2)延时执行
@Override
public void onClick(View v) {
DownloadThread downloadThread = new DownloadThread();
downloadThread.start();
}
class DownloadThread extends Thread{
@Override
public void run() {
try{
System.out.println("DownloadThread id " + Thread.currentThread().getId());
System.out.println("开始下载文件");
//此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程
Thread.sleep(5000);
System.out.println("文件下载完成");
//文件下载完成后更新UI
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Runnable thread id " + Thread.currentThread().getId());
MainActivity.this.statusTextView.setText("文件下载完成");
}
};
uiHandler.post(runnable,1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
二、主线程向子线程发送消息,
在子线程中使用handler
主线程向子线程发送消息的话,我们需要在子线程里初始化Looper,并在主线程里创建的Handler引用子线程的Looper(Handler中引用的是哪个线程的Looper,就在哪个线程里处理消息)。
在子线程中接收消息,我们要用到Looper,主线程不用,因为主线程已经默认使用Looper了,我们会使用looper的两个主要方法,一个是prepare和loop,前一个是创建Looper对象,后一个是执行Looper循环功能。
public class ThreadHandlerActivity extends Activity{
//创建子线程
class MyThread extends Thread{
private Looper looper;//取出该子线程的Looper
public void run() {
Looper.prepare();//创建该子线程的Looper
looper = Looper.myLooper();//取出该子线程的Looper
Looper.loop();//只要调用了该方法才能不断循环取出消息
}
}
private Handler mHandler;//将mHandler指定轮询的Looper
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
thread = new MyThread();
thread.start();//千万别忘记开启这个线程
//下面是主线程发送消息
mHandler = new Handler(thread.looper){
public void handleMessage(android.os.Message msg) {
Log.d("当前子线程是----->",Thread.currentThread()+"");
};
};
mHandler.sendEmptyMessage(1);//主线程的handler发送消息给子线程
}
}
其实这样就可以达到主线程向子线程发送消息了,然而当我们运行后发现程序会Crash掉,报了一个控制针,这是因为在Handler初始化的时候,thread.looper还没有初始化,所以会报控制针,这时我们可以让主线程等待一下子线程,也可以来一个while循环来判断thread.looper是否初始化完成。不过Android本身还提供了一个方法,那就是HandlerThread。
HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper.
主线程向子线程发送消息的例子:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText("Handler实验");
setContentView(tv);
//实例化一个特殊的线程HandlerThread,必须给其指定一个名字
HandlerThread thread = new HandlerThread("handler thread");
thread.start();//千万不要忘记开启这个线程
//将mHandler与thread相关联
mHandler = new Handler(thread.getLooper()){
public void handleMessage(android.os.Message msg) {
Log.d("当前子线程是----->", Thread.currentThread()+""); //在这里进行耗时操作
};
};
mHandler.sendEmptyMessage(1);//拿着主线程的handler发送消息,给主线程处理
}
HandlerThread主要还是使用在子线程向主线程发送消息,具体使用如下:
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tvMain;
private HandlerThread mHandlerThread;
//子线程中的handler
private Handler mThreadHandler;
//UI线程中的handler
private Handler mMainHandler = new Handler();
//以防退出界面后Handler还在执行
private boolean isUpdateInfo;
//用以表示该handler的常熟
private static final int MSG_UPDATE_INFO = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvMain = (TextView) findViewById(R.id.tv_main);
initThread();
}
private void initThread()
{
mHandlerThread = new HandlerThread("check-message-coming");
mHandlerThread.start();
mThreadHandler = new Handler(mHandlerThread.getLooper())
{
@Override
public void handleMessage(Message msg) //进行耗时操作
{
update();//模拟数据更新,进行耗时操作
if (isUpdateInfo)
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
};
}
private void update()
{
try
{
//模拟耗时
Thread.sleep(2000);
mMainHandler.post(new Runnable() //子线程完成耗时操作后,在主线程中更新UI
{
@Override
public void run()
{
String result = "每隔2秒更新一下数据:";
result += Math.random();
tvMain.setText(result);
}
});
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
@Override
protected void onResume()
{
super.onResume();
//开始查询
isUpdateInfo = true;
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause()
{
super.onPause();
//停止查询
//以防退出界面后Handler还在执行
isUpdateInfo = false;
mThreadHandler.removeMessages(MSG_UPDATE_INFO);
}
@Override
protected void onDestroy()
{
super.onDestroy();
//释放资源
mHandlerThread.quit();
}
}
HandlerThread的特点
- HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
- 开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。 - HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
- 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化;
总结
HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。
三、子线程之间相互通信
其实子线程向子线程之间通信,其实就是在一个子线程中创建一个Handler,它的回调自然就在此子线程中,然后在另一个子线程中调用此handler来发送消息就可以了,不过记得写上Looper哦,下面看代码:
new Thread(new Runnable() {
@Override
public void run() {
String msg;
Looper.prepare();
childHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("这个消息是从-->>" + msg.obj+ "过来的,在" + "btn的子线程当中" + "中执行的");
}
};
Looper.loop();//开始轮循
}
}).start();
然后我们创建第二个子线程:
new Thread(new Runnable() {
@Override
public void run() {
Looper loop = Looper.myLooper();
Message msg = childHandler.obtainMessage();
msg.obj = "btn2当中子线程";
childHandler.sendMessage(msg);
}
}).start();
下面这个是上面的更加完整的代码:
public class CounterActivity extends AppCompatActivity {
public Button pressBtn;
public TextView textView;
//在这里定义一个属于主界面的线程类,这个线程类必须包含一个Handler成员
//而且这个Handler不能是私有的
//而且重写了handleMessage方法
public class mThread extends Thread
{
public Handler cHandler;
@Override
public void run()
{
//初始化Looper
Looper.prepare();
cHanler=new Handler()
{
@Override
public void handleMessage(msg)
{
//对msg进行处理,获得主线程传来的数据
//对数据进行处理,进行耗时操作
}
};
Looper.loop();
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_counter);
//实例化一个上面定义的线程类,并启动它
final mThread mthread=new mThread();
mthread.start();
pressBtn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//当按钮被单击的时候,需要向已经启动的线程mthread
//传递数据,这里就要使用Handler
//将数据封装到Message对象中
Message msg=new Message();
//调用子线程的Handler,将msg发送给子线程
mthread.cHandler.sendMessage(msg); //主线程拿到子线程的handler发消息,就发送给了子线程的handleMessage方法执行
}
});
}
}
上面的例子中,在实际开发中遇见过一次crash,就是执行mthread.cHandler.sendMessage(msg);时概率性地报空指针异常,因为原来代码中的cHandler的初始化写在了mthread.start();的后面,也就是当子线程运行时,有可能cHandler仍然没有初始化完成,因此,这个实例的初始化应该在启动线程执行之前进行。
对比学习下,在子线程向主线程发送消息的过程:
public class MainActivity extends AppCompatActivity {
//由上面的讲解,显然我们要在这个MainActivity中定义一个Handler
public Handler mHandler;
public Button pressBtn;
public TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_counter);
//在这里重写mHandler的handleMessage方法
mHandler=new Handler()
{
@Override
public void handleMessage(Message msg)
{
//对msg进行处理,获得其中的数据也就是计算结果
//修改用户界面
textView.setText("result");
}
};
pressBtn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
new Thread()
{
@Override
public void run()
{
//在这里进行一段复杂计算
//可以用Thread.sleep(1000);代替
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
//启动一个线程进行一个复杂计算
//当计算完成时,我们不能这样写:
//textView.setText("result");
//而是要通过主线程的Handler通知主线程
Message msg=new Message();
//应当对要传递的Message进行定制,
//将计算结果附在Message对象中
mHandler.sendMessage(msg); //子线程拿着主线程的handler发送消息,就传送给了主线程的handleMessage方法执行
}
}.start();
}
});
}
}
由上面的例子,我们就可以想到,在子线程中声明一个Handler对象,并且实现它的handleMessage(Message msg)方法;然后在主线程中调用子线程的Handler的sendMessage(Message msg)方法向子线程发送数据。就实现了主线程-->子线程的消息传递。
这里就应该把Handler作为一个宏观的消息传递机制来看而不仅仅是一个类了。因为它和Looper,MessageQueue,Message是作为一个整体完成消息传递任务的。