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

news2024/10/6 2:21:51

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1090044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医生访问学者出国进修必备面试技巧

医生访问学者出国进修&#xff0c;一直以来都是医学领域内追求更高学术水平和国际化视野的重要途径之一。然而&#xff0c;要成功进入国外院校或研究机构进行进修&#xff0c;首先需要通过面试&#xff0c;因此&#xff0c;面试技巧显得尤为关键。本文知识人网小编将为您介绍一…

LeetCode 739 每日温度(单调栈的初步了解)

1、重新学习了栈的操作&#xff0c;isEmpty()、peek()以及pop()、push()操作 但是值得注意的点是push()必须要有输入 2、单调栈用在这里非常巧妙&#xff0c;通过暴力搜索的方法无法通过最后一个用例 并且通过使用单调栈可以使得时间复杂度从O()降到了O() 3、Deque<Inte…

竞赛 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测(多指标,多图)

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09;…

关于信息安全软考的记录6

1、入侵检测相关概念 及 入侵检测模型 入侵&#xff1a;违背访问目标的安全策略的行为 判断入侵的依据是&#xff1a;对目标的操作是否超出了目标的安全策略范围 入侵检测&#xff1a;通过收集操作系统、系统程序、应用程序、网络包等信息&#xff0c;发现系统中违背安全策略…

【unity】【VR】白马VR课堂系列-VR开发核心基础04-主体设置-XR Rig的引入和设置

接下来我们开始引入并构建XR Rig。 你可以将XR Rig理解为玩家在VR世界中的替身。 我们先删除Main Camera&#xff0c;在Hierarchy右键点击删除。 然后再在场景层右键选择XR下的XR Origin。这时一个XR Origin对象就被添加到了Hierarchy。 重设XR Origin的Position和Rotation…

C++中将十六进制数转化为字符串数据

C中将十六进制数转化为字符串数据 1、十六进制转字符串2、string转char[]3、调用4、调试结果 1、十六进制转字符串 std::string Number2HexStr( uint32_t mData ) {std::stringstream ss;ss << std::hex << std::setw(2) << std::setfill(0) << (int)…

快速排序 O(nlgn)

大家好&#xff0c;我是蓝胖子&#xff0c;我一直相信编程是一门实践性的技术&#xff0c;其中算法也不例外&#xff0c;初学者可能往往对它可望而不可及&#xff0c;觉得很难&#xff0c;学了又忘&#xff0c;忘其实是由于没有真正搞懂算法的应用场景&#xff0c;所以我准备出…

el-dialog两个弹框里面套弹框受外层弹框影响

el-dialog嵌套的影响及解决方法 解决方法如下&#xff1a; 在里层弹框里添加 append-to-body <el-dialogtitle"图片预览":visible.sync"dialogVisible"class"imgDialog":modal"false"append-to-body><img width"100%&q…

分享一个查询OpenAI Chatgpt key余额查询的工具网站

OpenAI Key 余额查询工具 欢迎使用 OpenAI Key 余额查询工具网站&#xff01;这个工具可以帮助您轻松地验证您的 OpenAI API 密钥&#xff0c;并查看您的余额。 http://tools.lbbit.top/check_key/ 什么是 OpenAI Key 余额查询工具&#xff1f; OpenAI Key 余额查询工具是一…

要想成为黑客,离不开这些资料

目录 一、想入门学黑客&#xff0c;去哪里找详细的教程&#xff1f; 二、适合新人入门的书籍 三、相关网站推荐 四、在线靶场 五、Web安全学习路线 六、Web安全入门基础学习 小白在学习黑客的过程中一般会遇到这样一些问题&#xff1a;感觉自己工具、原理都会了但是遇到真…

ue5打包失败与优化项目

打包报错&#xff1a; PackagingResults: Error: Multiple game targets found for project. Specify the desired target using the -Target... argument. 解决方案&#xff1a; 关闭项目后&#xff0c;删除项目目录下的 Intermediate 文件 再重新启动项目打包即可 参考&…

小学英语教学计划模板范文 英语优秀教案模板

小学英语课教学计划模板&#xff1a; 课程时长&#xff1a;40分钟/节 课程目标&#xff1a;本课程的目标是让学生达到一定的英语水平&#xff0c;包括词汇、语法、听说读写能力等。 授课内容&#xff1a; 主题 1&#xff1a;Unit 1 Greetings 内容&#xff1a;学习如何用英…

[Python小项目] 从桌面壁纸到AI绘画

从桌面壁纸到AI绘画 一、前言 1.1 确认问题 由于生活和工作需要&#xff0c;小编要长时间的使用电脑&#xff0c;小编又懒&#xff0c;一个主题用半年的那种&#xff0c;所以桌面壁纸也是处于常年不更换的状态。即时改变主题也是在微软自带的壁纸中选择&#xff0c;而这些自…

机器学习-有监督学习-神经网络

目录 线性模型分类与回归感知机模型激活函数维度诅咒过拟合和欠拟合正则数据增强数值稳定性神经网络大家族CNNRNNGNN&#xff08;图神经网络&#xff09;GAN 线性模型 向量版本 y ⟨ w , x ⟩ b y \langle w, x \rangle b y⟨w,x⟩b 分类与回归 懂得两者区别激活函数&a…

项目成本超支的主要原因以及解决方法

成本超支&#xff0c;是每个项目经理在其职业生涯中都会遇到的一个问题。当项目的实际成本超过估计或预算成本时&#xff0c;就会发生成本超支。这在建筑、制造和软件开发项目中尤其常见&#xff0c;并影响着项目的盈利能力、利益相关者满意度和竞争优势。 成本超支的原因 由…

LINUX定时解压缩方案

需求背景 对接客户中某个上游为外包系统&#xff0c;外包系统每日推送压缩文件至指定文件夹下&#xff0c;文件格式为YYYYMMDD_RegReport.zip。由于每日采集文件&#xff0c;无法对接压缩包内文件&#xff0c;需要将推送的压缩文件每日解压为文件夹 需求分析 与客户沟通后&a…

苹果电脑其他内存怎么清理?

苹果电脑中的应用程序大部分是可以通过将其拖拽至废纸篓并倾倒来卸载的。但是部分程序在卸载后仍有残留文件&#xff0c;比如support文件和pref设置等文件的。小编今天介绍下苹果电脑清理内存怎么清理卸载残留以及好用的清理技巧分享。 一、苹果电脑清理内存怎么清理 苹果电脑…

分享38个AI绘画网站

本文是参考AI沉思录「1000AI」栏目的第十二期&#xff0c;「1000AI」栏目专注研究有哪些AI产品&#xff0c;目标研究1000AI产品(进度:532/1000)。 AI沉思录 ​aichensilu.com/ 1、Midjourney 网址&#xff1a;https://www.midjourney.com/ 基于diffusion的AI艺术生成器。生成…

vue3 vue.config.js分包配置

主要用到的是 filename 和 chunkFilename 两个方法 方法一&#xff1a;configureWebpack.output配置 代码&#xff1a; module.exports { configureWebpack: {devtool: source-map,output: {filename: js/dong/[name].[chunkhash:8].js,chunkFilename: js/xxxd/[name].[chu…