分类目录归档:四大组件之Service

四大组件之Service 第7节 ADB调试和系统自带的Service

更新时间 修改意见
2016-08-02 陈敏

第7节 ADB调试和系统自带的Service

7.1 ADB启动service

用ADB工具启动已知Service,

这里的包名就形如:com.android.xxx,例如

7.2 系统自带的Service

安卓系统当中也自带了不少的Service,比如WindowManager BluetoothManager等等等等。

要使用它们也很简单,比如,

获取到这些Service对外提供的接口以后,就可以使用这些接口了。

类似的Service还有很多很多,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

四大组件之Service 第6节 Service的使用权限

更新时间 修改意见
2016-08-02 陈敏

第6节 Service的使用权限

和所有别的应用组件一样,Service需要在AndroidManifest.xml文件中声明。

在应用的AndroidManifest.xml文件中,要给Service设置上android:exported属性,

  1. android:exported属性设置成true:可被其他应用使用,这也是Service被创建后使用的默认属性;
  2. android:exported属性设置成false:只能被自己所在的应用使用;

在对其他应用开放Service的时候,也可以设置上访问权限,只对部分应用开放使用的权限。

6.1 Service设置权限

  1. AndroidManifest.xml文件中,对要设置权限的Service设置上android:permission属性,该属性值可以任意指定一个字符串。通常使用程序的包名作为其中的一部分,这样可以避免和其他应用中的权限声明冲突。例如"com.anddle.serviceaccess

  2. AndroidManifest.xml文件中,与同级的位置,对外声明前面使用的标签,表示这个应用发布了一个叫做com.anddle.serviceaccess的权限,

    其中android:name属性的值,就是标签中设置的android:permission的值。

6.2 使用带权限的Service

假如应用B要使用应用A中带权限的Service,需要在应用B的AndroidManifest.xml中加入权限的使用,

其中,标签中设置的android:name的值,就是应用A中对外声明的那个service的权限值。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

四大组件之Service 第5节 Service的线程和工作线程

更新时间 修改意见
2016-08-02 陈敏

第5节 Service的线程和工作线程

刚开始使用Service的时候,常常会有一个误区:认为这个Service运行于应用中一个单独的线程。其实Service组件和其他组件一样,都是运行于这个应用的主线程当中的,它们都运行于同一个单一的线程。

可以把Service简单的理解成一个没有界面显示的Activity(这个比喻其实并不准确,因为Service可以直接调用WindowManager实现界面展示,不过这里暂时就这样理解吧)。

5.1 Service的工作线程

如果需要Service进行一个需要持续的、耗时的任务,还是需要给它开启一个工作线程。

既然Service进行耗时工作也要开启的单独的线程,那么为什么不让Activity代替Service的工作呢?例如从网络下载一个文件,可以让应用对应的Activity开启一个下载线程单独进行下载工作。即使用户在下载的时候想使用其他应用也没有关系,点击HOME按钮就可以了。下载文件的那个Activity会自动隐藏起来,根本不会影响到其他应用的使用。

这看上去也是可以的,不过千万不能忽略了安卓系统的回收机制。假如系统资源需要回收,操作系统有可能把隐藏起来的Activity给回收了,所以将一个需要长期运行的任务与随时可能被清除的Activity关联在一起是很危险的。因此安卓系统引入了Service组件来处理这种情况。

正在运行的Service组件被系统回收的可能性与隐藏起来的Activity相比,会低很多。它能保证长期运行的任务能够始终运行着。

但要注意的是,这里并不是说不要在Activity当中创建工作线程。在Activity当中创建工作线程,进行短时间的耗时工作(例如,访问网络读取一条数据,然后显示到界面上)是完全没有问题的。

5.2 IntentService

很多时候,启动一个Service就是要它完成一个复杂而耗时的任务,完成之后,这个Service就可以退出了。

例如文件下载。Activity界面启动负责下载的ServiceService安静的下载,下载完成了,就可以自己退出。下载的过程中,Activity可以继续显示下载的进度,也可以不用关心当前的状态而完全退出。

Android SDK为开发者准备好了这样一个现成的ServiceIntentService

IntentServiceService的子类,就是为了上面的那种使用场景而设计的。Android SDK对Service类进行了进一步的封装和扩展,方便开发者直接使用,而不需要重新造轮子了。

使用IntentService

  1. 继承IntentService,实现它的onHandlerIntent()函数。onHandlerIntent()是在工作线程中被调用的,所以可以在它当中添加耗时的任务处理,

  2. AndroidManifest.xml文件中,声明这个Service

使用它的时候,直接启动Service就可以了。

