Android 主线程、子线程间的通信问题

在Android中,子线程是不可以操作UI界面的,必须在主线程中操作,同时在主线程中也不能使用网络通信,必须在子线程中使用(4.0版本后的新特性)。所以Android中关于线程间的通信便显得相当重要。

这里需要使用Looper和Handler,利用Message进行基于消息队列的线程通信,简单介绍一下几个对象

Looper:管理当前线程的消息队列。
Handler: 向目标线程发送消息Handler.sendMessage(Message msg),处理消息队列中的消息Handler.handleMessage(Message msg);
Message:线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。

找了一个关于这三者之间通信关系图:

基本原理是在主线程和子线程中各建立一个消息处理Handler以及消息队列Looper,然后通过sendMessage及handleMessage来进行消息的收发。发送载体为Message。
需要注意的一点是,在主线程中自带了Looper,所以不需要再建立Looper。
建立Handler及Looper的代码如下

在主线程中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected void onCreate(Bundle savedInstanceState) {

    mainHandler = new Handler() {
             
                @Override
                /**
                 * 主线程消息处理中心
                 */

                public void handleMessage(Message msg) {
                   
                    // 接收子线程的消息并处理...
       
                }
     
    };

}

在子线程中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void run() {
   
    //在子线程中创建Handler必须初始化Looper
        Looper.prepare();
 
        childHandler = new Handler() {
            /**
             * 子线程消息处理中心
             */

            public void handleMessage(Message msg) {
 
                // 接收主线程及其他线程的消息并处理...
                   
            }
        };
 
        //启动该线程的消息队列
        Looper.loop();
       
        //此处添加代码将不被执行,线程进入looper消息循环队列
       
    }

这里要注意一下,在子线程中一旦启动消息循环队列,则Looper.loop()之后的代码将不被执行,直到调用Handler.getLooper().quit()结束消息循环后才会执行。
我们通过在handleMessage中写入执行代码来供其他线程触发来达到线程间通信的目的,并可以通过Message内容的不同触发不同的事件。简单说一下Message的创建,一共有以下几种创建消息的函数

创建消息
public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

我们可以根据需求来使用
这里参考发送消息的一个例子

1
2
3
4
5
6
7
    void sendMessage(String str){
       
        //通知客户端线程 发送消息
        Message msg= ClientThread.childHandler.obtainMessage(0, str);
        ClientThread.childHandler.sendMessage(msg);
       
    }

这里的0是msg.what,str为msg.data, ClientThread。childHandler为静态对象,可以直接引用。
此时客户端线程接收到msg消息后,编码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
             public void handleMessage(Message msg) {
 
                // 接收主线程及其他线程的消息并处理...
                try {
                    outputStream.write(((String) (msg.obj)).getBytes());
                    outputStream.flush();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
               
               
            }

这里客户端线程的HandleMessage的作用是接受主线程的信息并发送给服务器,大家把注意力集中在这个收的的msg即可,msg.obj即是刚才发送的str,强转为String即可。当然我们可以根据收到的msg.what做出不同的反应,大家可以自己去尝试构建一个swich。

同理,子线程对客户端线程的操作也可用Handler进行操作
例如在子线程中对MainActivity的界面进行操作

1
2
3
4
5
6
MainActivity.mainHandler.post(new Runnable(){
    public void run(){
        MainActivity._connectBtn.setText("Close"); 
    }  
           
} );

对MainActivity的某按钮文字进行修改。

以上即为在Android中主线程与子线程互相通信的基本案例,更多的使用方法大家可以举一反三。
本篇到此,谢谢关注。

BeiTown
2013.04.14

本文链接:Android 主线程、子线程间的通信问题

转载声明:BeiTown原创,转载请注明来源:BeiTown's Coder 编码之源,谢谢


Tags: , , , , ,

2 Responses to Android 主线程、子线程间的通信问题

  1. 匿名 说道:

    Arduino, 就一个处理器, 你怎么做分时钟处理?
    这句: ” 这里要注意一下,在子线程中一旦启动消息循环队列,则Looper.loop()之后的代码将不被执行,直到调用Handler.getLooper().quit()结束消息循环后才会执行”
    本质上不还是同步执行的嘛! 最多就算个Code表示程序逻辑清晰.
    各人拙见, 仅做探讨. blueghost.chn@gmail.com

    • BeiTown 北呈-BeiTown 说道:

      。。。 我想说你看清楚题目再阐述见解 是Android 不是 Arduino单片机。。。虽然我写了不少Arduino的文章 但是这篇是说Android的线程问题,另外你连c++和java的代码都区分不出 我就真不想再多说什么了。。。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>