Android中的消息处理实例与分析

来源:互联网 发布:windows企业版怎么下载 编辑:程序博客网 时间:2024/06/05 18:13

目录(?)[+]

Android中的消息处理实例与分析

摘要

本文介绍了Android中的消息处理机制,给出了Android消息处理中的几个重点类Handler、Message、MessageQueue、Looper、Runnable、Thread的详细介绍,提供了两个消息处理的实例代码,并深入分析了使用Android消息机制应该遵循的几个原则。

阅读本文的收获

在具有java基础的情况下,Android的学习比较轻松,很多人在没有深刻了解Android消息处理机制的背景下,已经能够开发出可用的app,很多人开始想学习Android消息处理机制的第一个动机是遇到了一个著名的bug“CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.”。这个bug的含义即“只有创建一个view层次的线程能够更新此view”,在更多情况下,它是想说“只有主线程能够更新UI”。 
本文即是来解释如何利用Android的消息机制从主线程或者子线程中更新UI或完成其他操作。你可以学到: 
1. 如何使用Android的消息实现同步、异步操作; 
2. 如何在主线程和子线程发送并接收消息; 
3. 消息是如何得到处理的; 
4. 使用Android的消息处理机制应该遵循的几个原则; 
5. 两个具体的消息处理实例源代码。

阅读本文需要的技术背景

你可能需要如下技术背景才能完全理解本文的内容,如何你没有以下背景,建议先学习相关知识: 
1. Java语言基础 
2. Java多线程技术 
3. Android开发基本知识

第一个例子:在主线程和子线程中发送消息,在主线程中处理消息

先来看一个代码,代码地址为: 
http://download.csdn.net/detail/logicteamleader/8827099 
本例的作用是:创建一个Handler(处理消息的类),在界面上点击按钮就会向此Handler发送消息,Handler可以处理这些消息(后面会详细解释,消息的处理其实是多个类共同合作的结果),对UI进行修改。界面上有八个按钮,从上至下其效果分别是: 
1. 使用Handler的post来传递一个Runnable的实例,该实例的run方法会在按钮点击时运行; 
2. 使用Handler的postDelayed(Runnable r, long delayMillis)来传递一个Runnable的实例,该实例的run方法会在一段时间delayMillis后执行; 
3. 使用Handler的sendMessage方法传递一个消息,该消息在Handler的handleMessage方法中被解析执行; 
4. 使用Message的sendToTarget方法向获得该Message的Handler传递一个消息,该消息在Handler的handleMessage方法中被解析执行; 
第5、6、7、8个方法与上面四个方法类似,不同的是它们都是在子线程中调用的。 
源代码如下:

