Android组件通信——消息机制(二十六)

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)。

java 复制代码
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)的,为什么不在子线程中完成呢?

java 复制代码
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进行通讯操作:

java 复制代码
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,下面我们来完成一个子线程与主线程的数据交互。

对于子线程,不能更新组件,所以接受到消息之后也只能进行后台的输出。

java 复制代码
<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>
java 复制代码
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(); 	// 结束队列
	}

}

范例:时钟显示

java 复制代码
<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>
java 复制代码
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之中子线程不能直接对主线程的组件进行更新;

相关推荐
烬奇小云3 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野15 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO15 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他15 小时前
Android ANR分析总结
android
PenguinLetsGo17 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.1 天前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农1 天前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android