《Android服务Service详解电子版本.doc》由会员分享,可在线阅读,更多相关《Android服务Service详解电子版本.doc(137页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Good is good, but better carries it.精益求精,善益求善。Android服务Service详解-第8章Android服务8.1Service起步8.1.1Service的生命周期8.1.2绑定Activity和Service8.1.3在BroadcastReceiver中启动Ser.8.2.1获得系统服务8.2.2在模拟器上模拟重力感应8.3.1计时器:Chronometer8.3.2预约时间Handler8.3.4在线程中更新GUI组件8.3.5全局定时器AlarmManager(1)8.3.5全局定时器AlarmManager(2)8.3.5全局定时器Al
2、armManager(3)8.4.1什么是AIDL服务8.4.2建立AIDL服务的步骤(1)8.4.2建立AIDL服务的步骤(2)8.4.2建立AIDL服务的步骤(3)8.5本章小结第8章Android服务服务(Service)是Android系统中4个应用程序组件之一(其他的组件详见3.2节的内容)。服务主要用于两个目的:后台运行和跨进程访问。通过启动一个服务,可以在不显示界面的前提下在后台运行指定的任务,这样可以不影响用户做其他事情。通过AIDL服务可以实现不同进程之间的通信,这也是服务的重要用途之一。本章内容Service的生命周期绑定Activity和Service在Broadcast
3、Receiver中启动Service系统服务时间服务在线程中更新GUI组件AIDL服务在AIDL服务中传递复杂的数据8.1Service起步Service并没有实际界面,而是一直在Android系统的后台运行。一般使用Service为应用程序提供一些服务,或不需要界面的功能,例如,从Internet下载文件、控制Video播放器等。本节主要介绍Service的启动和结束过程(Service的生命周期)以及启动Service的各种方法。8.1.1Service的生命周期本节的例子代码所在的工程目录是srcch08ch08_servicelifecycleService与Activity一样,也有
4、一个从启动到销毁的过程,但Service的这个过程比Activity简单得多。Service启动到销毁的过程只会经历如下3个阶段:创建服务开始服务销毁服务一个服务实际上是一个继承android.app.Service的类,当服务经历上面3个阶段后,会分别调用Service类中的3个事件方法进行交互,这3个事件方法如下:1. publicvoidonCreate();/创建服务2. publicvoidonStart(Intentintent,intstartId);/开始服务3. publicvoidonDestroy();/销毁服务一个服务只会创建一次,销毁一次,但可以开始多次,因此,onC
5、reate和onDestroy方法只会被调用一次,而onStart方法会被调用多次。下面编写一个服务类,具体看一下服务的生命周期由开始到销毁的过程。1. packagenet.blogjava.mobile.service;2. importandroid.app.Service;3. importandroid.content.Intent;4. importandroid.os.IBinder;5. importandroid.util.Log;6. /MyService是一个服务类,该类必须从android.app.Service类继承7. publicclassMyServiceext
6、endsService8. 9. Override10. publicIBinderonBind(Intentintent)11. 12. returnnull;13. 14. /当服务第1次创建时调用该方法15. Override16. publicvoidonCreate()17. 18. Log.d(MyService,onCreate);19. super.onCreate();20. 21. /当服务销毁时调用该方法22. Override23. publicvoidonDestroy()24. 25. Log.d(MyService,onDestroy);26. super.onD
7、estroy();27. 28. /当开始服务时调用该方法29. Override30. publicvoidonStart(Intentintent,intstartId)31. 32. Log.d(MyService,onStart);33. super.onStart(intent,startId);34. 35. 在MyService中覆盖了Service类中3个生命周期方法,并在这些方法中输出了相应的日志信息,以便更容易地观察事件方法的调用情况。读者在编写Android的应用组件时要注意,不管是编写什么组件(例如,Activity、Service等),都需要在AndroidManif
8、est.xml文件中进行配置。MyService类也不例外。配置这个服务类很简单,只需要在AndroidManifest.xml文件的标签中添加如下代码即可:1. 其中android:enabled属性的值为true,表示MyService服务处于激活状态。虽然目前MyService是激活的,但系统仍然不会启动MyService,要想启动这个服务。必须显式地调用startService方法。如果想停止服务,需要显式地调用stopService方法,代码如下:1. publicvoidonClick(Viewview)2. 3. switch(view.getId()4. 5. caseR.id
9、.btnStartService:6. startService(serviceIntent);/单击【StartService】按钮启动服务7. break;8. caseR.id.btnStopService:9. stopService(serviceIntent);/单击【StopService】按钮停止服务10. break;11. 12. 其中serviceIntent是一个Intent对象,用于指定MyService服务,创建该对象的代码如下:1. serviceIntent=newIntent(this,MyService.class);运行本节的例子后,会显示如图8.1所示的
10、界面。图8.1开始和停止服务第1次单击【StartService】按钮后,在DDMS透视图的LogCat视图的Message列会输出如下两行信息:1. onCreate2. onStart然后单击【StopService】按钮,会在Message列中输出如下信息:1. onDestroy下面按如下的单击按钮顺序的重新测试一下本例。【StartService】【StopService】【StartService】【StartService】【StartService】【StopService】测试完程序,就会看到如图8.2所示的输出信息。可以看出,只在第1次单击【StartService】按钮后
11、会调用onCreate方法,如果在未单击【StopService】按钮时多次单击【StartService】按钮,系统只在第1次单击【StartService】按钮时调用onCreate和onStart方法,再单击该按钮时,系统只会调用onStart方法,而不会再次调用onCreate方法。图8.2服务的生命周期方法的调用情况在讨论完服务的生命周期后,再来总结一下创建和开始服务的步骤。创建和开始一个服务需要如下3步:(1)编写一个服务类,该类必须从android.app.Service继承。Service类涉及到3个生命周期方法,但这3个方法并不一定在子类中覆盖,读者可根据不同需求来决定使用哪
12、些生命周期方法。在Service类中有一个onBind方法,该方法是一个抽象方法,在Service的子类中必须覆盖。这个方法当Activity与Service绑定时被调用(将在8.1.3节详细介绍)。(2)在AndroidManifest.xml文件中使用标签来配置服务,一般需要将标签的android:enabled属性值设为true,并使用android:name属性指定在第1步建立的服务类名。(3)如果要开始一个服务,使用startService方法,停止一个服务要使用stopService方法。8.1.2绑定Activity和Service本节的例子代码所在的工程目录是srcch08ch
13、08_serviceactivity如果使用8.1.1节介绍的方法启动服务,并且未调用stopService来停止服务,这个服务就会随着Android系统的启动而启动,随着Android系统的关闭而关闭。也就是服务会在Android系统启动后一直在后台运行,直到Android系统关闭后服务才停止。但有时我们希望在启动服务的Activity关闭后服务自动关闭,这就需要将Activity和Service绑定。通过bindService方法可以将Activity和Service绑定。bindService方法的定义如下:1. publicbooleanbindService(Intentservic
14、e,ServiceConnectionconn,intflags)该方法的第1个参数表示与服务类相关联的Intent对象,第2个参数是一个ServiceConnection类型的变量,负责连接Intent对象指定的服务。通过ServiceConnection对象可以获得连接成功或失败的状态,并可以获得连接后的服务对象。第3个参数是一个标志位,一般设为Context.BIND_AUTO_CREATE。下面重新编写8.1.1节的MyService类,在该类中增加了几个与绑定相关的事件方法。1. packagenet.blogjava.mobile.service;2. importandroid.
15、app.Service;3. importandroid.content.Intent;4. importandroid.os.Binder;5. importandroid.os.IBinder;6. importandroid.util.Log;7. publicclassMyServiceextendsService8. 9. privateMyBindermyBinder=newMyBinder();10. /成功绑定后调用该方法11. Override12. publicIBinderonBind(Intentintent)13. 14. Log.d(MyService,onBind
16、);15. returnmyBinder;16. 17. /重新绑定时调用该方法18. Override19. publicvoidonRebind(Intentintent)20. 21. Log.d(MyService,onRebind);22. super.onRebind(intent);23. 24. /解除绑定时调用该方法25. Override26. publicbooleanonUnbind(Intentintent)27. 28. Log.d(MyService,onUnbind);29. returnsuper.onUnbind(intent);30. 31. Overri
17、de32. publicvoidonCreate()33. 34. Log.d(MyService,onCreate);35. super.onCreate();36. 37. Override38. publicvoidonDestroy()39. 40. Log.d(MyService,onDestroy);41. super.onDestroy();42. 43. Override44. publicvoidonStart(Intentintent,intstartId)45. 46. Log.d(MyService,onStart);47. super.onStart(intent,s
18、tartId);48. 49. publicclassMyBinderextendsBinder50. 51. MyServicegetService()52. 53. returnMyService.this;54. 55. 56. 现在定义一个MyService变量和一个ServiceConnection变量,代码如下:1. privateMyServicemyService;2. privateServiceConnectionserviceConnection=newServiceConnection()3. 4. /连接服务失败后,该方法被调用5. Override6. public
19、voidonServiceDisconnected(ComponentNamename)7. 8. myService=null;9. Toast.makeText(Main.this,ServiceFailed.,Toast.LENGTH_LONG).show();10. 11. /成功连接服务后,该方法被调用。在该方法中可以获得MyService对象12. Override13. publicvoidonServiceConnected(ComponentNamename,IBinderservice)14. 15. /获得MyService对象16. myService=(MyServi
20、ce.MyBinder)service).getService();17. Toast.makeText(Main.this,ServiceConnected.,Toast.LENGTH_LONG).show();18. 19. ;最后使用bindService方法来绑定Activity和Service,代码如下:1. bindService(serviceIntent,serviceConnection,Context.BIND_AUTO_CREATE);如果想解除绑定,可以使用下面的代码:1. unbindService(serviceConnection);在MyService类中定义了
21、一个MyBinder类,该类实际上是为了获得MyService的对象实例的。在ServiceConnection接口的onServiceConnected方法中的第2个参数是一个IBinder类型的变量,将该参数转换成MyService.MyBinder对象,并使用MyBinder类中的getService方法获得MyService对象。在获得MyService对象后,就可以在Activity中随意操作MyService了。运行本节的例子后,单击【BindService】按钮,如果绑定成功,会显示如图8.3所示的信息提示框。关闭应用程序后,会看到在LogCat视图中输出了onUnbind和on
22、Destroy信息,表明在关闭Activity后,服务先被解除绑定,最后被销毁。如果先启动(调用startService方法)一个服务,然后再绑定(调用bindService方法)服务,会怎么样呢?在这种情况下,虽然服务仍然会成功绑定到Activity上,但在Activity关闭后,服务虽然会被解除绑定,但并不会被销毁,也就是说,MyService类的onDestroy方法不会被调用。图8.3绑定服务8.1.3在BroadcastReceiver中启动Service本节的例子代码所在的工程目录是srcch08ch08_startupservice在8.1.1节和8.1.2节都是先启动了一个Ac
23、tivity,然后在Activity中启动服务。如果是这样,在启动服务时必须要先启动一个Activity。在很多时候这样做有些多余,阅读完第7章的内容,会发现实例43可以利用BroadcastReceiver在Android系统启动时运行一个Activity。也许我们会从中得到一些启发,既然可以在BroadcastReceiver中启动Activity,为什么不能启动Service呢?说做就做,现在让我们来验证一下这个想法。先编写一个服务类,这个服务类没什么特别的,仍然使用前面两节编写的MyService类即可。在AndroidManifest.xml文件中配置MyService类的代码也相同
24、。下面来完成最关键的一步,就是建立一个BroadcastReceiver,代码如下:1. packagenet.blogjava.mobile.startupservice;2. importandroid.content.BroadcastReceiver;3. importandroid.content.Context;4. importandroid.content.Intent;5. publicclassStartupReceiverextendsBroadcastReceiver6. 7. Override8. publicvoidonReceive(Contextcontext,
25、Intentintent)9. 10. /启动一个Service11. IntentserviceIntent=newIntent(context,MyService.class);12. context.startService(serviceIntent);13. IntentactivityIntent=newIntent(context,MessageActivity.class);14. /要想在Service中启动Activity,必须设置如下标志15. activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);16. conte
26、xt.startActivity(activityIntent);17. 18. 在StartupReceiver类的onReceive方法中完成了两项工作:启动服务和显示一个Activity来提示服务启动成功。其中MessageActivity是一个普通的Activity类,只是该类在配置时使用了android:style/Theme.Dialog主题,因此,如果服务启动成功,会显示如图8.4所示的信息。图8.4在BroadcastReceiver中启动服务如果安装本例后,在重新启动模拟器后并未出现如图8.4所示的信息提示框,最大的可能是没有在AndroidManifest.xml文件中配置
27、BroadcastReceiver和Service,下面来看一下AndroidManifest.xml文件的完整代码。1. 2. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 现在运行本例,然后重启一下模拟器,看看LogCat视图中是否输出了相应的日志信息。8.2系统服务在Android系统中有很多内置的软件,例如,当手机接到来电时,会显示对方的电话号。也可以根据周围的环境将手机设置成震动或静音。如果想把这些功能加到自己的软件中应该怎么办呢?答案就是系统服务。在Android系统中提供了很多这种服务,通过这些服务,
28、就可以像Android系统的内置软件一样随心所欲地控制Android系统了。本节将介绍几种常用的系统服务来帮助读者理解和使用这些技术。8.2.1获得系统服务系统服务实际上可以看作是一个对象,通过Activity类的getSystemService方法可以获得指定的对象(系统服务)。getSystemService方法只有一个String类型的参数,表示系统服务的ID,这个ID在整个Android系统中是唯一的。例如,audio表示音频服务,window表示窗口服务,notification表示通知服务。为了便于记忆和管理,AndroidSDK在android.content.Context类中
29、定义了这些ID,例如,下面的代码是一些ID的定义。1. publicstaticfinalStringAUDIO_SERVICE=audio;/定义音频服务的ID2. publicstaticfinalStringWINDOW_SERVICE=window;/定义窗口服务的ID3. publicstaticfinalStringNOTIFICATION_SERVICE=notification;/定义通知服务的ID下面的代码获得了剪贴板服务(android.text.ClipboardManager对象)。1. /获得ClipboardManager对象2. android.text.Clip
30、boardManagerclipboardManager=3. (android.text.ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);4. clipboardManager.setText(设置剪贴版中的内容);在调用ClipboardManager.setText方法设置文本后,在Android系统中所有的文本输入框都可以从这个剪贴板对象中获得这段文本,读者不妨自己试一试!窗口服务(WindowManager对象)是最常用的系统服务之一,通过这个服务,可以获得很多与窗口相关的信息,例如,窗口的长度和宽度,如下面的
31、代码所示:1. /获得WindowManager对象2. android.view.WindowManagerwindowManager=(android.view.WindowManager)3. getSystemService(Context.WINDOW_SERVICE);4. /在窗口的标题栏输出当前窗口的宽度和高度,例如,320*4805. setTitle(String.valueOf(windowManager.getDefaultDisplay().getWidth()+*6. +String.valueOf(windowManager.getDefaultDisplay()
32、.getHeight();本节简单介绍了如何获得系统服务以及两个常用的系统服务的使用方法,在接下来的实例47和实例48中将给出两个完整的关于获得和使用系统服务的例子以供读者参考。实例47:监听手机来电工程目录:srcch08ch08_phonestate当来电话时,手机会显示对方的电话号,当接听电话时,会显示当前的通话状态。在这期间存在两个状态:来电状态和接听状态。如果在应用程序中要监听这两个状态,并进行一些其他处理,就需要使用电话服务(TelephonyManager对象)。本例通过TelephonyManager对象监听来电状态和接听状态,并在相应的状态显示一个Toast提示信息框。如果是
33、来电状态,会显示对方的电话号,如果是通话状态,会显示正在通话.信息。下面先来看看来电和接听时的效果,如图8.5和图8.6所示。图8.5来电状态图8.6接听状态要想获得TelephonyManager对象,需要使用Context.TELEPHONY_SERVICE常量,代码如下:1. TelephonyManagertm=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);2. MyPhoneCallListenermyPhoneCallListener=newMyPhoneCallListener();3. /设置电话状态
34、监听器4. tm.listen(myPhoneCallListener,PhoneStateListener.LISTEN_CALL_STATE);其中MyPhoneCallListener类是一个电话状态监听器,该类是PhoneStateListener的子类,代码如下:1. publicclassMyPhoneCallListenerextendsPhoneStateListener2. 3. Override4. publicvoidonCallStateChanged(intstate,StringincomingNumber)5. 6. switch(state)7. 8. /通话状
35、态9. caseTelephonyManager.CALL_STATE_OFFHOOK:10. Toast.makeText(Main.this,正在通话.,Toast.LENGTH_SHORT).show();11. break;12. /来电状态13. caseTelephonyManager.CALL_STATE_RINGING:14. Toast.makeText(Main.this,incomingNumber,Toast.LENGTH_SHORT).show();15. break;16. 17. super.onCallStateChanged(state,incomingNum
36、ber);18. 19. 如果读者是在模拟器上测试本例,可以使用DDMS透视图的【EmulatorControl】视图模拟打入电话。进入【EmulatorControl】视图,会看到如图8.7所示的界面。在【Incomingnumber】文本框中输入一个电话号,选中【Voice】选项,单击【Call】按钮,这时模拟器就会接到来电。如果已经运行本例,在来电和接听状态就会显示如图8.5和图8.6所示的Toast提示信息。图8.7用【EmulatorControl】视图模拟拨打电话实例48:来电黑名单工程目录:srcch08ch08_phoneblacklist虽然手机为我们带来了方便,但有时实在不
37、想接听某人的电话,但又不好直接挂断电话,怎么办呢?很简单,如果发现是某人来的电话,直接将手机设成静音,这样就可以不予理睬了。本例与实例47类似,也就是说,仍然需要获得TelephonyManager对象,并监听手机的来电状态。为了可以将手机静音,还需要获得一个音频服务(AudioManager对象)。本例需要修改实例47中的手机接听状态方法onCallStateChanged中的代码,修改后的结果如下:1. publicclassMyPhoneCallListenerextendsPhoneStateListener2. 3. Override4. publicvoidonCallStateChanged(intstate,StringincomingNumber)5. 6. /获得音频服务(AudioManager对象)7. AudioManageraudioManager=(AudioManager)getSystemService(Context.AUDIO_SERVICE);8. switch(state)9. 10. caseTelephonyManager.CALL_STATE_IDLE:11. /在手机空闲状态时,将手机音频设为正常状态12. audioManager.setRingerMode(AudioManager.RINGER_M