使用IntentService有几个需要注意的地方,

  1. 任务通过Intent发布,如果需要使用参数,参数可以通过Intent传递;
  2. 一旦任务执行完成,Service就退出了;
  3. 可以处理多个Intent请求,但是会按照请求的先来后到挨个处理,一个接一个完成后,如果没有任务了,Service就退出;

/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

四大组件之Service 第4节 远程调用

更新时间 修改意见
2016-08-02 陈敏

第4节 远程调用

之前提到过:如果站在Service与触发Service运行的那个组件的角度,根据它们的关系进行分类,有两种:本地Service,远程Service。

本地Service就是同一个应用的组件的调用本地应用中的Service组件;

远程Service就是其它应用的组件跨进程调用其它应用中的Service组件。

对于使用Start Service的方式远程运行Service是很简单的。和本地Service几乎完全一样,只是要采用隐式调用的方式调用。

这里主要讲讲跨进程通过Bind Service的方式远程绑定Service

跨进程调用自定义Service有两种方式:Messager和AIDL。要让两个不同的进程之间进行函数调用,就要使用进程间通信IPC,这两种方式都使用了IPC技术。在安卓系统当中,它实际上是由Binder来实现的。

4.1 AIDL实现进程间调用

  1. 在源码目录下创建一个以aidl为后缀的文件,例如IRemoteCall.aidl,将Service要提供给其他进程使用的接口函数定义在里面,例如,

    Android Studio编译器会根据AIDL接口文件,自动生成对应的java源代码。它产生的java类可以直接拿来使用。

  2. 继承Service类,创建自己的Service,并实现由IRemoteCall.aidl定义的Binder

  3. AndroidManifest.xml文件中,用隐式的方式声明新创建的Service

    这里要把android:exported属性设置成true,其他进程中的组件才能够使用它,否则只有同一个进程的组件能使用。

在另一个应用中,要远程调用Service也很简单,

  1. 创建一个ServiceConnection,当绑定Service之后在onServiceConnected()中会得到Service返回的Binder;如果Service遇到异常情况退出时,会通过onServiceDisconnected通知已经绑定过它的组件,绑定断开。

    如果用户主动解除绑定,这个onServiceDisconnected()是不会被触发的。

  2. 假设Activity A中有个按钮,点击之后就用隐式调用的方式调用bindService;还有个按钮B,点击之后就调用unbindService

可以看到,通过AIDL进行远程调用与不使用远程调用基本一样,只是它们产生和获取Binder的方式不同,

  1. 远程调用是通过AIDL产生Binder
  2. 非远程调用是通过继承Binder类产生Binder

4.2 Messenger实现进程间调用

Messenger方式是一种进程间消息传递到方式,可以让组件B发送消息M到Service,让Service根据消息的类型进行相关的操作,

  1. Service中,创建一个内部的Handler类

  2. 创建一个用来传递消息的Messenger,将Messenger作为Binder返回给调用者,今后调用者就可以用这个给Service发消息了。消息的内容在IncomingHandlerhandleMessage()函数中得到。

组件A那边在使用的时候可以,

  1. 创建一个ServiceConnection,当绑定Service之后在onServiceConnected()中会得到Service返回的Binder;如果Service遇到异常情况退出时,会通过onServiceDisconnected通知已经绑定过它的组件,绑定断开。

  2. 假设Activity A中有个按钮,点击之后就用隐式调用的方式调用bindService;还有个按钮B,点击之后就调用unbindService

这样一来,组件A就可以向Service发送消息了。Service一收到消息,就会根据消息的类型,去执行对应的操作了。

可以看出,

  1. 因为对Service操作的请求是通过Handler进行的,所以组件们请求都会按照先来后到一个一个顺序执行;
  2. 只有其它组件可以向Service发送执行某个操作的消息,而Service无法主动回报数据。

4.3 AIDL与Messenger怎么选

使用Messenger要比使用AIDL更简单,Messenger 会将所有调用排入队列,按照顺序一个一个执行;而AIDL方式允许多个组件同时向Service发送请求,所以Service需要考虑同步的问题。

对于大多数应用,Service不需要执行多线程处理,不需要数据的主动回报,因此使用Messenger可让服务一次处理一个调用;否则就使用AIDL方式吧。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

四大组件之Service 第3节 Service的生命周期

更新时间 修改意见
2016-08-02 陈敏

第3节 Service的生命周期

3.1 Service的生命状态

Activity类似,Service也是有生命周期的。在实现一个Service的时候,可以覆盖它的生命周期函数,当它进入不同生命状态的时候,这些函数会被触发,我们就可以在它不同的生命阶段做不同的逻辑操作。

因为Service不像Activity有需要显示的界面,所以它的状态少了很多,没有onResume() onPause()等等与界面显示相关的状态。

