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

相关推荐
Kapaseker几秒前
千锤百炼写View 摸爬滚打名声就
android·kotlin
虫小宝12 分钟前
微信群发消息API接口对接中Java后端的请求参数校验与异常反馈优化技巧
android·java·开发语言
三少爷的鞋16 分钟前
架构避坑:为什么 UseCase 不该启动协程,也不该切线程?
android
Mr -老鬼29 分钟前
Android studio 最新Gradle 8.13版本“坑点”解析与避坑指南
android·ide·android studio
xiaolizi5674899 小时前
安卓远程安卓(通过frp与adb远程)完全免费
android·远程工作
阿杰100019 小时前
ADB(Android Debug Bridge)是 Android SDK 核心调试工具,通过电脑与 Android 设备(手机、平板、嵌入式设备等)建立通信,对设备进行控制、文件传输、命令等操作。
android·adb
梨落秋霜9 小时前
Python入门篇【文件处理】
android·java·python
遥不可及zzz12 小时前
Android 接入UMP
android
Coder_Boy_13 小时前
基于SpringAI的在线考试系统设计总案-知识点管理模块详细设计
android·java·javascript
冬奇Lab14 小时前
【Kotlin系列03】控制流与函数:从if表达式到Lambda的进化之路
android·kotlin·编程语言