Android多线程与消息循环

2013-01-26 03:20常州信息职业技术学院解志君
电子世界 2013年19期
关键词:线程队列消息

常州信息职业技术学院 解志君

1.引言

Android的UI界面更新在多线程并发的环境下是不安全的,因此Android要求对UI界面的更新必须在UI线程(即主线程)中进行。如果在非UI线程中进行界面更新,系统会提示错误,这就是Android的单线程模型。但这是否意味着Android应用中只能有一个线程呢,答案是否定的。在Android中,除了UI线程外,还存在着工作线程,工作线程的作用是处理耗时的业务逻辑。这是因为耗时的业务逻辑不能放在UI线程中,因为那样很可能阻塞UI线程,从而延迟程序对用户操作的响应,如果用户的操作在20s内得不到响应,系统会提示用户ANR(Application Not Responsible)信息[1]。但是工作线程和UI线程又不是完全独立的,有时候又要根据工作线程处理的结果来更新UI界面,但是更新UI界面不能在工作线程中进行,那么,工作线程又是如何通知UI线程来更新UI界面的呢?这就涉及到Android的消息循环机制。

2.Android的消息循环机制

Android的消息循环机制是事件处理的一种形式,它主要是为了解决Android线程间的通信问题。因此,深入理解Android系统的消息循环机制对于从更深层次上把握Android应用程序的运行机制是大有裨益的。其实Google在设计Android系统的消息循环时是参考了Windows程序的消息循环机制的,即Android应用程序的消息处理机制也是由消息循环、消息发送和消息处理三部分构成的。消息循环是指发送给每个线程的消息都被按处理时间的先后顺序存入该线程的消息队列MessageQueue中,然后该线程的循环器Looper不断的从该队列中依次取出每条消息进行处理,若消息队列为空,则该线程处于空闲等待状态;消息发送是指把消息发送到需要处理该消息的线程的消息队列中,也就是需要与当前线程进行通信的线程的消息队列中,Android中一般是通过Handler的相关方法来进行消息发送的;消息处理是指消息被从消息队列中取出时,调用Handler的相关方法,一般是handleMessage()方法来对消息进行处理。理解Android的消息循环机制需要把握以下几点:

(1)每个线程都有一个唯一的消息循环器Looper,它扮演着MessageQueue和Handler之间桥梁的角色,它源源不断的依次从MessageQueue中取出消息,并将消息分发到指定的处理者Handler对象进行处理[2]。

(2)每个Looper都封装了一个消息队列MessageQueue,它是一个FIFO的队列,用来存储该Looper所关联的线程的消息。

(3)每个Handler在创建时都被绑定到一个Looper,它是消息的发送者和处理者,它把消息发送到与它绑定的Looper的消息队列中,同时,该消息的处理者就被设置为该Handler对象。当Looper从MessageQueue中循环到该条消息时,消息的处理者Handler对象的handleMessage()方法就会被自动调用,以处理该消息。

(4)Message是消息类,它封装了消息的相关内容,它的target属性指明了该消息的处理者。下图形象的说明了Handler的消息循环过程。

图1 Android消息循环机制示意图

3.Android UI线程与工作线程的消息循环

为了方便UI线程的消息事件处理,在Android应用程序的UI线程被创建时,系统会自动为UI线程创建一个消息循环器Looper,该类中封装了一个MessageQueue的成员变量。也就是说,主线程在创建后就自动具有了消息循环器和消息队列,开发人员只需将需要处理的消息发送至UI线程的消息队列中。而在自定义的工作线程中,系统是不会自动为工作线程创建Looper和MessageQueue的,必须由程序员自己创建工作线程的Looper和MessageQueue。使用Android提供的API,可以非常容易做到这一点。

要为工作线程建立消息循环,只需要四个步骤:

(1)生成工作线程的Looper,通过调用Looper.prepare()方法来实现。

(2)将Handler与工作线程的Looper的绑定,通过在工作线程中创建Handler对象来实现,因为Handler在默认情况下是与创建它的线程的Looper绑定的,否则就需要在创建Handler时指定其构造方法的Looper参数来显式指定该Handler与哪个线程的Looper绑定。

