Android系统中的广播Broadcast机制简要介绍和学习计划_第1页
Android系统中的广播Broadcast机制简要介绍和学习计划_第2页
Android系统中的广播Broadcast机制简要介绍和学习计划_第3页
Android系统中的广播Broadcast机制简要介绍和学习计划_第4页
Android系统中的广播Broadcast机制简要介绍和学习计划_第5页
免费预览已结束,剩余12页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制;这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用;本文通过一个简单的例子来学习Android系统的广播机制,为后续分析广播机制的源代码作准备。在Android系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。然而,广播机制却是不可替代的,它和Binder机制不一样的地

2、方在于,广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。在软件工程中,是非常强调模块之间的高内聚低耦合性的,不然的话,随着系统越来越庞大,就会面临着越来越难维护的风险,最后导致整个项目的失败。Android应用程序的组织方式,可以说是把这种高内聚低耦合性的思想贯彻得非常透彻,在任何一个Activity中,都可以使用一个简单的Intent,通过startActivity或者startService,就可以把另外一个Activity或者Service启动起来为它服务,而且它根本上不依赖

3、这个Activity或者Service的实现,只需要知道它的字符串形式的名字即可,而广播机制更绝,它连接收者的名字都不需要知道。不过话又说回来,广播机制在Android系统中,也不算是什么创新的东西。如果读者了解J2EE或者COM,就会知道,在J2EE中,提供了消息驱动Bean(Message-DrivenBean),用来实现应用程序各个组件之间的消息传递;而在COM中,提供了连接点(ConnectionPoint)的概念,也是用来在应用程序各个组间间进行消息传递。无论是J2EE中的消息驱动Bean,还是COM中的连接点,或者Android系统的广播机制,它们的实现机理都是消息发布/订阅模式的

4、事件驱动模型,消息的生产者发布事件,而使用者订阅感兴趣的事件。废话说了一大堆,现在开始进入主题了,和前面的文章一样,我们通过具体的例子来介绍Android系统的广播机制。在这个例子中,有一个Service,它在另外一个线程中实现了一个计数器服务,每隔一秒钟就自动加1,然后将结果不断地反馈给应用程序中的界面线程,而界面线程中的Activity在得到这个反馈后,就会把结果显示在界面上。为什么要把计数器服务放在另外一个线程中进行呢?我们可以把这个计数器服务想象成是一个耗时的计算型逻辑,如果放在界面线程中去实现,那么势必就会导致应用程序不能响应界面事件,最后导致应用程序产生ANR(Applicatio

5、nNotResponding)问题。计数器线程为了把加1后的数字源源不断地反馈给界面线程,这时候就可以考虑使用广播机制了。首先在Android源代码工程中创建一个Android应用程序工程,名字就称为Broadcast吧。关于如何获得Android源代码工程,请参考在Ubuntu上下载、编译和安装Android最新源代码一文;关于如何在Android源代码工程中创建应用程序工程,请参考在Ubuntu上为Android系统内置Java应用程序测试ApplicationFrameworks层的硬件服务一文。这个应用程序工程定义了一个名为shy.luo.broadcast的package,这个例子的

6、源代码主要就是实现在这里了。下面,将会逐一介绍这个package里面的文件。首先,我们在src/shy/luo/broadcast/ICounterService.java文件中定义计数器的服务接口:viewplain1.packageshy.luo.broadcast;4.publicvoidstartCounter(intinitVal)5.publicvoidstopCounter。;6.3.publicinterfaceICounterService这个接口很简单,它只有两个成员函数,分别用来启动和停止计数器;启动计数时,还可以指定计数器的初始值。接着,我们来看一个应用程序的默认Act

7、ivity的实现,在src/shy/luo/broadcast/MainActivity.java件中:viewplain18.publicclassMainActivityextendsActivityimplementsOnClickListener19.privatefinalstaticStringLOG_TAG="shy.luo.broadcast.MainActivity"1.packageshy.luo.broadcast;2.3.importandroid.app.Activity;4.importandroid.content.BroadcastRecei

8、ver;5.importandroid.content.ComponentName;6.importandroid.content.Context;7.importandroid.content.Intent;8.importandroid.content.IntentFilter;9.importandroid.content.ServiceConnection;10.importandroid.os.Bundle;11.importandroid.os.IBinder;12.importandroid.util.Log;13.importandroid.view.View;14.impor

