Android Service使用详解.docx
- 文档编号:6960233
- 上传时间:2023-01-13
- 格式:DOCX
- 页数:22
- 大小:468.40KB
Android Service使用详解.docx
《Android Service使用详解.docx》由会员分享,可在线阅读,更多相关《Android Service使用详解.docx(22页珍藏版)》请在冰豆网上搜索。
AndroidService使用详解
AndroidService使用详解
Service是Android系统中的四大组件之一,主要有两个应用场景:
后台运行和跨进程访问。
Service可以在后台执行长时间运行操作而不提供用户界面,除非系统必须回收内存资源,否则系统不会停止或销毁服务。
服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。
此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)
需要注意的是,Service是在主线程里执行操作的,可能会因为执行耗时操作而导致ANR
一、基础知识
Service可以分为以下三种形式:
启动
当应用组件通过调用startService()启动服务时,服务即处于“启动”状态。
一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。
已启动的服务通常是执行单一操作,而且不会将结果返回给调用方
绑定
当应用组件通过调用bindService()绑定到服务时,服务即处于“绑定”状态。
绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信(IPC)跨进程执行这些操作。
多个组件可以同时绑定服务,服务只会在组件与其绑定时运行,一旦该服务与所有组件之间的绑定全部取消,系统便会销毁它
启动且绑定
服务既可以是启动服务,也允许绑定。
此时需要同时实现以下回调方法:
onStartCommand()和onBind()。
系统不会在所有客户端都取消绑定时销毁服务。
为此,必须通过调用stopSelf()或stopService()显式停止服务
无论应用是处于启动状态还是绑定状态,或者处于启动且绑定状态,任何应用组件均可像使用Activity那样通过调用Intent来使用服务(即使此服务来自另一应用),也可以通过清单文件将服务声明为私有服务,阻止其他应用访问
要使用服务,必须继承Service类(或者Service类的现有子类),在子类中重写某些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务
onStartCommand()
当组件通过调用startService()请求启动服务时,系统将调用此方法(如果是绑定服务则不会调用此方法)。
一旦执行此方法,服务即会启动并可在后台无限期运行。
在指定任务完成后,通过调用stopSelf()或stopService()来停止服务
onBind()
当一个组件想通过调用bindService()与服务绑定时,系统将调用此方法(如果是启动服务则不会调用此方法)。
在此方法的实现中,必须通过返回IBinder提供一个接口,供客户端用来与服务进行通信
onCreate()
首次创建服务时,系统将调用此方法来执行初始化操作(在调用onStartCommand()或onBind()之前)。
如果在启动或绑定之前Service已在运行,则不会调用此方法
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法,这是服务接收的最后一个调用,在此方法中应清理占用的资源
仅当内存过低必须回收系统资源以供前台Activity使用时,系统才会强制停止服务。
如果将服务绑定到前台Activity,则它不太可能会终止,如果将服务声明为在前台运行,则它几乎永远不会终止。
或者,如果服务已启动并要长时间运行,则系统会随着时间的推移降低服务在后台任务列表中的位置,而服务也将随之变得非常容易被终止。
如果服务是启动服务,则必须将其设计为能够妥善处理系统对它的重启。
如果系统终止服务,那么一旦资源变得再次可用,系统便会重启服务(这还取决于onStartCommand()的返回值)
二、声明Service
如同其他组件一样,想要使用Service,必须在清单文件中对其进行声明
声明方式是添加
例如
android: allowBackup="true" android: icon="@mipmap/ic_launcher" android: label="@string/app_name" android: supportsRtl="true" android: theme="@style/AppTheme"> name=".MyService"/>
android:
name属性是唯一必需的属性,用于指定服务的类名,还可将其他属性包括在
为了确保应用的安全性,最好始终使用显式Intent启动或绑定Service,且不要为服务声明Intent过滤器。
启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供Intent过滤器并从Intent中排除相应的组件名称,但随后必须使用setPackage()方法设置Intent的软件包,这样可以充分消除目标服务的不确定性
此外,还可以通过添加android:
exported属性并将其设置为“false“,确保服务仅适用于本应用。
这可以有效阻止其他应用启动本应用内的服务,即便在使用显式Intent时也是如此
Service包含的属性有
description="stringresource" android: directBootAware=["true"|"false"] android: enabled=["true"|"false"] android: exported=["true"|"false"] android: icon="drawableresource" android: isolatedProcess=["true"|"false"] android: label="stringresource" android: name="string" android: permission="string" android: process="string"> 三、启动Service 启动服务由组件通过调用startService()启动,服务启动之后,其生命周期即独立于启动它的组件,并且可以在后台无限期地运行,即使启动服务的组件已被销毁也不受影响。 因此,服务应通过调用stopSelf()来自行停止运行,或者由另一个组件调用stopService()来停止 可以通过扩展两个类来创建启动服务: Service 这是所有服务的父类。 扩展此类时,如果要执行耗时操作,必须创建一个用于执行操作的新线程,因为默认情况下服务将运行于UI线程 IntentService 这是Service的子类,它使用工作线程逐一处理所有启动请求。 如果应用不需要同时处理多个请求,这是最好的选择。 IntentService只需实现构造函数与onHandleIntent()方法即可,onHandleIntent()方法会接收每个启动请求的Intent 3.1、继承Service 这里举一个音乐播放器的例子 继承Service类实现自定义Service,提供在后台播放音乐、暂停音乐、停止音乐的方法 publicclassMyServiceextendsService{ privatefinalStringTAG="MyService"; privateMediaPlayermediaPlayer; privateintstartId; publicenumControl{ PLAY,PAUSE,STOP } publicMyService(){ } @Override publicvoidonCreate(){ if(mediaPlayer==null){ mediaPlayer=MediaPlayer.create(this,R.raw.music); mediaPlayer.setLooping(false); } Log.e(TAG,"onCreate"); super.onCreate(); } @Override publicintonStartCommand(Intentintent,intflags,intstartId){ this.startId=startId; Log.e(TAG,"onStartCommand---startId: "+startId); Bundlebundle=intent.getExtras(); if(bundle! =null){ Controlcontrol=(Control)bundle.getSerializable("Key"); if(control! =null){ switch(control){ casePLAY: play(); break; casePAUSE: pause(); break; caseSTOP: stop(); break; } } } returnsuper.onStartCommand(intent,flags,startId); } @Override publicvoidonDestroy(){ Log.e(TAG,"onDestroy"); if(mediaPlayer! =null){ mediaPlayer.stop(); mediaPlayer.release(); } super.onDestroy(); } privatevoidplay(){ if(! mediaPlayer.isPlaying()){ mediaPlayer.start(); } } privatevoidpause(){ if(mediaPlayer! =null&&mediaPlayer.isPlaying()){ mediaPlayer.pause(); } } privatevoidstop(){ if(mediaPlayer! =null){ mediaPlayer.stop(); } stopSelf(startId); } @Override publicIBinderonBind(Intentintent){ Log.e(TAG,"onBind"); thrownewUnsupportedOperationException("Notyetimplemented"); } } 在布局中添加三个按钮,用于控制音乐播放、暂停与停止 publicclassMainActivityextendsAppCompatActivity{ @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } publicvoidplayMusic(Viewview){ Intentintent=newIntent(this,MyService.class); Bundlebundle=newBundle(); bundle.putSerializable("Key",MyService.Control.PLAY); intent.putExtras(bundle); startService(intent); } publicvoidpauseMusic(Viewview){ Intentintent=newIntent(this,MyService.class); Bundlebundle=newBundle(); bundle.putSerializable("Key",MyService.Control.PAUSE); intent.putExtras(bundle); startService(intent); } publicvoidstopMusic(Viewview){ Intentintent=newIntent(this,MyService.class); Bundlebundle=newBundle(); bundle.putSerializable("Key",MyService.Control.STOP); intent.putExtras(bundle); startService(intent); //或者是直接如下调用 //Intentintent=newIntent(this,MyService.class); //stopService(intent); } } 在清单文件中声明Service,为其添加label标签,便于在系统中识别Service android: name=".MyService" android: label="@string/app_name"/> 通过Log日志可以发现,多次点击“播放音乐”按钮,“onCreate()”方法只会在初始时调用一次,“onStartCommand(Intentintent,intflags,intstartId)”方法会在每次点击时都被调用,点击“停止音乐”按钮,“onDestroy()”方法会被调用 当中,每次回调onStartCommand()方法时,参数“startId”的值都是递增的,startId用于唯一标识每次对Service发起的处理请求 如果服务同时处理多个onStartCommand()请求,则不应在处理完一个启动请求之后立即销毁服务,因为此时可能已经收到了新的启动请求,在第一个请求结束时停止服务会导致第二个请求被终止。 为了避免这一问题,可以使用stopSelf(int)确保服务停止请求始终基于最新一次的启动请求。 也就是说,如果调用stopSelf(int)方法的参数值与onStartCommand()接受到的最新的startId值不相符的话,stopSelf()方法就会失效,从而避免终止尚未处理的请求 如果服务没有提供绑定,则使用startService()传递的Intent是应用组件与服务之间唯一的通信模式。 如果希望服务返回结果,则启动服务的客户端可以为广播创建一个PendingIntent(使用getBroadcast()),并通过启动服务的Intent传递给服务。 然后,服务就可以使用广播传递结果 当中,onStartCommand()方法必须返回一个整数,用于描述系统应该如何应对服务被杀死的情况,返回值必须是以下常量之一: START_NOT_STICKY 如果系统在onStartCommand()返回后终止服务,则除非有挂起Intent要传递,否则系统不会重建服务。 这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务 START_STICKY 如果系统在onStartCommand()返回后终止服务,则会重建服务并调用onStartCommand(),但不会重新传递最后一个Intent。 相反,除非有挂起Intent要启动服务(在这种情况下,将传递这些Intent),否则系统会通过空Intent调用onStartCommand()。 这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务) START_REDELIVER_INTENT 如果系统在onStartCommand()返回后终止服务,则会重建服务,并通过传递给服务的最后一个Intent调用onStartCommand()。 任何挂起Intent均依次传递。 这适用于主动执行应该立即恢复的作业(例如下载文件)的服务 3.2、IntentService 由于大多数启动服务都不必同时处理多个请求,因此使用IntentService类实现服务也许是最好的选择 IntentService执行以下操作: 创建默认的工作线程,用于在应用的主线程外执行传递给onStartCommand()的所有Intent 创建工作队列,用于将Intent逐一传递给onHandleIntent()实现,这样就不必担心多线程问题 在处理完所有启动请求后停止服务,因此不必自己调用stopSelf()方法 提供onBind()的默认实现(返回null) 提供onStartCommand()的默认实现,可将Intent依次发送到工作队列和onHandleIntent() 因此,只需实现构造函数与onHandleIntent()方法即可 这里举一个关于输出日志的例子 publicclassMyIntentServiceextendsIntentService{ privatefinalStringTAG="MyIntentService"; publicMyIntentService(){ super("MyIntentService"); } @Override protectedvoidonHandleIntent(Intentintent){ Bundlebundle=intent.getExtras(); if(bundle! =null){ for(inti=0;i<5;i++){ try{ Thread.sleep(200); }catch(InterruptedExceptione){ e.printStackTrace(); } Log.e(TAG,bundle.getString("key","默认值")); } } } } publicclassStartIntentServiceActivityextendsAppCompatActivity{ privateinti=1; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_start_intent_service); } publicvoidstartService(Viewview){ Intentintent=newIntent(this,MyIntentService.class); Bundlebundle=wBundle(); bundle.putString("key","当前值: "+i++); intent.putExtras(bundle); startService(intent); } } 四、绑定Service 应用组件(客户端)通过调用bindService()绑定到服务,绑定是异步的,系统随后调用服务的onBind()方法,该方法返回用于与服务交互的IBinder。 要接收IBinder,客户端必须提供一个ServiceConnection实例用于监控与服务的连接,并将其传递给bindService()。 当Android系统创建了客户端与服务之间的连接时,会回调ServiceConnection对象的onServiceConnected()方法,向客户端传递用来与服务通信的IBinder 多个客户端可同时连接到一个服务。 不过,只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来检索IBinder。 系统随后无需再次调用onBind(),便可将同一IBinder传递至其他绑定的客户端。 当所有客户端都取消了与服务的绑定后,系统会将服务销毁(除非startService()也启动了该服务) 另外,只有Activity、服务和内容提供者可以绑定到服务,无法从广播接收器绑定到服务 可以通过以下三种方法定义IBinder接口: 扩展Binder类 如果服务是供本应用专用,并且运行在与客户端相同的进程中,则应通过扩展Binder类并从onBind()返回它的一个实例来创建接口。 客户端收到Binder后,可利用它直接访问Service中可用的公共方法 使用Messenger 如需让接口跨不同的进程工作,则可使用Messenger为服务创建接口。 服务可以这种方式定义对应于不同类型Message对象的Handler。 此Handler是Messenger的基础,后者随后可与客户端分享一个IBinder,从而让客户端能利用Message对象向服务发送命令。 此外,客户端还可定义自有Messenger,以便服务回传消息。 这是执行进程间通信(IPC)的最简单方法,因为Messenger会在单一线程中创建包含所有请求的队列,这样就不必对服务进行线程安全设计 使用AIDL AIDL(Android接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行IPC。 之前采用Messenger的方法实际上是以AIDL作为其底层结构。 如上所述,Messenger会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果想让服务同时处理多个请求,则可直接使用AIDL。 在此情况下,服务必须具备多线程处理能力,并采用线程安全式设计。 如需直接使用AIDL,必须创建一个定义编程接口的.aidl文件。 AndroidSDK工具利用该文件生成一个实现接口并处理IPC的抽象类,随后可在服务内对其进行扩展 4.1、绑定服务的具体步骤: 4.1.1、扩展Binder类 如果服务仅供本地应用使用,不需要跨进程工作,则可以实现自有Binder类,让客户端通过该类直接访问服务中的公共方法。 此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效 以下是具体的设置方法: 在服务中创建一个可满足下列任一要求的Binder实例: 包含客户端可调用的公共方法 返回当前Service实例,其中包含客户端可调用的公共方法 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法 从onBind()回调方法返回此Binder实例 在客户端中,从onServiceConnecte
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android Service使用详解 Service 使用 详解