这里面的onStartCommand()函数与onUnbind()函数要特别注意,它们的返回值有特别的意义,会影响到Service的生命周期,我们会在最后单独解释。

3.2 生命周期

通过startService()运行的Service与通过bindService()运行的Service相比较,它们的生命周期有所不同。

假设Service-MyService 还没有被启动过,我们来看看它被一个或多个组件使用startService() stopService() bindService() unbindService()这些方法后,生命周期会怎么变化。

组件名称 启动Service的方法 关闭Service的方法
A startService() stopService()
B startService() stopService()
C bindService() unbindService()
D bindService() unbindService()

3.2.1 Start Service的生命周期

对于只使用startService()stopService()来控制Service的情况:

  1. 情况1

  2. 情况2

  3. 情况3

  4. 情况4

可以看出,

  1. 任意组件调用startService()后,如果这个Service还没有创建,系统会首先创建-onCreate()

  2. 之后触发onStartCommand()

  3. 任意组件调用stopService()后,如果这个Service还没有被销毁,系统会销毁-onDestroy()

3.2.2 Bind Service的生命周期

onStartCommand()函数与onUnbind()函数也会对生命周期有影响,所以这里我们假设使用的是默认的返回值,

组件触发Service运行的bindService()函数也会对生命周期有影响--第三个参数,所以这里我们假设使用Context.BIND_AUTO_CREATE

那么,对于只使用bindService()unbindService()来控制Service的情况:

  1. 情况1

  2. 情况2

  3. 情况3

  4. 情况4

可以看出,

  1. 任意组件调用bindService()后,如果这个Service还没有创建,系统会首先创建-onCreate()

  2. 之后触发onBind()

  3. 之后组件的onServiceConnected()被触发,告知绑定成功;

  4. 任意已绑定的组件调用unbindService()后,会触发onUnbind()

  5. 如果这个Service没有与其他的组件绑定,那么系统会销毁-onDestroy()

3.2.3 混合模式下的生命周期

对于使用了startService() stopService()bindService()unbindService()等两种方式来控制Service的情况:

  1. 情况1

  2. 情况2

  3. 情况3

  4. 情况4

可以看出,当一个Service被创建-onCreate()之后;如果任意组件想要销毁-onDestroy()这个Service;必须等到Service与它关联的所有组件切断联系(通过stopService()或者unbindService())才行。

3.3 其它因素对生命周期的影响

Service生命周期的影响还有其它的因素。

3.3.1 unbind()函数返回值

自定义Service时,unbind()函数的返回值对Service的生命周期是有影响的。它会决定onRebind()函数是否被调用。

前面我们都看到了在unbind()返回falseService生命周期的变化。在unbind()返回true时,它的周期变化如下,

也就是说,假如一个Service正在运行,此时有个组件C要绑定它,那么会触发onBind()函数;如果C解除绑定,然后又再次绑定,那么不会触发onBind()而是触发onRebind()函数。

3.3.2 bindService函数的参数

在绑定Service的时候,通常使用的Context.BIND_AUTO_CREATE参数,

如果不使用Context.BIND_AUTO_CREATE,那么一个Service在没有被运行起来之前,使用bindService()是不会让Service自动运行起来的。

但是如果设置上了这个参数,那么就会让它运行起来,然后进行绑定。

除了Context.BIND_AUTO_CREATE参数,还有好些参数可以使用,

可以用的方式同时使用其它的标志,例如,

3.3.3 onStartCommand函数返回值

前面的onStartCommand()返回值使用的是START_STICKY,它对Service的周期都是有影响的。

Service启动之后,如果因为某种意外而停止运行(例如系统回收了该Service),那么系统要自动的把这个Service再运行起来。这个时候,onStartCommand()的返回值就决定了Service重启的行为,

  1. START_STICKYService重启(onCreate())之后,会触发onStartCommand(),但是此时onStartCommand()的传入的参数Intent会变成空值,

  2. START_NOT_STICKYService将不会被自动重启,所以也就不会触发onStartCommand()

  3. START_REDELIVER_INTENTService重启(onCreate())之后,会触发onStartCommand(),此时onStartCommand()的参数Intent不会变成空值。该Intent将是以前想要启动这个ServiceIntent;如果以前有多个Intent想要启动它,那么这里会传入最后一个,也就是最近的一个;


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

四大组件之Service 第2节 Start Service和Bind Service

更新时间 修改意见
2016-08-02 陈敏

第2节 Start Service和Bind Service

我们先从触发Service运行的角度来认识Service,有了整体的认识之后,我们再来看看远程Service和本地Service。

2.1 Start Service

其他组件通过调用startService()函数将Service运行起来,再通过调用stopService()函数让其停止运行。

