线程与消息机制
2013-12-15 00:00
239 查看
摘要: 一、线程
二、Handler
三、通过Handler实现线程间的通信
原则上来讲,worder thread里不允许操作UI。只有主线程main thread可以。
我们新建一个项目,在MainActivity里设置一个Button,点击它时启动一个线程。
public class MainActivity extends Activity {
private TextView textView;
private Button button;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new listener());
}
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
Thread t = new thread();
t.start();
}
}
//worker thread
class thread extends Thread{
public void run(){
try {
Thread.sleep(1*1000);//阻塞当前线程指定的时间
} catch(InterruptedException e) {
e.printStackTrace();
}
textView.setText("来自于线程的修改");
}
}
}
上面是已经写好的代码,我们在监听器里实例化了一个自定义的Thread,调用了它的start方法。
我们在thread类的run方法里修改了textView的文本,即操作了UI。
这个项目在运行后,点击按钮,出现了如下错误:
这就是因为在worder thread里操作了UI。但是有一些特殊的控件是允许的,比如progressBar。
但如果把所有事情放在主线程里做,非常容易出现AR错误,就是“应用程序没有响应”。
比如,我们把监听器里的代码改成
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
try {
Thread.sleep(10*1000);//让主线程休眠十秒
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
我们点击按钮,它接近于死机状态,过个几秒,会出现如下画面:
这是因为主线程阻塞了。
结论:
main thread与worder thread各有所职。
主线程主要用于接收用户的输入,以及将运算结果反馈给用户。主线程不允许阻塞,不要进行耗时较长的操作,如访问网络,大量读写等。这些操作必须放在worder thread中。
main thread负责UI,worder thread负责运算。
提问:
那么,worder thread中返回的信息如何影响UI呢?如何与main thread通信呢?这需要用到handler。
看下图:
MessageQueue是一种数据结构,符合先进先出的原则,类似一个隧道。
MessageQueue可以存放多个消息对象。
Handler负责将消息对象加入到MessageQueue。
Looper是一个循环器,不停的从MessageQueue中取出消息对象。
如果MessageQueue中没有消息对象,Looper会处于等待状态。
Looper将消息对象从MessageQueue中取出,再交给Handler处理。
新建一个项目,mainActivity里加一个button,设置监听器。
接下来,我们定义一个handler,它需要继承Handler。
class handler extends Handler{
}
注意:
在引入包的时候,要选择android.os.Handler。
然后,我们要复写一个方法,是handleMessage方法。
菜单栏Source->Override/Implement Methods
然后我们声明一handler对象
private Handler handler;
在oncreate里给它赋值
handler = new handler();
当用户点击按钮时,我们创建一个消息对象,并使用handler发送该对象。监听器里代码如下:
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
Message msg = handler.obtainMessage();//获取一个消息对象
msg.what = 2;//给message的一个属性赋值
handler.sendMessage(msg);//用handler发送这个对象到message queue中去
}
}
一旦message queue里有消息对象了,looper立刻将消息对象取出,找到与消息对象对应的handler,然后调用handler对象的handlerMessage方法,处理消息对象。
这时,我们回到自定义的handler的handlerMessage方法,填充代码:
class handler extends Handler{
@Override
public voidhandleMessage(Message msg) {
int what = msg.what;
System.out.println(what);
}
}
还是新建一个项目,放一个按钮和文本,点击按钮后启动一个线程,让这个线程休眠两秒之后把文本的内容修改掉。这个例子的实际意义在于,把休眠替换成访问服务器就是开发中经常用到的例子。
准备好按钮和文本控件,我们来定义一个线程,在run函数里模拟了一次访问服务器和取值:
//worker thread
class thread extendsThread{
@Override
public void run() {
try {
Thread.sleep(2*1000);//模拟访问网络,休眠两秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "从网络中获取的数据";//模拟服务器的返回值
}
}
然后在button的监听器里启动这个线程。
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
Thread t = new thread();
t.start();
}
}
重点来了,刚才我们定义的Thread是Worker Thread,它不能直接操作textView。我们需要Handler。
先把它定义出来,先放着:
class handler extends Handler{
@Override
public voidhandleMessage(Message msg) {
}
}
我们回到线程里去生成消息对象,把S赋给消息对象,再发送消息:
//worker thread
class thread extendsThread{
@Override
public void run() {
//System.out.println("worker thread:"+Thread.currentThread().getName());//打印当前线程的名字
try {
Thread.sleep(2*1000);//模拟访问网络,休眠两秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "从网络中获取的数据";//模拟服务器的返回值
Message msg = handler.obtainMessage();
msg.obj = s;
handler.sendMessage(msg);
}
}
发送消息完成,接下来就是处理消息,我们回到自定义的handler,接收S,并写入textview的文本:
class handler extends Handler{
@Override
public voidhandleMessage(Message msg) {
//System.out.println("handleMessage:"+Thread.currentThread().getName());//打印当前线程的名字
String s = (String) msg.obj;
textView.setText(s);
}
}
这样,我们实现了worker thread向main thread发送消息。访问网络都是这样操作的。
首先还是新建一个项目,准备一个Button。然后定义一个线程:
//worker thread
class thread extendsThread{
@Override
public void run() {
//准备Looper对象
Looper.prepare();
//在worker thread中定义一个handler(匿名内部类)
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
//让Looper开始循环,不断的从消息队列中取出消息对象,如果没有消息对象,则该线程阻塞。
Looper.loop();
}
}
这个线程有点复杂,首先是准备一个Looper对象,然后用匿名内部类的方式定义一个handler,为什么?因为handleMessage方法是定义在接收消息的一方。我们这个例子是要worker thread接收并处理消息。
先把handleMessage的内容空着,接下来,让Looper开始循环,它会不断的从消息队列中取出消息对象。
接下来,我们在主线程里让worker thread启动:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button =(Button) findViewById(R.id.button);
thread t = new thread();
t.start();
}
然后,我们让点击按钮时,发送消息:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button =(Button) findViewById(R.id.button);
button.setOnClickListener(newOnClickListener() {
@Override
public void onClick(View v) {
Message msg = handler.obtainMessage();
handler.sendMessage(msg);
}
});
thread t = new thread();
t.start();
}
最后完成handleMessage方法,打印一个值。
public voidhandleMessage(Message msg) {
System.out.println("收到了消息对象");
}
这样就实现了main thread向worker thread发送消息。
二、Handler
三、通过Handler实现线程间的通信
一、线程
1.JAVA中的线程
1.1线程的概念
线程是程序运行的基本执行单元。当操作系统在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程。1.2实现线程的方法
在Java中建立线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口,其实这两种方法从本质上说是一种方法,即都是通过Thread类来建立线程,并运行run方法的。1.3线程的生命周期
线程也同样要经历开始(等待)、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。2.MainThread与WorkerThread
MainThread是主线程,又叫UI线程,WorkerThread是除了主线程之外的其他线程。原则上来讲,worder thread里不允许操作UI。只有主线程main thread可以。
我们新建一个项目,在MainActivity里设置一个Button,点击它时启动一个线程。
public class MainActivity extends Activity {
private TextView textView;
private Button button;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new listener());
}
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
Thread t = new thread();
t.start();
}
}
//worker thread
class thread extends Thread{
public void run(){
try {
Thread.sleep(1*1000);//阻塞当前线程指定的时间
} catch(InterruptedException e) {
e.printStackTrace();
}
textView.setText("来自于线程的修改");
}
}
}
上面是已经写好的代码,我们在监听器里实例化了一个自定义的Thread,调用了它的start方法。
我们在thread类的run方法里修改了textView的文本,即操作了UI。
这个项目在运行后,点击按钮,出现了如下错误:
这就是因为在worder thread里操作了UI。但是有一些特殊的控件是允许的,比如progressBar。
但如果把所有事情放在主线程里做,非常容易出现AR错误,就是“应用程序没有响应”。
比如,我们把监听器里的代码改成
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
try {
Thread.sleep(10*1000);//让主线程休眠十秒
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
我们点击按钮,它接近于死机状态,过个几秒,会出现如下画面:
这是因为主线程阻塞了。
结论:
main thread与worder thread各有所职。
主线程主要用于接收用户的输入,以及将运算结果反馈给用户。主线程不允许阻塞,不要进行耗时较长的操作,如访问网络,大量读写等。这些操作必须放在worder thread中。
main thread负责UI,worder thread负责运算。
提问:
那么,worder thread中返回的信息如何影响UI呢?如何与main thread通信呢?这需要用到handler。
二、Handler
1.运行基本原理
Handler、Looper和MessageQueue三者构成了android的消息机制。看下图:
MessageQueue是一种数据结构,符合先进先出的原则,类似一个隧道。
MessageQueue可以存放多个消息对象。
Handler负责将消息对象加入到MessageQueue。
Looper是一个循环器,不停的从MessageQueue中取出消息对象。
如果MessageQueue中没有消息对象,Looper会处于等待状态。
Looper将消息对象从MessageQueue中取出,再交给Handler处理。
2.一个简单的例子
我们建立一个简单的handler例子:新建一个项目,mainActivity里加一个button,设置监听器。
接下来,我们定义一个handler,它需要继承Handler。
class handler extends Handler{
}
注意:
在引入包的时候,要选择android.os.Handler。
然后,我们要复写一个方法,是handleMessage方法。
菜单栏Source->Override/Implement Methods
然后我们声明一handler对象
private Handler handler;
在oncreate里给它赋值
handler = new handler();
当用户点击按钮时,我们创建一个消息对象,并使用handler发送该对象。监听器里代码如下:
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
Message msg = handler.obtainMessage();//获取一个消息对象
msg.what = 2;//给message的一个属性赋值
handler.sendMessage(msg);//用handler发送这个对象到message queue中去
}
}
一旦message queue里有消息对象了,looper立刻将消息对象取出,找到与消息对象对应的handler,然后调用handler对象的handlerMessage方法,处理消息对象。
这时,我们回到自定义的handler的handlerMessage方法,填充代码:
class handler extends Handler{
@Override
public voidhandleMessage(Message msg) {
int what = msg.what;
System.out.println(what);
}
}
三、通过Handler实现线程间的通信
1.worker thread向main thread发送消息
前面的例子是把handler放在主线程中运行。现在做另外一个例子,在主线程中实现Handler的handleMessage方法,在worker thread中通过handler发送消息。还是新建一个项目,放一个按钮和文本,点击按钮后启动一个线程,让这个线程休眠两秒之后把文本的内容修改掉。这个例子的实际意义在于,把休眠替换成访问服务器就是开发中经常用到的例子。
准备好按钮和文本控件,我们来定义一个线程,在run函数里模拟了一次访问服务器和取值:
//worker thread
class thread extendsThread{
@Override
public void run() {
try {
Thread.sleep(2*1000);//模拟访问网络,休眠两秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "从网络中获取的数据";//模拟服务器的返回值
}
}
然后在button的监听器里启动这个线程。
class listener implementsOnClickListener{
@Override
public void onClick(View v) {
Thread t = new thread();
t.start();
}
}
重点来了,刚才我们定义的Thread是Worker Thread,它不能直接操作textView。我们需要Handler。
先把它定义出来,先放着:
class handler extends Handler{
@Override
public voidhandleMessage(Message msg) {
}
}
我们回到线程里去生成消息对象,把S赋给消息对象,再发送消息:
//worker thread
class thread extendsThread{
@Override
public void run() {
//System.out.println("worker thread:"+Thread.currentThread().getName());//打印当前线程的名字
try {
Thread.sleep(2*1000);//模拟访问网络,休眠两秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "从网络中获取的数据";//模拟服务器的返回值
Message msg = handler.obtainMessage();
msg.obj = s;
handler.sendMessage(msg);
}
}
发送消息完成,接下来就是处理消息,我们回到自定义的handler,接收S,并写入textview的文本:
class handler extends Handler{
@Override
public voidhandleMessage(Message msg) {
//System.out.println("handleMessage:"+Thread.currentThread().getName());//打印当前线程的名字
String s = (String) msg.obj;
textView.setText(s);
}
}
这样,我们实现了worker thread向main thread发送消息。访问网络都是这样操作的。
2. main thread向worker thread发送消息
与前一个例子不同的是,我们需要在worker thread中准备一个looper对象,并把handler定义在worker thread中。首先还是新建一个项目,准备一个Button。然后定义一个线程:
//worker thread
class thread extendsThread{
@Override
public void run() {
//准备Looper对象
Looper.prepare();
//在worker thread中定义一个handler(匿名内部类)
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
//让Looper开始循环,不断的从消息队列中取出消息对象,如果没有消息对象,则该线程阻塞。
Looper.loop();
}
}
这个线程有点复杂,首先是准备一个Looper对象,然后用匿名内部类的方式定义一个handler,为什么?因为handleMessage方法是定义在接收消息的一方。我们这个例子是要worker thread接收并处理消息。
先把handleMessage的内容空着,接下来,让Looper开始循环,它会不断的从消息队列中取出消息对象。
接下来,我们在主线程里让worker thread启动:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button =(Button) findViewById(R.id.button);
thread t = new thread();
t.start();
}
然后,我们让点击按钮时,发送消息:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button =(Button) findViewById(R.id.button);
button.setOnClickListener(newOnClickListener() {
@Override
public void onClick(View v) {
Message msg = handler.obtainMessage();
handler.sendMessage(msg);
}
});
thread t = new thread();
t.start();
}
最后完成handleMessage方法,打印一个值。
public voidhandleMessage(Message msg) {
System.out.println("收到了消息对象");
}
这样就实现了main thread向worker thread发送消息。
相关文章推荐
- android 进程/线程管理(四)续----消息机制的思考(自定义消息机制)
- Android线程之异步消息处理机制(一)
- Android 线程消息机制简介
- Android消息机制之实现两个不同线程之间相互传递数据相互调用
- 消息处理机制(线程)
- Android消息机制(AsyncTask操控进度条(颜色)线程的用法)
- Android消息队列及线程机制详解
- Android:在子线程中更新UI,解析异步消息处理机制(Handler)
- android 进程/线程管理(一)----消息机制的框架
- Android线程间消息机制-Handler源码分析(FrameWork)
- Android异步通信机制简单地理解,Handler是当前线程的消息队列中的一个子队列,而Runable是可以被安排到Handler去运行的接口。 首先在Activity中创建一个继承自Han
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android消息机制 & Android线程间通信机制
- 线程通信机制:共享内存 VS 消息传递
- Android消息机制中Handler切换线程的思考
- 在子线程中更新UI+延时更新(异步消息处理机制)
- [Andriod]计时器实现-基于线程消息机制三种方式
- Android消息队列及线程机制详解
- Android的Message机制(跨线程操作消息)
- 图解 Android Handler 线程消息机制