package com.example.wxbhandlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.os.MessageQueue;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;/** * @author wxb * Android中的消息处理实例之一 * * 一、在主线程内发送消息 * 1.使用post * 2.使用postDelay * 3.使用sendMessage * 4.使用Message.sentToTarget * 二、在子线程中使用Handler * 1.使用post * 2.使用postDelay * 3.使用sendMessage * 4.使用Message.sentToTarget */public class MainActivity extends Activity {    private Runnable runnable=null;    private Runnable runnableDelay=null;    private Runnable runnableInThread=null;    private Runnable runnableDelayInThread=null;    private static TextView tv;    private static TextView tvOnOtherThread;    //自定义Message类型    public final static int MESSAGE_WXB_1 = 1;    public final static int MESSAGE_WXB_2 = 2;    public final static int MESSAGE_WXB_3 = 3;    public final static int MESSAGE_WXB_4 = 4;    public final static int MESSAGE_WXB_5 = 5;    private static Handler mHandler=new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){            case MESSAGE_WXB_1:                tv.setText("invoke sendMessage in main thread");                break;            case MESSAGE_WXB_2:                tv.setText("Message.sendToTarget in main thread");                break;            case MESSAGE_WXB_3:                tvOnOtherThread.setText("invoke sendMessage in other thread");                break;            case MESSAGE_WXB_4:                tvOnOtherThread.setText("Message.sendToTarget in other thread");                break;              }            super.handleMessage(msg);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv = (TextView) this.findViewById(R.id.tvOnMainThread);        tvOnOtherThread = (TextView) this.findViewById(R.id.tvOnOtherThread);        //方法1.post        runnable = new Runnable(){            public void run(){                tv.setText(getString(R.string.postRunnable));            }        };        Button handler_post = (Button) this.findViewById(R.id.btnHandlerpost);        handler_post.setOnClickListener(new OnClickListener() {                 @Override            public void onClick(View v) {                mHandler.post(runnable);            }        });        //方法2:postDelay        runnableDelay = new Runnable(){            public void run(){                tv.setText(getString(R.string.postRunnableDelay));            }        };        Button handler_post_delay = (Button) this.findViewById(R.id.btnHandlerPostdelay);        handler_post_delay.setOnClickListener(new OnClickListener() {                   @Override            public void onClick(View v) {                mHandler.postDelayed(runnableDelay, 1000);  //1秒后执行            }        });        //方法3:sendMessage        Button btnSendMessage = (Button) this.findViewById(R.id.btnSendMessage);        btnSendMessage.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Message msg = mHandler.obtainMessage();                msg.what = MESSAGE_WXB_1;                mHandler.sendMessage(msg);            }        });        //方法4:Message.sendToTarget        Button btnSendtoTarget = (Button) this.findViewById(R.id.btnSendtoTarget);        btnSendtoTarget.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Message msg = mHandler.obtainMessage();                msg.what = MESSAGE_WXB_2;                msg.sendToTarget();            }        });       //在其他线程中发送消息        //1.post        runnableInThread = new Runnable(){            public void run(){                tvOnOtherThread.setText(getString(R.string.postRunnableInThread));            }        };        Button btnPost = (Button) this.findViewById(R.id.btnPost);        btnPost.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        mHandler.post(runnableInThread);                    }                }.start();            }        });        //2.postDelay        runnableDelayInThread = new Runnable(){            public void run(){                tvOnOtherThread.setText(getString(R.string.postRunnableDelayInThread));            }        };        Button btnPostDelay = (Button) this.findViewById(R.id.btnPostDelay);        btnPostDelay.setOnClickListener(new OnClickListener() {                         @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        mHandler.postDelayed(runnableDelayInThread, 1000);                    }                }.start();            }        });        //3.sendMessage        Button btnSendMessage2 = (Button) this.findViewById(R.id.btnSendMessage2);        btnSendMessage2.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        Message msg = mHandler.obtainMessage();                        msg.what = MESSAGE_WXB_3;                        mHandler.sendMessage(msg);                    }                }.start();            }        });      //方法4:Message.sendToTarget        Button btnSendToTarget2 = (Button) this.findViewById(R.id.btnSendToTarget2);        btnSendToTarget2.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        mHandler.obtainMessage(MESSAGE_WXB_4).sendToTarget();                    }                }.start();            }        });    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199

几个规则

看完代码后,在解释具体类之前,先了解几个规则: 
第一, 只有创建view的线程能够更新此view;一般来说,创建UI的是Android主线程,因此只有在主线程中才能更新UI; 
第二, 处理消息的类是Handler,它依附于创建自己的线程;如果在主线程中创建Handler mHandler,则向mHandler发送的消息会在主线程中被解析;如果在子线程中创建Handler sHandler,则向sHandler发送的消息会在子线程中被解析; 
第三, 发送消息有三类方法,Handler的post方法,Handler的sendMessage方法,以及Message的sentToTarget方法,它们最终其实都调用了Handler的sendMessage方法; 
第四, 消息的方法可以即时也可以延时,代表就是post和postDelay,以及sendMessage和sendMessageDelayed;延时发送消息可以实现很多用户需要的界面延时效果,例如最常用的SplashWindow。 
第五, post类型的方法只需要实例化一个Runnable接口,且不需要重载Handler. handleMessage方法,比较简单易用; 
第六, sendMessage和sentToTarget需要重载Handler. handleMessage方法,实现对不同消息的解析。

Handler

Handler是Android消息处理中最重要的一个类,不管什么编程语言或者框架,叫Handler的类一般都很重要,也都很复杂,当年的Windows句柄类更是让人一头雾水。 
幸好,Android的Handler很容易理解和使用。它就是一个消息处理的目标类,其主要作用有两个:1)它安排要执行的消息和runnable在未来某个时间点执行;2)处理来自不同线程的消息并执行操作。 
使用Handler要注意以下几点: 
1. Handler属于创建它的线程,并与线程的消息循环关联,同时与此线程的消息队列(MessageQueue)关联。 
2. Handler发送消息的方法有两类:post类和sendMessage类,用法见例子; 
3. 如果使用post发送消息,则消息中包含的Runnable会在解析消息时执行; 
4. 如果使用sendMessage发送消息,则需要重载Handler. handleMessage方法,实现对不同消息的解析

Message

