• 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类的操作,都会比较不太理解,现在完成一个更新的操作(子线程向主线程发送消息)。

    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:orientation="vertical">
    6. <TextView
    7. android:id="@+id/info"
    8. android:layout_width="wrap_content"
    9. android:layout_height="wrap_content"/>
    10. </LinearLayout>

    下面就是希望完成文本自动更新的操作,使用任务管理器完成(TimerTask)。

    1. package com.example.messageproject;
    2. import java.util.Timer;
    3. import java.util.TimerTask;
    4. import android.app.Activity;
    5. import android.os.Bundle;
    6. import android.os.Handler;
    7. import android.os.Message;
    8. import android.widget.TextView;
    9. public class MainActivity extends Activity {
    10. private TextView info = null;
    11. private static int count = 0;
    12. public static final int SET = 1;//定义消息的标记
    13. private Handler myHandler = new Handler(){
    14. @Override
    15. public void handleMessage(Message msg) {
    16. switch(msg.what){
    17. case SET:
    18. MainActivity.this.info.setText("Wanczy-" + count++);
    19. break;
    20. }
    21. }
    22. };
    23. @Override
    24. protected void onCreate(Bundle savedInstanceState) {
    25. super.onCreate(savedInstanceState);
    26. super.setContentView(R.layout.activity_main);
    27. this.info = (TextView) super.findViewById(R.id.info);
    28. Timer timer = new Timer();//定义调度器
    29. timer.schedule(new MyTimerTask(), 0, 1000);//启动定时调度
    30. }
    31. /**
    32. * 定义了一个子线程
    33. * @author Administrator
    34. *
    35. */
    36. private class MyTimerTask extends TimerTask{
    37. @Override
    38. public void run() {
    39. Message msg = new Message();//定义消息
    40. msg.what = SET;//定义操作标记
    41. MainActivity.this.myHandler.sendMessage(msg);//发送消息
    42. }
    43. }
    44. }

    对于这个程序而言,发现是在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进行通讯操作:

    1. package com.example.messageproject;
    2. import android.app.Activity;
    3. import android.os.Bundle;
    4. import android.os.Handler;
    5. import android.os.Looper;
    6. import android.os.Message;
    7. import android.view.View;
    8. import android.view.View.OnClickListener;
    9. import android.widget.Button;
    10. import android.widget.TextView;
    11. public class MainActivity extends Activity {
    12. private TextView info = null;
    13. private Button but = null;
    14. public static final int SET = 1;//定义消息的标记
    15. @Override
    16. protected void onCreate(Bundle savedInstanceState) {
    17. super.onCreate(savedInstanceState);
    18. super.setContentView(R.layout.activity_main);
    19. this.info = (TextView) super.findViewById(R.id.info);
    20. this.but = (Button) super.findViewById(R.id.but);
    21. this.but.setOnClickListener(new OnClickListenerImpl());
    22. }
    23. private class OnClickListenerImpl implements OnClickListener{
    24. @Override
    25. public void onClick(View v) {
    26. Looper looper = Looper.myLooper();//取得Looper对象
    27. MyHandler handler = new MyHandler(looper);
    28. handler.removeMessages(0);//清空队列中所有消息
    29. String data = "厦门万策智业科技有限公司(Wanczy)";
    30. Message msg = handler.obtainMessage(SET, data);
    31. handler.sendMessage(msg);//发送消息
    32. }
    33. }
    34. private class MyHandler extends Handler{
    35. public MyHandler(Looper looper){//用来接收Looper
    36. super(looper);
    37. }
    38. @Override
    39. public void handleMessage(Message msg) {
    40. switch(msg.what){
    41. case SET:
    42. MainActivity.this.info.setText(msg.obj.toString());//取得消息的内容
    43. break;
    44. }
    45. }
    46. }
    47. }

    在程序中,去掉Looper之后,发现程序的运行也是一样的,说明Activity程序会自动启动Looper,很多时候不需要开发者去定义Looper。现在我们程序里面这3个关键类都有使用了,对于操作而言,以后就只需要用到Message 和Handler,下面我们来完成一个子线程与主线程的数据交互。

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

    1. "http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:orientation="vertical">
    6. android:id="@+id/info"
    7. android:layout_width="match_parent"
    8. android:layout_height="wrap_content"/>
    9. android:id="@+id/but"
    10. android:layout_width="match_parent"
    11. android:layout_height="wrap_content"
    12. android:text="交互"/>
    1. package com.example.messageproject;
    2. import android.app.Activity;
    3. import android.os.Bundle;
    4. import android.os.Handler;
    5. import android.os.Looper;
    6. import android.os.Message;
    7. import android.view.View;
    8. import android.view.View.OnClickListener;
    9. import android.widget.Button;
    10. import android.widget.TextView;
    11. public class MainActivity extends Activity {
    12. public static final int SETMAIN = 1; // 设置一个what标记
    13. public static final int SETCHILD = 2; // 设置一个what标记
    14. private Handler mainHandler, childHandler; // 定义Handler对象
    15. private TextView msg; // 文本显示组件
    16. private Button but;
    17. class ChildThread implements Runnable { // 子线程类
    18. @Override
    19. public void run() {
    20. Looper.prepare(); // 初始化Looper
    21. MainActivity.this.childHandler = new Handler() {
    22. public void handleMessage(Message msg) {
    23. switch (msg.what) { // 判断what操作
    24. case SETCHILD: // 主线程发送给子线程的信息
    25. System.out.println("*** Main Child Message : "
    26. + msg.obj); // 打印消息
    27. Message toMain = MainActivity.this.mainHandler.obtainMessage(); // 创建Message
    28. toMain.obj = "\n\n[B] 这是子线程发给主线程的信息:"; // 设置显示文字
    29. toMain.what = SETMAIN; //设置主线程操作的状态码
    30. MainActivity.this.mainHandler.sendMessage(toMain); break;
    31. }
    32. }
    33. };
    34. Looper.loop(); // 启动该线程的消息队列
    35. }
    36. }
    37. @Override
    38. public void onCreate(Bundle savedInstanceState) {
    39. super.onCreate(savedInstanceState);
    40. super.setContentView(R.layout.activity_main); // 调用布局文件
    41. this.msg = (TextView) super.findViewById(R.id.info); // 取得组件
    42. this.but = (Button) super.findViewById(R.id.but); // 取得按钮
    43. this.mainHandler = new Handler() { // 主线程的Handler对象
    44. public void handleMessage(Message msg) { // 消息处理
    45. switch (msg.what) { // 判断Message类型
    46. case SETMAIN: // 设置主线程的操作类
    47. MainActivity.this.msg.setText("主线程接收数据:"
    48. + msg.obj.toString()); // 设置文本内容
    49. break;
    50. }
    51. }
    52. };
    53. new Thread(new ChildThread(), "Child Thread").start(); // 启动子线程
    54. this.but.setOnClickListener(new OnClickListenerImpl()) ; // 单击事件操作
    55. }
    56. private class OnClickListenerImpl implements OnClickListener {
    57. @Override
    58. public void onClick(View view) {
    59. if (MainActivity.this.childHandler != null) { // 已实例化子线程Handler
    60. Message childMsg = MainActivity.this.childHandler
    61. .obtainMessage(); // 创建一个消息
    62. childMsg.obj = MainActivity.this.mainHandler.getLooper()
    63. .getThread().getName()+ " --> Hello MLDN .";// 设置消息内容
    64. childMsg.what = SETCHILD; // 操作码
    65. MainActivity.this.childHandler.sendMessage(childMsg); // 向子线程发送
    66. }
    67. }
    68. }
    69. @Override
    70. protected void onDestroy() {
    71. super.onDestroy();
    72. MainActivity.this.childHandler.getLooper().quit(); // 结束队列
    73. }
    74. }

    范例:时钟显示

    1. "http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:orientation="vertical">
    6. android:id="@+id/myAnalogClock"
    7. android:layout_width="match_parent"
    8. android:layout_height="wrap_content"
    9. />
    10. android:id="@+id/info"
    11. android:layout_width="match_parent"
    12. android:layout_height="wrap_content"/>
    1. package com.example.messageproject;
    2. import java.text.SimpleDateFormat;
    3. import java.util.Date;
    4. import android.app.Activity;
    5. import android.os.Bundle;
    6. import android.os.Handler;
    7. import android.os.Message;
    8. import android.widget.TextView;
    9. public class MainActivity extends Activity {
    10. private TextView info = null;
    11. public static final int SET = 1;
    12. private Handler myHandler = new Handler(){
    13. @Override
    14. public void handleMessage(Message msg) {
    15. switch (msg.what){
    16. case SET:
    17. MainActivity.this.info.setText("当前时间为:"+msg.obj.toString());
    18. break;
    19. }
    20. }
    21. };
    22. protected void onCreate(Bundle savedInstanceState) {
    23. super.onCreate(savedInstanceState);
    24. super.setContentView(R.layout.activity_main);
    25. this.info = (TextView) super.findViewById(R.id.info);
    26. new Thread(new ChildThread()).start();//启动子线程
    27. }
    28. private class ChildThread implements Runnable{
    29. public void run(){
    30. while(true){
    31. Message msg = MainActivity.this.myHandler.obtainMessage();
    32. msg.what = SET;
    33. msg.obj = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    34. MainActivity.this.myHandler.sendMessage(msg);//发送消息
    35. }
    36. }
    37. }
    38. }
    39. package com.example.messageproject;
    40. import java.text.SimpleDateFormat;
    41. import java.util.Date;
    42. import android.app.Activity;
    43. import android.os.Bundle;
    44. import android.os.Handler;
    45. import android.os.Message;
    46. import android.widget.TextView;
    47. public class MainActivity extends Activity {
    48. private TextView info = null;
    49. public static final int SET = 1;
    50. private Handler myHandler = new Handler(){
    51. @Override
    52. public void handleMessage(Message msg) {
    53. switch (msg.what){
    54. case SET:
    55. MainActivity.this.info.setText("当前时间为:"+msg.obj.toString());
    56. break;
    57. }
    58. }
    59. };
    60. protected void onCreate(Bundle savedInstanceState) {
    61. super.onCreate(savedInstanceState);
    62. super.setContentView(R.layout.activity_main);
    63. this.info = (TextView) super.findViewById(R.id.info);
    64. new Thread().start();//启动子线程
    65. }
    66. private class ChildThread implements Runnable{
    67. public void run(){
    68. while(true){
    69. Message msg = MainActivity.this.myHandler.obtainMessage();
    70. msg.what = SET;
    71. msg.obj = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    72. MainActivity.this.myHandler.sendMessage(msg);//发送消息
    73. }
    74. }
    75. }
    76. }

    1.3 小结

    (1)在Android之中子线程不能直接对主线程的组件进行更新;

  • 相关阅读:
    OpenHarmony应用开发【01】
    金字塔的思维--思维的格式化与体系化
    [题]跳房子 #单调队列优化(伪)
    信息爆炸!78天闭门深造千页SpringCloud,再战京东
    每天五分钟机器学习:神经网络和支持向量机的基础——感知机模型
    FPGA 学习笔记:Vivado 工程管理技巧
    传感器数据采集:采样定理(奈奎斯特定理)
    前端vue中箭头函数省略return的写法之详细讲解
    LabVIEW使用视觉采集软件从GigE视觉相机进行采集 1
    [数据结构]链表OJ--环形链表判断是否有环(快慢指针法)
  • 原文地址:https://blog.csdn.net/weixin_41830242/article/details/133817988