昨天看了mars老师的视频。学习了android线程间的通讯,初步熟悉了Handler, Looper, MessageQueue的用法,趁着还没忘写下来吧。
Handler、Looper、MessageQueue的作用:
Handler:它有两个作用——发送消息(sendMessage)和处理消息(handleMessage);程序使用Handler发送消息至消息队列中去,也可以处理由Looper发送过来的消息;创建Handler对象时必须重写handleMessage方法;
Looper:不断的从消息队列中取出消息对象,并调用Handler的handlerMessage方法处理消息;如果队列中没有消息对象,则其处于阻塞等待状态;
Message:Handler处理和发送的消息对象;
MessageQueue:消息队列,采用先进先出的模式(FIFO)来管理Message;
线面通过两种方式来展现线程之间的通讯,第一种是从子线程向UI程发送消息;第二种是从UI线程向主线程发送消息。
一、子线程向UI线程发送消息,代码如下:
package cui.handler_test02;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/**
* 实现线程之间的通讯
* 从子线程向UI线程发送消息"我来自另一个线程",UI线程接受到消息后打印在TextView中
* 布局文件为一个TextView和Button,很简单就不呈现了
* @author pramb
*
*/
public class MainActivity extends Activity {
TextView textView;
Button button;
//创建一个Handler的全局变量
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
textView = (TextView) findViewById(R.id.textView);
handler = new MyHandler();
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//按下按钮后启动子线程
new Thread(new MyThread()).start();
}
});
}
class MyHandler extends Handler{
//必须要重写handlerMessage方法,处理Looper传来的消息,因为怎么处理消息对象时你自己的事
@Override
public void handleMessage(Message msg) {
//收到由子线程传来的消息后先打印出当前操作的线程名字,然后打印出
//传来的消息,并且更新到textView中
System.out.println(Thread.currentThread().getName());
System.out.println((String)msg.obj);
textView.setText((String)msg.obj);
}
}
//创建线程类
class MyThread implements Runnable{
@Override
public void run() {
//获取一个消息对象,至于为什么这样获取,API上说这样获取更好,
//可以自己查看一下obtainMessage()方法的介绍
Message msg = handler.obtainMessage();
//所发送的消息
String s = "我来自另一个线程";
//复制给Message的属性
msg.obj = s;
//向消息队列中发送消息对象
handler.sendMessage(msg);
//打印当前操作线程的名字
System.out.println(Thread.currentThread().getName());
}
}
@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;
}
@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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
二、由UI线程向子线程发送消息,和第一个有点不同:
package cui.handler_test03;
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.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* 实现UI线程与子线程之间的通讯,要注意与上一个实现方式不同
* 主要功能是按下按钮后先打印当前操作的线程的名字,并发送消息对象到子线程,子线程拿到
* 消息对象后,打印当前操作的线程的名字
* @author yonglin
*
*/
public class MainActivity extends Activity {
Button button;
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
new Thread(new MyThread()).start();
//点击按钮后,打印当前操作的线程,然后向子线程发送消息
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println(Thread.currentThread().getName());
Message msg = handler.obtainMessage();
handler.sendMessage(msg);
}
});
}
class MyThread implements Runnable{
@Override
public void run() {
//创建Looper对象
Looper.prepare();
//重写handleMessage方法
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//接受由UI线程发送过来的消息,并打印当前操作的线程名字
System.out.println(Thread.currentThread().getName());
}
};
//不断的从消息队列中取出消息对象,并调用handleMessage(Message msg)处理
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;
}
@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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
上图为打印结果
两种方式的程序结构有很大的不同,仔细看一下,至于为什么不同,可以参考一下源代码,我也不大清楚;还有一点很重要:原则上不能在子线程中操作UI控件,否则会报错!其实线程间通讯我感觉就是:A线程想向B线程发送消息的话,A先把消息传入消息队列,Looper把消息从消息队列中取出,再传给B,应该就是这个意思~~~第一次写博客,也不大会写,技术还又菜,多多包涵哈!