目录
1.什么是Handler、Looper、MessageQueue
2.Handler、Looper和MessageQueue的基本原理
5.对于Handler-Looper-MessageQueue-线程关系的Android源码分析
1.什么是Handler、Looper、MessageQueue
Handler:就是将消息放入队列的机制。我们在哪个线程中创新handler,handler就将消息放入所在的线程,除非在创建handler对象时是指定具体的线程。通常handler在Main Theread创建。
Looper:相当于消息的载体,它的内部有一个消息队列,也就是MessageQueue,Handler发送的所有消息都会走向这个消息队里。
MessageQueue:就是一个消息队列
2.Handler、Looper和MessageQueue的基本原理
一个简单例子阐述Handler、Looper和MessageQueue之间的关系:
实验步骤:
1.当用户点下按钮后,就会创建一个消息对象,并且给这个消息对象的what属性赋值为2
2.然后调用handler的sendMessage方法,把msg对象发送到消息队列(MessageQueue)去
3 当消息队列有数据之后,Looper就会从消息队列中将消息对象取出
4.Looper将会找到与消息对象对应的Handler对象(生成消息对象msg的handler(handler 是Handler的引用类型))
5.Looper将会调用Handler对象的handlerMessage(Message,msg)方法,用于处理消息对象
新建一个应用程序,命名为:S17_Handler1
在布局文件增加一个 按钮和一个TextView控件;
声明引用,获取控件的标签用对象表示,实现监听器接口,为button绑定监听器:
实现监听器接口,并在onClick方法,编写按钮按下时需要做的工作,然后继承Handler,生成Handler子类的对象
两个小提示:
1.继承Handler时,需要导入包可以用快捷键,注意导入正确的包
2.handlerMessage方法是复写Handler的:
MainActivity.java源码:
package com.yuan.s17_handler1;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
//声明引用
private TextView textView;
private Button button;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取控件的标签用对象表示
button =(Button)findViewById(R.id.buttonId);
textView =(TextView)findViewById(R.id.textViewId);
handler = new FirstHandler();
//生成监听器对象,为button绑定监听器
OnButtonListener listener =new OnButtonListener();
button.setOnClickListener(listener);
}
//当用户点下按钮后,就会创建一个消息对象,并且给这个消息对象的what属性赋值为2
//然后调用handler的sendMessage方法,把msg对象发送到消息队列去
//当消息队列有东西之后,Looper就会从消息队列中将消息对象取出
//Looper将会找到与消息对象对应的Handler对象
//Looper将会调用Handler对象的handlerMessage(Message,msg)方法,用于处理消息对象
class OnButtonListener implements OnClickListener{
@Override
public void onClick(View v) {
//当用户点击按钮时,创建一个消息对象,并使用Handler发送该对象
//创建消息对象
Message msg = handler.obtainMessage();//每个消息对象,都有与之对应的Handler对象
//调用消息对象的属性 what,赋值为2
msg.what =2;
//调用Handler方法,把消息对象放入到消息队列当中去
handler.sendMessage(msg);
}
}
//也就是调用这个对象的的HandlerMessage方法
class FirstHandler extends Handler{
@Override
public void handleMessage(Message msg) {
int what = msg.what;
textView.setText("what:"+what);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
试着运行这个应用程序:
可能看过上面的例子会有点疑惑,为什么通过Handler机制把数据存入消息队列,然后通过在Looper把数据取出来,然后又把送回去给Handler对象去解决呢,这么麻烦,Handler自己去解决了不就行了吗?
答案就在于线程线程间通讯里,其实我们要解决的正是线程通信里面的一些问题,看完下面这些可能就会解惑了:
3.通过Handler实现线程通信:(子线程向主线程传输)
例子:
实现目标:点击发送消息按钮后启动一个线程,让这个线程休眠2s钟口修改TextView里面的内容,抽象例子子:
提示:我们上一节说到,在Worker Thread是不能去修改 UI的属性的,详细点击:点我查看
这次我们通过Handler通信机制去解决这个问题。
新建一个应用程序,命名为:S17_Handler2
布局文件代码和视图如下:
绑定按钮的监听器:
然后新建一个名为MyThread的线程,利用按钮去启动线程,详细看上一节: 点我查看
和上面一样,在主线程新建一个Handler,去接受处理与之对象的消息队列的信息,同样去打印线程的名字:
总体代码如下:
package com.yuan.s17_handler2;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
//声明引用
private TextView textView;
private Button button;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取代表控件的对象
textView =(TextView)findViewById(R.id.textViewId2);
button =(Button)findViewById(R.id.buttonId2);
//生成一个handler对象
handler = new MyHandler();
//生成监听器对象,绑定监听器
button.setOnClickListener(new OnbuttonListener());
}
class MyHandler extends Handler{
@Override
public void handleMessage(Message msg) {
//打印当前线程的名字
System.out.println("handlerMessage-->"+ Thread.currentThread().getName());
}
}
class OnbuttonListener implements OnClickListener{
@Override
public void onClick(View v) {
Thread ft =new NetWorkThread();
ft.start();
}
}
//模拟一个网络线程
class NetWorkThread extends Thread{
@Override
public void run() {
//打印当前线程的名字
System.out.println("netWork-->"+ Thread.currentThread().getName());
//模拟访问网络,线程运行时,休眠2s
try {
Thread.sleep(2*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//变量s的值,模拟从网络当中获取的数据
//应为这里不是UI线程,所以不能去修改UI的属性,除非特殊控件
String s = "从网络获取的数据";
//生成Message对象
Message msg = handler.obtainMessage();
//把消息发送到消息队列里面,Looper就会自动把消息取出来
//然后用相对应的Handler对象的handleMessage去处理Message对象。
handler.sendMessage(msg);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
运行这个应用程序,在没有运行之前先预测一下运行结果:应该是,进去UI界面——》按发送按钮——》就会启动一个NetWork的线程——》(在线程里先打印出该线程的名字——》该线程休眠2s,模拟获取网络数据——》再生成一个Message对象——》通过handler把对象放在消息队列里面——》)队列里有消息——》Looper就会把消息取出来——》把消息交给相应Handler对象的handleMessage()方法处理——》此时我们并没有去处理msg,我们只在handleMessage函数打印该线程的名字。
可以看到,和我们预想的一样,先打印NetWork线程名字(为Worker Thread),然后去执行handlerMessage的语句,打印出线程名字,发现其实是主线程(Main Thread):
CatLog的输出结果:
所以得出一个结论:通过sendMessage()方法,无论是在主线程(Mian Thread)还是(Worker Thread)都是可以的被handleMessage(msg)收到,既然能收到,我们就可以通过这种机制去处理(work Thread)进程的数据了,也就意味着,不管你Worker Thread是否堵塞,只要不堵塞主线程,UI界面就能正常显示,一旦Work Thread处理好数据,就可以通过sendMessage()方法把数据发送到消息队列,Looper就可以去取出数据给HandlerMessage(msg)处理,又因为handleMessage(msg)是主线程的所以他可以去修改UI的属性(比如TextView等控件)。
上面我们已经解决可可以把(Work Thread)的数据发送到(Main Thread),那么就可以把可以把刚刚的 s (Sting)字符串,通过 sendMessage()这种机制把它传输出来,然后再主线程接受,在主线程调用TextView.setText()方法就可以在UI上反馈(Work Thread)线程返回的结果了。
修改NetworkThread类如下:
修改MyHandler类如下:
运行这个应用程序:
在第一个例子里,留了点疑惑,然后再这里解答,为什么不能直接通过Handler去解决,而是要通过间接解决,到这里就明白了,Handler也是在主线程,如果放在主线程执行,同样会阻塞主线程,而且最重要的一点是,问题不是Handler解决的,问题仍然是(Worker Thread)解决的,Handler只是在Main Thread 和 Worker Thread之间架了一个通讯桥梁而已。
用一个图,做一个抽象的比喻:
4.通过Handler实现线程通信:(主线程向子线程传输)
- 如果需要主线程向子线程发送消息的话,就需要在子线程中初始化Looper,然其工作起来,上面已经了解Looper的作用,并在主线程中的Handler需要去引用子线程的Looper,在Handler,引用哪个Looper就是去处理哪个子线程的消息。
步骤:
1.新建一个WorkThread,然后在进程中准备Looper对象
2.在Worker Thread当中生成Handler对象
3.在Mian Thread中发送消息
例子:
新建一个应用程序,命名为:S17_Handler3
修改布局文件:
修改MainActivity.java程序
1.新建一个WorkThread,然后在进程中准备Looper对象
2.在Worker Thread当中生成Handler对象
3.在Mian Thread中发送消息
整体程序:注意下面跟更正了两条语句
System.out.println("onClick---->"+Thread.currentThread().getName());
System.out.println("handleMessage---->"+Thread.currentThread().getName());
package com.yuan.s17_handler3;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取代表控件的对象
button =(Button)findViewById(R.id.buttonId3);
//创建线程对象,启动MyThread线程
Thread ft = new MyThread();
ft.start();
//生成监听器对象,绑定监听器,使用匿名内部类方法
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
System.out.println("onClick---->"+Thread.currentThread().getName());
//得到一个Message对象
Message msg = handler.obtainMessage();
//给msg的what属性赋值
msg.what=10;
//这个Message对象就会发送消息队列里
handler.sendMessage(msg);
}
});
}
//子线程
class MyThread extends Thread{
@Override
public void run() {
//准备Looper对象
Looper.prepare();
//在Work Thread生成一个Handler对象
//使用匿名内部类实现
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("handleMessage---->"+Thread.currentThread().getName());
System.out.println("收到了从MainThread传来的消息:"+msg.what);
}
};
//调用Looper的loop()方法之后,Looper对象不断从消息队列中取出消息对象
//然后调用handler的handleMessage()方法处理该消息对象
//如果消息队列中没有消息,则线程阻塞
Looper.loop();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
运行程序:
结果下图所示,第一句证明了,消息是行MainThread发送出去的,第二三句证明了消息是在WorkerThread收到的,并且收到了是我们发送的消息:
5.对于Handler-Looper-MessageQueue-线程关系的Android源码分析
Android源码分析:点我查看
注意:对于上面匿名内部类的说明
1.对于什么是匿名内部类:点我查看
2.对于上面的程序使用的解释:
附录:
本博文Demo下载:https://github.com/zGuangYuan/Android-
github的用法:点我查看