9、tandroid.view.View.OnClickListener;15.importandroid.widget.Button;16.importandroid.widget.TextView;17.21.privateButtonstartButton=null;22.privateButtonstopButton=null;23.privateTextViewcounterText=null20.;24.25.privateICounterServicecounterService=null;26.27.Override28.publicvoidonCreate(Bundlesaved

10、InstanceState)29.super.onCreate(savedInstanceState);30.setContentView(R.layout.main);31.32.startButton=(Button)findViewById(R.id.button_start);33. stopButton=(Button)findViewById(R.id.button_stop);34. counterText=(TextView)findViewById(R.id.textview_counter);35.36. startButton.setOnClickListener(thi

11、s);37. stopButton.setOnClickListener(this);38.39. startButton.setEnabled(true);40. stopButton.setEnabled(false);41.class);42. IntentbindIntent=newIntent(MainActivity.this,CounterService.43. bindService(bindIntent,serviceConnection,Context.BIND_AUTO_CREATE);44.45. Log.i(LOG_TAG,"MainActivityCrea

12、ted.");46. 47.48. Override49. publicvoidonResume()50. super.onResume();51.52. IntentFiltercounterActionFilter=newIntentFilter(CounterService.BROADCAST_COUNTER_ACTION);53. registerReceiver(counterActionReceiver,counterActionFilter);54. 55.56. Override57. publicvoidonPause()58. super.onPause();59

13、. unregisterReceiver(counterActionReceiver);60. 61.62. Override63. publicvoidonDestroy()64. super.onDestroyO;65. unbindService(serviceConnection);66. 67.68. Override69. publicvoidonClick(Viewv)70. if(v.equals(startButton)71. if(counterService!=null)72. counterService.startCounter(0);73.74. startButt

14、on.setEnabled(false);75. stopButton.setEnabled(true);1.92.)elseif(v.equals(stopButton)if(counterService!=null)counterService.stopCounter();startButton.setEnabled(true);stopButton.setEnabled(false);privateBroadcastReceivercounterActionReceiver=newBroadcas

15、tReceiver()publicvoidonReceive(Contextcontext,Intentintent)intcounter=intent.getIntExtra(CounterService.COUNTER_VALUE,0);Stringtext=String.valueOf(counter);counterText.setText(text);93. Log.i(LOG_TAG,"Receivecounterevent");94. 95. ;96.97. privateServiceConnectionserviceConnection=newServic

16、eConnection()98. publicvoidonServiceConnected(ComponentNameclassName,IBinderservice)99. counterService=(CounterService.CounterBinder)service).getService();100.101. Log.i(LOG_TAG,"CounterServiceConnected");102. 103. publicvoidonServiceDisconnected(ComponentNameclassName)104. counterService=

17、null;105. Log.i(LOG_TAG,"CounterServiceDisconnected");106. 107. ;108. MainActivity的实现也很简单,它在创建(onCreate)的时候,会调用bindService函数来把计数器服务(CounterService)启动起来,它的第二个参数serviceConnection是一个ServiceConnection实例.计数器服务启动起来后,系统会调用这个实例的onServiceConnected函数将一个Binder对象传回来,通过调用这个Binder对象的getService函数,就可以获得计数

18、器服务接口。这里,把这个计数器服务接口保存在MainActivity的counterService成员变量中。同样,当我们调用unbindService停止计数器服务时,系统会调用这个实例的onServiceDisconnected函数告诉MainActivity,它与计数器服务的连接断开了。注意,这里通过调用bindService函数来启动Service时,这个Service与启动它的Activity是位于同一个进程中,它不像我们在前面一篇文章Android系统在新进程中启动自定义服务过程(startService)bindService启动服务的的原理分析中所描述那样在新的进程中启动服务,

19、后面我们再写一篇文章来分析过程。在MainActivity的onResume函数中,通过调用registerReceiver函数注册了一个广播接收器counterActionReceiver,它是一个BroadcastReceiver实例,并且指定了这个广播接收器只对CounterService.BROADCAST_COUNTER_ACTION类型的广播感兴趣。当CounterService发出一个CounterService.BROADCAST_COUNTER_ACTION类型的广播时,系统就会把这个广播发送到counterActionReceiver实例的onReceiver函数中去。在o

20、nReceive函数中,从参数intent中取出计数器当前的值,显示在界面上。MainActivity界面上有两个按钮,分别是StartCounter和StopCounter按钮,点击前者开始计数,而点击后者则停止计数。计数器服务CounterService实现在src/shy/luo/broadcast/CounterService.java文件中:viewplain1.packageshy.luo.broadcast;2.3.importandroid.app.Service;4.importandroid.content.Intent;5.importandroid.os.AsyncTa

21、sk;6.importandroid.os.Binder;7.importandroid.os.IBinder;8.importandroid.util.Log;9.10. publicclassCounterServiceextendsServiceimplementsICounterService11.privatefinalstaticStringLOG_TAG="shy.luo.broadcast.CounterService"12.13.publicfinalstaticStringBROADCAST_COUNTER_ACTION="shy.luo.br

22、oadcast.COUNTER_ACTION"14.publicfinalstaticStringCOUNTER_VALUE="shy.luo.broadcast.counter.value"15.16. privatebooleanstop=false;17.18. privatefinalIBinderbinder=newCounterBinder。;19.20. publicclassCounterBinderextendsBinder21. publicCounterServicegetService。22. returnCounterService.th

23、is;23. 24. 25.26. Override27. publicIBinderonBind(Intentintent)28. returnbinder;29. 30.31. Override32. publicvoidonCreate()33. super.onCreate();34.35. Log.i(LOG_TAG,"CounterServiceCreated.");36. 37.38. Override39. publicvoidonDestroyO40. Log.i(LOG_TAG,"CounterServiceDestroyed.");

24、41. 42.43. publicvoidstartCounter(intinitVal)44. AsyncTask<Integer,Integer,Integer>task=newAsyncTask<Integer,Integer,Integer>045. Override46. protectedIntegerdoInBackground(Integer.vals)47. IntegerinitCounter=vals0;48.49. stop=false;50. while(!stop)51. publishProgress(initCounter);52.53.

25、 try54. Thread.sleep(1000);55. catch(InterruptedExceptione)56. e.printStackTrace();57. 58.59. initCounter+;60. 61.62. returninitCounter;63. 64.65. Override66. protectedvoidonProgressUpdate(Integer.values)67. super.onProgressUpdate(values);68.69. intcounter=values0;70.71. Intentintent=newIntent(BROAD

26、CAST_COUNTER_ACTION);72. intent.putExtra(COUNTER_VALUE,counter);73.74. sendBroadcast(intent);75. 76.77. Override78. protectedvoidonPostExecute(Integerval)79. intcounter=val;80.81. Intentintent=newIntent(BROADCAST_COUNTER_ACTION);82. intent.putExtra(COUNTER_VALUE,counter);83.84. sendBroadcast(intent)

27、;85. 86.87. ;88.89. task.execute(0);90. 91.92. publicvoidstopCounter()93. stop=true;94. 95. 这个计数器服务实现了ICounterService接口。当这个服务被binderService函数启动时,系统会调用它的onBind函数,这个函数返回一个Binder对象给系统。上面我们说到,当MainActivity调用bindService函数来启动计数器服务器时,系统会调用MainActivity的ServiceConnection实例serviceConnection的onServiceConnected

28、函数通知MainActivity,这个服务已经连接上了,并且会通过这个函数传进来一个Binder远程对象,这个Binder远程对象就是来源于这里的onBind的返回值了。函数onBind返回的Binder对象是一个自定义的CounterBinder实例,它实现了一个getService成员函数。当系统通知MainActivity,计数器服务已经启动起来并且连接成功后,并且将这个Binder对象传给MainActivity时,MainActivity就会把这个Binder对象强制转换为CounterBinder实例,然后调用它的getService函数获得服务接口。这本MainActivity就

29、通过这个Binder对象和CounterService关联起来了。当MainActivity调用计数器服务接口的startCounter函数时,计数器服务并不是直接进入计数状态,而是通过使用异步任务(AsyncTask)在后台线程中进行计数。这里为什么要使用异步任务来在后台线程中进行计数呢?前面我们说过,这个计数过程是一个耗时的计算型逻辑,不能把它放在界面线程中进行,因为这里的CounterService启动时,并没有在新的进程中启动,它与MainActivity一样,运行在应用程序的界面线程中,因此,这里需要使用异步任务在在后台线程中进行计数。异步任务AsyncTask的具体用法可以参考官方

30、文档务实区J的execute(task.execute)方法时,当前调用线程就返回了,系统启动一个后台线程来执行这个异步任务实例的doInBackground函数,这个函数就是我们用来执行耗时计算的地方了,它会进入到一个循环中,每隔1秒钟就把计数器加1,然后进入休眠(Thread.sleep),醒过来,再重新这个计算过程。在计算的过程中,可以通过调用publishProgress函数来通知调用者当前计算的进度,好让调用者来更新界面,调用publishProgress函数的效果最终就是直入到这个异步任务实例的onProgressUpdate函数中,这里就可以把这个进度值以广播的形式(sendBr

31、oadcast)发送出去了,这里的进度值就定义为当前计数服务的计数值。当MainActivity调用计数器服务接口的stopCounter函数时,会告诉函数doInBackground停止执行计数(stop=true),于是,函数doInBackground就退出计数循环,然后将最终计数结果返回了,返回的结果最后进入到onPostExecute函数中,这个函数同样通过广播的形式(sendBroadcast)把这个计数结果广播出去。计算器服务就介绍到这里了,下面我们看看应用程序的配置文件AndroidManifest.xml:viewplain1. <?xmlversion="1

32、.0"encoding="utf-8"?>2. manifestxmlns:android="3. package="shy.luo.broadcast"4. android:versionCode="1"5. android:versionName="1.0">6. <applicationandroid:icon="drawable/icon"android:label="string/app_name">7. <acti

33、vityandroid:name=".MainActivity"8. android:label="string/app_name">9. <intent-filter>10. <actionandroid:name="ent.action.MAIN"/>11. <categoryandroid:name="ent.category.LAUNCHER"/>12. </intent-filter>13. </act

34、ivity>14. <serviceandroid:name=".CounterService"15. android:enabled="true">16. </service>17. </application>18. /manifest这个配置文件很简单,只是告诉系统,它有一个Activity和一个Service。再来看MainActivity的界面文件,它定义在res/layout/main.xml文件中:viewplain1. <?xmlversion="1.0"encoding

35、="utf-8"?>2. <LinearLayoutxmlns:android="3. android:orientation="vertical"4. android:layout_width="fill_parent".5.46.47.android:layout_height=&q

36、uot;fill_parent”android:gravity="center"><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_marginBottom="10px"android:orientation="horizontal"android:gravity="center"><TextViewandr

37、oid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="4px"android:gravity="center"android:text="string/counter"></TextView><TextViewandroid:id="+id/textview_counter"android:layout_width=&

38、quot;wrap_content"android:layout_height="wrap_content"android:gravity="center"android:text="0"></TextView></LinearLayout><LinearLayoutandroid:layout_widthandroid:layout_heightandroid:orientation="fill_parent"="wrap_content"=&qu

39、ot;horizontal"android:gravity="center"><Buttonandroid:id="+id/button_start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:text="string/start"></Button><Buttonand

40、roid:id="+id/button_stop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:text="string/stop"></Button></LinearLayout>48.</LinearLayout>这个界面配置文件也很简单,等一下我们在模拟器把这个应用程序启动起来后,就可以看到

41、它的截图了。应用程序用到的字符串资源文件位于res/values/strings.xml文件中:viewplain1.<?xmlversion="1.0"encoding="utf-8"?>2.<resources>3.<stringname="app_name">Broadcast</string>4.<stringname="counter">Counter:</string>5.<stringname="start&quo

42、t;>StartCounter</string>6.<stringname="stop">StopCounter</string>7.</resources>最后,我们还要在工程目录下放置一个编译脚本文件Android.mk:viewplain1. LOCAL_PATH尸$(callmy-dir)2. include$(CLEAR_VARS)3.4. LOCAL_MODULE_TAGS=optional5.6. LOCAL_SRC_FILES:=$(callall-subdir-java-files)7.8. LOCAL

43、_PACKAGE_NAME=Broadcast9.10. include$(BUILD_PACKAGE)接下来就要编译了。有关如何单独编译Android源代码工程的模块,以及如何打包system.img,请参考如何单独编译Android源代码中的模块一文。执行以下命令进行编译和打包:viewplain1. USER-NAMEMACHINE-NAME:/Android$mmmpackages/experimental/Broadcast2. USER-NAMEMACHINE-NAME:/Android$makesnod这样,打包女?的Android系统镜像文件system.img就包含我们前面创

44、建的Broadcast应用程序了再接下来,就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器,请参考在Ubuntu上下载、编译和安装Android最新源代码一文。执行以下命令启动模拟器:viewplain1.USER-NAMEMACHINE-NAME:/Android$emulator模拟器启动起,就可以AppLauncher中找到Broadcast应用程序图标,接着把它启动起来,然后点击界面上的StartCounter按钮,就可以把计数器服务启动起来了,计数器服务又通过广播把计数值反馈给MainActivity,于是,我们就会在MainActivity界面看到计

45、数器的值不断地增加了:5S54:<buitd>35ri4:26BroadcastCounter:26StopCounter峥这样,使用广播的例子就介绍完了。回顾一下,使用广播的两个步骤:1.广播的接收者需要通过调用registerReceiver函数告诉系统,它对什么样的广播有兴趣,即指定IntentFilter,并且向系统注册广播接收器,即指定BroadcastReceiver:viewplain1. IntentFiltercounterActionFilter=newIntentFilter(CounterService.BROADCAST_COUNTER_ACTION);2. registerReceiver(counterAct

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论