单纯的使用这种形式的Service最为简单,它和它的调用者之间没有什么联系,调用者只是负责启动它和停止它,或者在启动它的时候通过Intent传递一点数据,除此之外,两者没有数据交换、没有其他的功能调用,这两个组件之间基本上互不影响。

设计这样的一个Service需要,

  1. 继承Android SDK提供的Service类,重写onBind()函数,让它返回空值;

  2. AndroidManifest.xml中,声明新创建的Service

使用这种Service也很简单。假设Activity A中有个按钮start,点击之后就调用startService;还有个按钮B-stop,点击之后就调用stopService

这里运行Service的时候,是通过Intent明确指定被运行的Service。这种明确指定启动哪个Service的方式叫做Service的显示调用。与之对应的还有隐式调用。我们稍后来详细介绍。

2.2 Bind Service

其他组件通过调用bindService()绑定Service,让它运行起来;再通过调用unbindService()解除绑定。

这种形式的Service与调用者之间通常有功能调用或者频繁的数据交换,调用者会向Service发出请求让Service进行特定的操作,并返回结果。

设计这样的一个Service需要,

  1. 继承Android SDK提供的Service类,

  2. 实现一个自定义的Binder,让它这个继承Binder类。

    Binder可以将Service与调用者联系起来,在Binder中提供的方法,就是Service对外提供的方法。

    组件和Service之间的调用是通过Binder来进行的。我们可以把Binder看作是一个连接其他组件和Service的桥梁,它的实现原理是什么,我们暂时不用去关心,只要知道这样用就可以了。

  3. AndroidManifest.xml中,声明新创建的Service

其他组件使用这个Service的时候,

  1. 创建一个ServiceConnection,当绑定Service之后,在onServiceConnected()中会得到Service返回的Binder;如果Service遇到异常情况退出时,会通过onServiceDisconnected()通知绑定它的组件。

    获得了MyService.MyServiceIBinder之后,我们就可以向调用普通函数那样,调用到Service对外提供的接口函数了。

    需要注意的是,如果用户主动解除绑定,onServiceDisconnected()是不会被触发的。

  2. 假设Activity A中有个按钮,点击之后就调用bindService;还有个按钮B,点击之后就调用unbindService

    这里同样采用的是对Service的显示调用。

2.3 混合模式

Service并不是只能给一个组件使用,它可以同时服务于多个组件。
所以一个Service既可以是Start Service,也可以是Bind Service。只要把两者需要实现的地方都实现了就行。组件A可以通过startService()运行一个Service,组件B可以通过bindService()再次运行同一个Service

2.4 显式调用和隐式调用

Activity一样,Service有两种被调用的方式,显式调用和隐式调用。

2.4.1 显式调用

通过Intent明确的指定要运行哪个Service,这就是显式调用。
就好像给指定的个人发货,写下接收方具体的名字和地址。

设计显式调用,

通常,应用中的组件启动自己应用中的Service,可以采用这样的方式,

2.4.2 隐式调用

如果是应用A的组件要使用应用B的Service,通常会使用到隐式调用,不明确的告知要启动的Service是什么,而是通过IntentAction Name让操作系统自己匹配最合适的响应者。

就好像给某个机构发货,只需要发送给某个机构,但不需要知道这个机构中具体是哪个人。

设计隐式调用,

  1. AndroidManifest.xml文件中为这个Service指定一个过滤器,为过滤器指定一个Action name

  2. 应用A的组件,要使用这个Service的时候,就可以,

注意,对于Android5.0版本的以上的系统上要设置Intent发送目标的包名。因为从Android5.0开始,不允许隐式启动Service了。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

四大组件之Service 第1节 Service介绍

更新时间 修改意见
2016-08-02 陈敏

第1节 Service介绍

Service是安卓系统的四大组件之一。如果说Activity是专门为用户“看”的系统组件,那Service就是隐藏在角落默默付出的系统组件。

最典型的例子就是音乐播放器。音乐播放器应用中,播放音乐的组件就是一个Service。音乐播放器的界面就是一个提供了音乐控制方式的Activity,当我们点击Activity上的播放按钮之后,Activity通知播放器的Service组件,让Service组件开始播放音乐;之后即使用户选择退出了音乐播放器的Activity界面,音乐仍然在被播放着,并没有随着界面的退出而停止播放。

一个应用要拥有与用户交互的界面,它就要使用Activity组件;一个应用不需要与用户交互,只要在看不见的地方默默工作,它就要使用Service组件;不过只有很少的应用只会单独使用Service组件。

Service按照创建的方式进行分类,有两种:启动Service-start Service,绑定Service-bind Service。前者使用startService()运行,后者使用bindService()运行。

如果站在Service与触发Service运行的那个组件的角度区分,Service可以分成两种:本地Service,远程Service。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/