王 涛
(江苏广播电视大学武进学院,江苏常州213149)
在移动互联网时代,移动终端的地位变得越来越重要,智能手机已成为人们日常生活中的必需品。基于移动设备操作系统的开发是现在程序开发的新方向。
根据Nielsen公司2013年6月的调查,智能机中53%为Android操作系统,领先于Apple IOS、BlackBerry以及Windows Phone等其他手机操作系统。作为程序开发人员,掌握Android平台下手机软件的开发技术势在必行。在Android开发中,掌握Android消息的处理方法,是从事Android开发的必备条件。
一个Android应用中,在不同时机都会有大量的消息生成和传递,如图1所示。①父Activity界面可以向子Activity界面传送结果,同样子Activity也可以向父Activity传递用户的选择。②一个Activity中可以嵌入多个Fragment片断,Fragment接收用户的操作,并将操作结果在另一个Fragment中显示时,同样存在着数据的传递。③UI主线程在需要执行耗时任务时,通常会启动子线程完成工作,因此子线程与UI主线程之间必然需要信息的互通。
下文,将根据产生消息组件的类型不同,来说明组件间消息传递的方法。
图1 Android应用中消息的传递
一个Android应用,通常会有多个界面组成,这些界面即为Activity。在一个界面中调用另一个界面,这两个界面即存在父子关系。父界面要向子界面传递结果信息,或者子界面要向父界面反馈用户选择结果,这时在两个Activity之间负责传递消息的中介为Intent对象,如图2所示。
图2 Activity消息传递
父Activity中通过startActivity(Intent)方法通知Android操作系统要调用子Activity,并在Intent对象中放入要传递的数据。子Activity通常在其on⁃Create()方法中,获取Intent对象,并提取传递来的数据。
根据Android参考文档(API level 19)中的解释,Intent类是将要执行动作的抽象描述,可以用其启动Activity,或与Service交互,或是向Broadcast⁃Receiver组件发送消息。
Intent最重要的用途是启动Activities,一个In⁃tent包含的重要信息有两块,即action和data。ac⁃tion是指动作的简要描述,如系统定义的“VIEW_ACTION”为查看;data用Uri的形式表示,描述要action要操作的数据,如“tel:123”,该Uri数据表示电话号码123。
Intent除了action和data这两个重要属性外,还有一个extras属性,可用于携带Bundle类型的额外数据,我们在Activity间传递的数据就是通过调用Intent对象的putExtras(Bundle)方法,放入这个属性中。下面的示例,将显示启动一个子Activity,并将当前的时间作为消息进行传送。
若子Activity需要反馈用户结果给父Activity,则需要按以下步骤进行:
1)父 Activity 以 StartActivityForResult(Intent,int requestCode)的方法启动子Activity。
2)子Activity在合适时机,通过setResult(int re⁃sultCode,Intent),设置结果状态,返回结果。
3)父 Activity覆写 protected void onActivityRe⁃sult(int requestCode,int resultCode,Intent data)方法,对子Activity返回结果进行处理。
父Activity可以启动多个子Activity,并要求这些子界面返回用户操作结果,不同子Activity以用户自定义的requestCode进行区分。子Activity使用setResult()设置结果,这会激活父界面使用onAc⁃tivityResult()进行事件处理,父界面在使用request⁃Code区分子Activity后,就可以用同样的方法处理Intent中的extras属性携带的数据。
Fragment是Android3.0新增加的特征,是可以被嵌入在Activity中,响应用户操作的UI组件。一个Activity界面中可以被嵌入一个或多个Frag⁃ment。如多标签页面,或者平板电脑中大用户界面的分块显示都需要用到Fragment。当在多个Frag⁃ment之间切换,并进行数据交换时,可以直接调用Fragment对象上setArguments(Bundle)方法设置数据,在目标Fragment中使用getArguments()来获取数据。
下面的例子假设Activity上有两个Fragment。当前显示的是Fragment1,当界面组件从Fragment1切换至Fragment2时,数据会需要从Fragment1传送到Fragment2。
Activity中要进行Fragment的各项操作,要先获取FragmentTransaction对象,并使用它的add(),replace()或remove()方法对界面组件进行添加,替换或删除操作,并在最后使用FragmentTransaction对象的commit()方法让Fragment操作执行。从以上代码可知,Fragment2即将要显示在界面上。
Fragment要获取其他片断传递来的数据,通常在onCreateView()方法中实现代码,通过调用继承的getArguments()方法就能获得Bundle数据。
Android支持多线程编程,即一个应用程序中可以创建多个线程,同时执行多个任务。我们经常会把耗时的工作放在子线程中执行,避免UI界面延迟对用户操作的响应,当子线程执行完毕后,可能有结果需要反馈给UI线程进行界面的更新。当然除了子线程需要返回数据给UI线程,多个子线程之间可能也需要数据的交换,线程间消息传递的桥梁,是名为Handler的类。
Handler类的主要作用有两个,一个是向Han⁃dler类所在线程发送消息,另一个是帮助所在线程获取和处理消息,如图3所示。
图3 Handler工作原理
为了更好地理解Handler工作原理,需要了解与Handler一起工作的三个组件,分别是Message,Looper以及MessageQueue。
1)Message:是Handler接收和处理的消息对象。
2)MessageQueue:是消息队列,存放Message,并由Looper不断进行读取。
3)Looper:Looper类有一个loop()方法,用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。每个线程都只能有一个Looper对象。
如果线程需要获取其他线程的消息,就需要在线程中创建Looper对象以及Handler对象,Loop⁃er对象,需要使用Looper.prepare()方法创建,一旦Looper对象初始化成功,系统会同时创建为线程创建MessageQueue对象。唯一例外的线程是UI主线程,Android系统在UI主线程中已经初始化了一个Looper对象,因此当UI主线程需要获取其他线程消息时,只要创建Handler对象即可。
下面的示例代码,用于非UI线程接受其他子线程的消息。需要提醒的是Looper对象的创建不能放在子线程的构造方法中,必须放在run()方法内。
public class MyThread implements Runnable{
若其他线程向示例中的MyThread线程发送消息,则要调用MyThread对象.threadHandler.send⁃Message(Message)方法,因此子线程的Handler对象的访问级别最好设置为public,使其他子线程方便调用。子线程中的Looper对象在MessageQueue发现新消息后,会通知Handler对象调用handleMes⁃sage()方法进行处理。
在我们实际的手机应用编程中,消息的处理非常广泛,比如用户配置界面向主界面返回配置数据,游戏程序中游戏状态需要周期性地更新UI,又如网络数据的获取反馈等。本文详细介绍并分析了Activity界面、Fragment片断以及线程Thread之间信息传递的原理和方法,对提升程序员的An⁃droid手机程序编制水平是非常有帮助的。
[1]纪晓阳.线程在Android开发中的应用[J].软件,2013,34(8):24-26.
[2]解志群.Android多线程与消息循环[J].电子世界,2013(19):87-88.
[3]李刚.疯狂Android讲义[M].北京:电子工业出版社,2013.
[4]杨丰盛.Android技术内幕[M].北京:机械工业出版社,2011.