1. 消息机制
1.1 知识点
(1)掌握Message、Handler、Looper类的使用以及消息的传递;
(2)可以通过消息机制动态取得信息;
1.2 具体内容
对于android的消息机制,我们主要要使用Java中线程的一些知识:
线程:线程是进程一个细的划分,一个进程可以存在多个线程。Java中实现多线程的手段有两种:
·继承Thread类
·实现Runnable接口
在开发中,使用第二种方式实现多线程是优先选择的,主要继承Thread实现的多线程有一下两个问题:
·Java单继承的局限
·使用Runnable可以实现数据的共享
但是使用第二种方式实现的多线程,在线程启动的时候也必须使用Thread进行线程的启动。
主线程一般在android中成为UI线程,就是一个界面显示,那么这种就是主线程,而子线程就是利用那些实现了Runnable接口的线程的操作类。
对于Message和Handler类的操作,都会比较不太理解,现在完成一个更新的操作(子线程向主线程发送消息)。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
下面就是希望完成文本自动更新的操作,使用任务管理器完成(TimerTask)。
package com.example.messageproject;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView info = null;
private static int count = 0;
public static final int SET = 1;//定义消息的标记
private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case SET:
MainActivity.this.info.setText("Wanczy-" + count++);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
this.info = (TextView) super.findViewById(R.id.info);
Timer timer = new Timer();//定义调度器
timer.schedule(new MyTimerTask(), 0, 1000);//启动定时调度
}
/**
* 定义了一个子线程
* @author Administrator
*
*/
private class MyTimerTask extends TimerTask{
@Override
public void run() {
Message msg = new Message();//定义消息
msg.what = SET;//定义操作标记
MainActivity.this.myHandler.sendMessage(msg);//发送消息
}
}
}
对于这个程序而言,发现是在Handler中处理组件(TextView)的,为什么不在子线程中完成呢?
01-26 08:24:47.374: ERROR/AndroidRuntime(3533): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
这个错误指:子线程不能更新主线程中各个组件的状态。表示只要是子线程就无法去更新组件,那么现在只能采用之前的方法,在子线程中返回要操作的信息,而后主线程中利用Handler处理这些消息,从而实现线程的操作。
在正常的开发之中,不需要开发者去手动处理Looper,Activity类中会自动的启动好。
范例:Looper进行通讯操作:
package com.example.messageproject;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView info = null;
private Button but = null;
public static final int SET = 1;//定义消息的标记
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
this.info = (TextView) super.findViewById(R.id.info);
this.but = (Button) super.findViewById(R.id.but);
this.but.setOnClickListener(new OnClickListenerImpl());
}
private class OnClickListenerImpl implements OnClickListener{
@Override
public void onClick(View v) {
Looper looper = Looper.myLooper();//取得Looper对象
MyHandler handler = new MyHandler(looper);
handler.removeMessages(0);//清空队列中所有消息
String data = "厦门万策智业科技有限公司(Wanczy)";
Message msg = handler.obtainMessage(SET, data);
handler.sendMessage(msg);//发送消息
}
}
private class MyHandler extends Handler{
public MyHandler(Looper looper){//用来接收Looper
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case SET:
MainActivity.this.info.setText(msg.obj.toString());//取得消息的内容
break;
}
}
}
}
在程序中,去掉Looper之后,发现程序的运行也是一样的,说明Activity程序会自动启动Looper,很多时候不需要开发者去定义Looper。现在我们程序里面这3个关键类都有使用了,对于操作而言,以后就只需要用到Message 和Handler,下面我们来完成一个子线程与主线程的数据交互。
对于子线程,不能更新组件,所以接受到消息之后也只能进行后台的输出。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/but"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="交互"/>
</LinearLayout>
package com.example.messageproject;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
public static final int SETMAIN = 1; // 设置一个what标记
public static final int SETCHILD = 2; // 设置一个what标记
private Handler mainHandler, childHandler; // 定义Handler对象
private TextView msg; // 文本显示组件
private Button but;
class ChildThread implements Runnable { // 子线程类
@Override
public void run() {
Looper.prepare(); // 初始化Looper
MainActivity.this.childHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) { // 判断what操作
case SETCHILD: // 主线程发送给子线程的信息
System.out.println("*** Main Child Message : "
+ msg.obj); // 打印消息
Message toMain = MainActivity.this.mainHandler.obtainMessage(); // 创建Message
toMain.obj = "\n\n[B] 这是子线程发给主线程的信息:"; // 设置显示文字
toMain.what = SETMAIN; //设置主线程操作的状态码
MainActivity.this.mainHandler.sendMessage(toMain); break;
}
}
};
Looper.loop(); // 启动该线程的消息队列
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main); // 调用布局文件
this.msg = (TextView) super.findViewById(R.id.info); // 取得组件
this.but = (Button) super.findViewById(R.id.but); // 取得按钮
this.mainHandler = new Handler() { // 主线程的Handler对象
public void handleMessage(Message msg) { // 消息处理
switch (msg.what) { // 判断Message类型
case SETMAIN: // 设置主线程的操作类
MainActivity.this.msg.setText("主线程接收数据:"
+ msg.obj.toString()); // 设置文本内容
break;
}
}
};
new Thread(new ChildThread(), "Child Thread").start(); // 启动子线程
this.but.setOnClickListener(new OnClickListenerImpl()) ; // 单击事件操作
}
private class OnClickListenerImpl implements OnClickListener {
@Override
public void onClick(View view) {
if (MainActivity.this.childHandler != null) { // 已实例化子线程Handler
Message childMsg = MainActivity.this.childHandler
.obtainMessage(); // 创建一个消息
childMsg.obj = MainActivity.this.mainHandler.getLooper()
.getThread().getName()+ " --> Hello MLDN .";// 设置消息内容
childMsg.what = SETCHILD; // 操作码
MainActivity.this.childHandler.sendMessage(childMsg); // 向子线程发送
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
MainActivity.this.childHandler.getLooper().quit(); // 结束队列
}
}
范例:时钟显示
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<AnalogClock
android:id="@+id/myAnalogClock"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
package com.example.messageproject;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView info = null;
public static final int SET = 1;
private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case SET:
MainActivity.this.info.setText("当前时间为:"+msg.obj.toString());
break;
}
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
this.info = (TextView) super.findViewById(R.id.info);
new Thread(new ChildThread()).start();//启动子线程
}
private class ChildThread implements Runnable{
public void run(){
while(true){
Message msg = MainActivity.this.myHandler.obtainMessage();
msg.what = SET;
msg.obj = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
MainActivity.this.myHandler.sendMessage(msg);//发送消息
}
}
}
}
package com.example.messageproject;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView info = null;
public static final int SET = 1;
private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case SET:
MainActivity.this.info.setText("当前时间为:"+msg.obj.toString());
break;
}
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
this.info = (TextView) super.findViewById(R.id.info);
new Thread().start();//启动子线程
}
private class ChildThread implements Runnable{
public void run(){
while(true){
Message msg = MainActivity.this.myHandler.obtainMessage();
msg.what = SET;
msg.obj = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
MainActivity.this.myHandler.sendMessage(msg);//发送消息
}
}
}
}
1.3 小结
(1)在Android之中子线程不能直接对主线程的组件进行更新;