Message是消息类,它有几个重要的属性: 
public int what:消息类型 
public int arg1:参数1 
public int arg2:参数2 
public Object obj:对象参数 
以上几个参数用户可以随意定制。 
值得注意的有几点: 
1. Android使用消息池模式来提高消息的效率,因此一般不适用new来创建消息,而是使用obtain方法来从消息池中获取一个Message的实例;Handler类也有obtain方法; 
2. Message有一个Handler作为Target,一般来说就是获取该消息的所在线程的关联Handler;因此使用sendToTarget方法发送消息时,将消息发给它的Target;

MessageQueue

MessageQueue是消息队列,一个线程最多拥有一个消息队列,这个类一般不会被程序员直接使用。它的入队方法enqueueMessage用来将一个Message加入队列,这个方法是线程安全的,因此才保证了Android的消息处理机制是线程安全的。 
MessageQueue的next方法用来获取下一个Message,没有任何消息时,主线程常常在此方法处等待。

Runnable

Java的接口,它代表一个可执行的代码片段,如下所示:

public interface Runnable {    /**     * Starts executing the active part of the class' code. This method is     * called when a thread is started that has been created with a class which     * implements {@code Runnable}.     */    public void run();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Thread

Java的线程类,大家都应该很熟悉了。

第二个例子:在子线程中创建消息处理循环

第一个例子描述了如何在多个线程中发送消息,并在主线程中统一接收和处理这些消息;第二个例子则描述如何在子线程中建立一个消息循环,并从主线程发送消息给子线程,让子线程处理这些消息。 
第二个例子中有两个消息循环,两个Handler,主线程首先向子线程发送一个消息,子线程的收到消息后再向主线程发送一个消息,主线程收到消息后更新UI。 
例子代码如下: 
http://download.csdn.net/detail/logicteamleader/8827401 
源代码如下:

package com.example.wxbloopinthread;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 static TextView tv = null;    //自定义Message类型    public final static int MESSAGE_WXB_1 = 1;    //主线程中创建Handler    private static Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){            case MESSAGE_WXB_1:                tv.setText("主线程发送,子线程接收消息后回发,主线程修改UI");                break;            }            super.handleMessage(msg);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);             setContentView(R.layout.activity_main);        tv = (TextView) this.findViewById(R.id.textView1);        //创建子线程        new LooperThread().start();        //点击按钮向子线程发送消息        Button btn = (Button) this.findViewById(R.id.btnSendMessage);        btn.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                LooperThread.sHandler.sendEmptyMessage(MESSAGE_WXB_1);            }        });    }    //定义子线程    static class LooperThread extends Thread {        public static Handler sHandler = null;        public void run() {            //创建消息循环            Looper.prepare();            sHandler = new Handler() {                public void handleMessage(Message msg) {                    switch(msg.what){                    case MESSAGE_WXB_1:                        mHandler.sendEmptyMessage(MESSAGE_WXB_1);                        break;                    }                }            };            //开启消息循环            Looper.loop();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

第二个例子使用了另一个重要的类Looper,它是代表Android中的消息循环处理类。

Looper

Looper被用来创建一个线程的消息循环。线程默认情况下是没有消息循环的,要创建消息循环,必须先调用Looper.prepare,然后在合适的地方调用Looper.loop,这个loop方法就开始循环处理本线程接收到的消息,直到loop循环被停止。 
在大部分情况下,Looper都是和Handler一起使用,它通过Handler接收和处理消息。 
使用Looper要注意以下几点: 
1. Android的主线程是默认已经创建了Looper对象的,因此不能在主线程中调用Looper.prepare; 
2. Looper.prepare和Looper.loop都是静态方法,调用时要注意,不要使用new来创建一个Looper;

总结

使用Android消息的方法有以下几种: 
1. 使用Handler和Runnable,即时或延时发送一个消息; 
2. 使用Handler和Message,即时或延时发送一个消息,需重载Handler. handleMessage方法; 
需要注意的规则有以下几条: 
1. 只有创建view的线程能够更新此view;一般来说,创建UI的是Android主线程,因此只有在主线程中才能更新UI; 
2. 处理消息的类是Handler,它依附于创建自己的线程;如果在主线程中创建Handler mHandler,则向mHandler发送的消息会在主线程中被解析;如果在子线程中创建Handler sHandler,则向sHandler发送的消息会在子线程中被解析; 
3. Looper被用来创建一个线程的消息循环,要创建消息循环,必须先调用Looper.prepare,然后在合适的地方调用Looper.loop。 
具体的体会,还是要多看代码才行。

转自http://blog.csdn.net/logicteamleader/article/details/46591499

阅读全文
0 0
原创粉丝点击