当应用程序启动时,Android首先会开启一个主线程(也就是UI线程),主线程为管理界面中的UI控件。在程序开发时,对于比较耗时的操作,通常会为其开辟一个单独的线程来执行,以尽可能减少用户的等待时间。在Android中,默认情况下,所有的操作都是在主线程中进行的,主线程负责与UI相关的事件。而在自己新建的线程中,不能对UI进行操作。因此Android提供了消息处理传递机制来解决这一问题。
一、几个概念:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。Message是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。Message中包含了两个额外的 int字段和一个object字段,这样在大部分情况下,使用者就不需要再做内存分配工作了。虽然Message的构造函数是public的,但是最好是使用Message.obtain( )或Handler.obtainMessage( )函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提供效率。
MessageQueue:简单的说就是用来存放Message,但是Message是由Looper来分发的,Message不能直接添加到MessageQueue中,而是要通过与Looper关联的Handler去添加。其中的Message是由Looper来分发的,Message不能直接添加到MessageQueue中,而是要通过与Looper关联的Handler去添加。
Looper:用来循环读取存放于MessageQueue中的消息。一个线程对应一个Looper,一个Looper对象对应一个MessageQueue。Android中新增的线程是没有开启消息循环的,需要在线程中调用perpare函数,然后调用loop去处理消息。主线程除外。系统自动为主线程创建Looper对象。
Handler:实现消息的发送以及处理,负责发送用户消息以及调用用户注册的callback或接口进行消息处理。因此每个消息肯定有一个对应的handler,否则消息无法被发送/处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
二、消息机制的原理:
现Message机制需要Handler、Message、Looper三个之间的互相作用来实现;当线程A需要发消息给线程B的时候,线程B要用自己的Looper实例化Handler类,就是构造handler对象时,把当前线程的Looper传给Handler构造函数,handler本身会保存对Looper的引用,handler构造好以后,就可以用handler的obtainMessage方法实例化Message对象,只要把要传的数据给Handler,Handler就会构造Message对象,并且把Message对象添加到消息队列里面。然后就可以调用handler的sendMessage方法把Message对象发送出去,Looper就把消息放到消息队列中;最后当Looper知道消息队列不为空时候,就会循环的从消息队列中取消息,取出消息就会调用刚才实例化好的Handler对象的handleMessage方法取处理消息,整个Message过程就是这样。
三、第一个例子:
UI上两个button,点击不同button的时候显示不同的文本:
MainActivity.java
1 package com.zdx.messagetest; 2 3 import java.security.spec.MGF1ParameterSpec; 4 5 import android.app.Activity; 6 import android.os.Bundle; 7 import android.os.Handler; 8 import android.os.Looper; 9 import android.os.Message; 10 import android.util.Log; 11 import android.view.Menu; 12 import android.view.MenuItem; 13 import android.view.View; 14 import android.widget.Button; 15 import android.widget.TextView; 16 17 public class MainActivity extends Activity { 18 public boolean clickIF,threadBoolean=false; 19 private Button bt_success,bt_failed; 20 private TextView tv_textShow; 21 22 @Override 23 protected void onCreate(Bundle savedInstanceState) { 24 super.onCreate(savedInstanceState); 25 setContentView(R.layout.activity_main); 26 tv_textShow = (TextView) findViewById(R.id.tv_show); 27 } 28 public void showSuccess(View v){ 29 threadBoolean=false; 30 clickIF=true; 31 Log.i("zdx", "clickIF为true"); 32 LoThread loThread = new LoThread(); 33 Thread mThread = new Thread(loThread); 34 mThread.start(); 35 36 37 } 38 public void showFailed(View v){ 39 threadBoolean=false; 40 clickIF=false; 41 Log.i("zdx", "clickIF为false"); 42 LoThread loThread = new LoThread(); 43 Thread mThread = new Thread(loThread); 44 mThread.start(); 45 46 } 47 48 private Handler mHandler = new Handler(){ 49 50 @Override 51 public void handleMessage(Message msg) { 52 // TODO Auto-generated method stub 53 super.handleMessage(msg); 54 switch(msg.what){ 55 case 0: 56 tv_textShow.setText(String.valueOf(msg.obj)); 57 threadBoolean=true; 58 break; 59 case 1: 60 tv_textShow.setText(String.valueOf(msg.obj)); 61 threadBoolean=true; 62 break; 63 } 64 } 65 }; 66 private class LoThread implements Runnable{ 67 68 @Override 69 public void run() { 70 // TODO Auto-generated method stub 71 while(!threadBoolean){ 72 Looper.prepare(); 73 if(clickIF) 74 { 75 String successful = "成功"; 76 Message messages = mHandler.obtainMessage(0, successful); 77 messages.sendToTarget(); 78 Log.i("zdx", "成功"); 79 } 80 else 81 { 82 String failure = "失败"; 83 Message messages = mHandler.obtainMessage(1, failure); 84 messages.sendToTarget(); 85 Log.i("zdx", "失败"); 86 } 87 if (threadBoolean) { 88 Thread.currentThread().interrupt(); 89 } 90 Looper.loop(); 91 } 92 } 93 } 94 }
activity_main.xml
1 <RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context="com.zdx.messagetest.MainActivity" > 10 11 <LinearLayout 12 android:id="@+id/linearLayout1" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:orientation="horizontal" > 16 17 <Button 18 android:id="@+id/bt_success" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:layout_alignParentLeft="true" 22 android:layout_alignParentTop="true" 23 android:text="@string/s_successful" 24 android:onClick="showSuccess"/> 25 26 <Button 27 android:id="@+id/bt_failed" 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_centerHorizontal="true" 31 android:text="@string/s_failure" 32 android:onClick="showFailed"/> 33 </LinearLayout> 34 35 <TextView 36 android:id="@+id/tv_show" 37 android:layout_width="match_parent" 38 android:layout_height="60dp" 39 android:layout_alignParentLeft="true" 40 android:layout_below="@+id/linearLayout1" 41 android:layout_marginTop="17dp"/> 42 43 </RelativeLayout>