(3)定义消息处理方法,通过重写Handler的handleMessage()方法实现。

(4)启动消息循环,通过调用Looper.loop()方法实现。

(5)结束消息循环,通过调用Looper.quit()方法实现。

另外需要说明的是,Android为了开发人员的方便,也提供了一个带有消息循环的线程类HandlerThread,开发人员可以直接使用该类来创建工作线程,使用该类创建的线程会自动具有循环器Looper和消息队列MessageQueue。

4.使用消息循环实现在工作线程中通知UI线程更新UI界面

下面的例子每隔1秒时间将界面上文本的颜色更改为一种随机生成的颜色。这个功能使用Android消息循环是容易实现的。只需要在工作线程中每隔1秒向UI线程发送一条消息,UI线程处理这条,实现文本颜色的改变。下面来看一看实现这一功能的具体实现。

4.1 在工作线程中周期性的发送消息到UI线程的消息队列

要在工作线程中周期性的向UI线程的消息队列发送消息,需要在工作线程中使用一个循环,可以通过一个标志变量来控制该循环的开始与停止;然后使用UI线程中创建的Handler对象的相关方法生成消息并发送至UI线程的消息队列;接着让工作线程休息1秒钟。这样工作线程就以周期为1秒的间隔不断的向UI线程发送消息,直到工作线程停止。该工作线程的开启可在Activity的onCreate()方法中来完成。下面是工作线程的实现代码,代码中的注释对相关方法的功能进行了说明。

Thread t=new Thread(new Runnable(){//线程参数是一个匿名内部类

@Override

public void run(){

while(!Thread.currentThread().isInterrupted()){//线程循环

Message msg=handler.obtain Message();//生成消息

msg.what=0x110;//设置消息标识,即Message的what属性

handler.sendMessage(msg);//发送消息

try{

Thread.sleep(1000);//线程休眠1秒,实现周期性发送消息

}catch(InterruptedException e){

e.printStackTrace();

}

}

}

});

4.2 在UI线程中处理工作线程发送过来的消息

消息的处理是由Handler来完成的。要显示消息的处理,需要在UI线程中创建Handler时重写Handler的handlerMessage()方法,该方法定义了消息处理逻辑[3]。下面是在UI线程中创建Handler的代码,代码中注释解释了相关方法的功能。

//该Handler必须在UI线程中创建,以使之与UI线程关联。否则需要使用带Looper参数的

//造访方法来创建Handler

Handler handler=new Handler(){//内部类形式定义的Handler对象

@Override

public void handleMessage(Message msg){//重写消息处理方法

if(msg.what==0x110){//通过消息表示区分消息

int red=new Random().nextInt(255);

int green=new Random().nextInt(255);

int blue=new Random().nextInt(255);

int color=Color.rgb(red,green,blue);//生成随机颜色

txt.setTextColor(color);//设置文本颜色

}

}

};

5.结束语

Android的消息循环机制是Android事件处理的基石,所以从本质上说,Android程序的运行机制是基于消息循环的,也就是说,Android程序是消息驱动的。在Android框架的实现源码中,消息处理代码随处可见。当然,在我们实际的应用编程中,消息处理也应用得非常广泛,比如工作线程通知主线程更新UI、游戏中周期性更新UI、线程间通信等。总之,Android的消息循环在Android框架中占有非常重要的地位,深入理解Android的消息循环机制,对于提升程序员的Android程序编制水平是非常有帮助的。

[1]李刚.疯狂Android讲义[M].北京:电子工业出版社,2013.

[2]高洪岩.Android学习精要[M].北京:清华大学出版社,2012.

[3]王国辉,李伟等.Android开发宝典[M].北京:机械工业出版社,2012.

猜你喜欢
线程队列消息
基于C#线程实验探究
队列里的小秘密
基于多队列切换的SDN拥塞控制*
一张图看5G消息
基于国产化环境的线程池模型研究与实现
在队列里
丰田加速驶入自动驾驶队列
浅谈linux多线程协作
消息
消息