当应用程序启动时,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>