下面接着上篇继续谈谈我对Handler机制的认识。
关系图见上一篇http://blog.csdn.net/u014294166/article/details/50677910
ActivityThread会创建所有的Activity并回调所有的Activity的方法,同时默认创建main线程,在创建main线程中会创建Looper,在创建Looper过程中又会默认创建Message。
源码跟踪分析如下:(先用word做的处理)
小结:Handler(sendMessage())负责发送消息,Looper(Looper.looper())负责接收Handler发送的消息,并直接把消息传给Handler自己(handMessage())。MessageQueue就是一个容器。
/** * 自定义与线程相关的Handler */
public class MainActivity extends AppCompatActivity {
private Handler mainHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("用来更新主线程中UI的handler---->" + Thread.currentThread());
}
};
class MyThread implements Runnable{
private Handler handler;// handler的自定义线程
// 以下注释处的过程已经通过以上源码分析说明
@Override
public void run() {
Looper.prepare();// 创建一个loop对象
handler = new Handler(){// 获取当前线程的Looper对象
@Override
public void handleMessage(Message msg) {
//Toast.makeText(MainActivity.this, "current thread:" + Thread.currentThread(), Toast.LENGTH_LONG).show();
System.out.println("自定义的用来更新UI的handler------>" + Thread.currentThread());
}
};
Looper.loop();// 死循环处理消息
}
}
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyThread myThreadt = new MyThread();
new Thread(myThreadt).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThreadt.handler.sendEmptyMessage(1);// handler自定义的线程
mainHandler.sendEmptyMessage(1);// 主线程的handler
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
}
耗时操作一般不直接在UI主线程上进行,否则容易出现卡死现象。
2 . HandlerThread
HandlerThread 继承于Thread,其本质就是Thread。与普通的Thread的差别在于,HandlerThread有Looper 成员变量,即主要的区别就在Looper的创建上,具体区别如下:
(1). 在普通Thread中实例化Handler时的一般步骤是:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop(),这样我们就在该线程当中创建好Looper。(注意:Looper.loop()方法默认是死循环)
(2). 在HandlerThread中创建一个HandlerThread即创建了一个Looper的线程,步骤如下:
*创建一个HandlerThread,即创建了一个包含Looper的线程。
HandlerThread handlerThread = new HandlerThread(“HandlerThread”);
handlerThread.start(); //创建HandlerThread后一定要记得start()
获取HandlerThread的Looper
Looper looper = handlerThread.getLooper();
创建Handler,通过Looper初始化
Handler handler = new Handler(looper);*
可以避免多线程并发时出现的空指针异常问题。比如说线程切换时Handler所需要Looper还没有创建。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();// !!
// Handler获取指定的Looper,无参时为默认个给定的Looper
handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
System.out.println("current thread----->" + Thread.currentThread());
}
};
handler.sendEmptyMessage(1);// 以上三步创建HandlerThread,然后通过handler发送消息
//handlerThread.quit();// 如果想让HandlerThread退出,则需要调用handlerThread.quit();
}
3 . 主线程向子线程发送消息
以上所谈的都是子线程向主线程发送消息,通知主线程更新UI,但是也有主线程向子线程发送消息的情况。下面谈谈这种情况。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button b_send;// 发送消息
private Button b_stop;// 停止发送消息
// 主线程的Handler
private Handler mainHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Message message = new Message();
System.out.println("=====》Main Handler");
// 在主线程中给子线程每秒发送一个message
subHandler.sendMessageDelayed(message, 1000);
}
};
private Handler subHandler;// 子线程的Handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b_send = (Button) findViewById(R.id.btn_send);
b_stop = (Button) findViewById(R.id.btn_stop);
b_send.setOnClickListener(this);
b_stop.setOnClickListener(this);
// 子线程的looper的轮询是通过创建HandlerThread指定的
HandlerThread hthread = new HandlerThread("Handler Thread");
hthread.start();
subHandler = new Handler(hthread.getLooper()){
@Override
public void handleMessage(Message msg) {
Message message = new Message();
System.out.println("=====》Sub Handler");
// 在子线程中给主线程每秒发送一个message
mainHandler.sendMessageDelayed(message, 1000);
}
};
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_send:
mainHandler.sendEmptyMessage(1);
break;
case R.id.btn_stop:
mainHandler.removeMessages(1);
break;
default:
break;
}
}
}
子线程中利用主线程的Handler向主线程发消息,主线程中利用子线程的Handler向子线程发消息。
public class MainActivity extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
//updateUI1();
//updateUI2();
//updateUI3();
updateUI4();
}
public void updateUI1(){
new MyThread().start();
}
class MyThread extends Thread{
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
tv.setText("使用runOnUiThread的方法更新UI");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
public void updateUI2(){
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
tv.setText("Handler的post方法更新UI");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
private Handler handler3 = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 3){
tv.setText("利用Handler的sendMessage方法更新UI");
}
}
};
public void updateUI3(){
new Thread(new MyThread3()).start();
}
class MyThread3 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(2000);
handler3.sendEmptyMessage(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void updateUI4(){
tv.post(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
tv.setText("View控件自身调用post方法更新UI");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
(1). 如下代码能正常更新UI
(2). 如下代码报如下异常:
FATAL EXCEPTION: Thread-186 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
不难看出,Android系统框架在对非UI线程直接操作UI信息进行判断的时候,准确度不够。在时间差内(代码片段中进行耗时操作,在本例中使用Thread.sleep模拟耗时操作)是可以在非UI线程直接更新UI信息的。
原因分析:
一步步查看setText()源码做分析:View中是通过如下代码检测当前线程是否是UI线程的,其中mThread即主线程。
View视图是通过ViewRootImpl类来绘制的,ViewRootImpl是在调用onResume()方法时创建的,而onResume()方法是在onCreate()之后调用的,在不休眠的情况下,ViewRootImpl还没有被创建出来,进而没来得及调用TextView里setText()方法后续的checkThread()方法。