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,必须在清单文件中对其进行声明

声明方式是添加 < service > 元素作为 < application > 元素的子元素

例如

  <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service android:name=".MyService" />
</application>

android:name 属性是唯一必需的属性,用于指定服务的类名,还可将其他属性包括在 < service > 元素中以定义一些特性

为了确保应用的安全性,最好始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。 启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样可以充分消除目标服务的不确定性

此外,还可以通过添加 android:exported 属性并将其设置为 “false“,确保服务仅适用于本应用。这可以有效阻止其他应用启动本应用内的服务,即便在使用显式 Intent 时也是如此

  • Service包含的属性有
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
</service>
属性 说明
description 对服务进行描述,属性值应为对字符串资源的引用,以便进行本地化
directBootAware 设置是否可以在用户解锁设备之前运行,默认值为“false”
enabled 设置是否可以由系统来实例化服务。< application >元素有自己的enabled属性,适用于包括服务在内的所有应用程序组件。要启用服务,< application >和< service >属性必须都为“true”(默认情况下都为true)。如果其中一个是“false”,则服务被禁用
exported 设置其他应用程序的组件是否可以调用本服务或与其交互,如果可以,则为“true”。当值为“false”时,只有同一个应用程序或具有相同用户ID的应用程序的组件可以启动该服务或绑定到该服务。该属性的默认值取决于服务是否包含Intent filters。没有任何过滤器意味着它只能通过指定其确切的类名来调用,这意味着该服务仅用于应用程序内部使用(因为其他人不知道类名)。所以在这种情况下,默认值为“false”。 另一方面,如果存在至少一个过滤器,意味着该服务打算供外部使用,因此默认值为“true”
icon 服务的图标,属性值应是对drawable资源的引用。如果未设置,则将使用应用程序图标
isolatedProcess 设置该服务是否作为一个单独的进程运行,如果设置为true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限,与它唯一的通信是通过服务API(绑定和启动)
label 可以向用户显示的服务的名称,属性值应是对字符串资源的引用
name 服务类的完全限定名
permission 设定组件必须具有的权限,得以启动服务或绑定服务。如果startService(),bindService()或stopService()的调用者没有被授予此权限,则该方法将不会工作,并且Intent对象不会传递到服务中
process 用来运行服务的进程的名称。通常,应用程序的所有组件都运行在应用程序创建的默认进程中,它与应用程序包名具有相同的名称。 < application >元素的process属性可以为所有组件设置不同的默认值,但组件可以使用自己的进程属性覆盖默认值,从而允许跨多个进程扩展应用程序

三、启动Service

启动服务由组件通过调用 startService() 启动,服务启动之后,其生命周期即独立于启动它的组件,并且可以在后台无限期地运行,即使启动服务的组件已被销毁也不受影响。因此,服务应通过调用 stopSelf() 来自行停止运行,或者由另一个组件调用 stopService() 来停止

可以通过扩展两个类来创建启动服务:

  • Service

    这是所有服务的父类。扩展此类时,如果要执行耗时操作,必须创建一个用于执行操作的新线程,因为默认情况下服务将运行于UI线程
  • IntentService

    这是 Service 的子类,它使用工作线程逐一处理所有启动请求。如果应用不需要同时处理多个请求,这是最好的选择。IntentService只需实现构造函数与 onHandleIntent() 方法即可,onHandleIntent()方法会接收每个启动请求的 Intent
3.1、继承Service

这里举一个音乐播放器的例子

继承Service类实现自定义Service,提供在后台播放音乐、暂停音乐、停止音乐的方法

public class MyService extends Service {

    private final String TAG = "MyService";

    private MediaPlayer mediaPlayer;

    private int startId;

    public enum Control {
PLAY, PAUSE, STOP
} public MyService() {
} @Override
public void onCreate() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.music);
mediaPlayer.setLooping(false);
}
Log.e(TAG, "onCreate");
super.onCreate();
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.startId = startId;
Log.e(TAG, "onStartCommand---startId: " + startId);
Bundle bundle = intent.getExtras();
if (bundle != null) {
Control control = (Control) bundle.getSerializable("Key");
if (control != null) {
switch (control) {
case PLAY:
play();
break;
case PAUSE:
pause();
break;
case STOP:
stop();
break;
}
}
}
return super.onStartCommand(intent, flags, startId);
} @Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
super.onDestroy();
} private void play() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
} private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
} private void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
stopSelf(startId);
} @Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
throw new UnsupportedOperationException("Not yet implemented");
}

点击播放音乐后,状态栏就出现了一个通知

当中,提供给 startForeground() 的整型参数不得为 0。要从前台移除服务,需调用 stopForeground()方法,此方法不会停止服务。 但是,如果前台服务被停止,则通知也会被移除

六、Service的生命周期

服务生命周期从创建到销毁可以遵循两条不同的路径:

  • 启动服务

    该服务在其他组件调用 startService() 时创建,然后无限期运行,必须通过调用 stopSelf() 来自行停止运行或通过其他组件调用 stopService() 来停止服务。服务停止后,系统会将其销毁
  • 绑定服务

    服务在另一个组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务(服务不必自行停止运行)

这两条路径并非完全独立。也就是说,可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则 stopService() 或 stopSelf() 不会实际停止服务

Service使用详解的更多相关文章

  1. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  2. 【转载】Android Studio Service AIDL 详解

    公司产品之前IM这块存在很多问题,消息到达率低,加上协议上有些问题,丢消息频繁,所以需要重构IM,AIDL不能解决以上问题.好吧!那AIDL可以解决什么问题?什么是AIDL? 什么是AIDL? AID ...

  3. Kubernetes K8S之Service服务详解与示例

    K8S之Service概述与代理说明,并详解所有的service服务类型与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master Cent ...

  4. Service通信详解

    1.使用Intent进行异步通讯 在Service任务一旦完成后,就发送广播.开发者只需要实现一个BroadcastReceiver来监听响应既可. Activity.startService启动in ...

  5. [Android] Service服务详解以及如何使service服务不被杀死

    排版上的细节有些不好看,主要是我用的MarkDown编辑器预览和这里的不一样,在那个上面的样式很舒服.这里要改的地方太多就不想改了,将就看吧.下次写的时候注意.还有看到错误给我提啊. 本文链接:htt ...

  6. linux centos service 参数详解

    Service文件 开门见山,直接来看两个实际的服务配置文件吧. 第一个配置是 CoreOS 系统中 Docker 服务的 Unit 文件,路径是 /usr/lib/systemd/system/do ...

  7. Android中Service 使用详解(LocalService + RemoteService)

    Service 简介: Service分为本地服务(LocalService)和远程服务(RemoteService): 1.本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外L ...

  8. Android AIDL SERVICE 双向通信 详解

    http://www.cnblogs.com/punkisnotdead/p/5062631.html 起因 是这个blog 提到了 用webview 的时候 用开启子进程的方式 可以极大避免内存泄露 ...

  9. Windows Service 之 详解(一)

    一.Windows 服务简介 Windows 服务是可以在系统启动时自动打开的(不需要任何人登录计算机)的程序. 1.适合创建Windows 服务的场景: [1] 在没有用户交互操作的情况下运行程序: ...

  10. Metadata Service 架构详解 - 每天5分钟玩转 OpenStack(165)

    下面是 Metadata Service 的架构图,本节我们详细讨论各个组件以及它们之间的关系. nova-api-metadata nova-api-metadata 是 nova-api 的一个子 ...

随机推荐

  1. 使用vim编写hexo文档,并用ultisnips/snipmates/snippets插件补全

    作为一个vim使用者,编写markdown文档时若不能用vim这怎么能受的了! 下面是我编写markdown的时候用到的插件 Plugin 'Markdown'Plugin 'Markdown-syn ...

  2. 个人怎么申请微信小程序

    1.打开微信公众平台(mp.weixin.qq.com).拉到中间的"账号分类",鼠标悬浮于"小程序"框中并点击"查看详情". 2.进入微信 ...

  3. [mysql使用(1)] 64位Linux下安装mysql-5.7.13-linux-glibc2.5-x86_64

    由于公司临时让将Oracle的数据移植到mysql上面,所以让我在公司服务器上面安装一下mysql.下面就是我的安装过程以及一些错误解决思路.其实对于不同版本安装大体都有差不多. 1. 从官网下载 m ...

  4. .NET程序集引用COM组件MSScriptControl所遇到的问题

    问题描述:为了在C#中执行js脚本,在一个目标平台编译为Any Cpu的.NET程序集中引用了MSScriptControl组件,在winform程序中,调用这个程序集中的执行js的方法,没有任何问题 ...

  5. django-rest-framework之序列化

    前言:昨天学习了rest-framework序列化方面的知识,故写了博客记录一下.官网:http://www.django-rest-framework.org/tutorial/1-serializ ...

  6. [译]ASP.NET Core 2.0 路由引擎之网址生成

    问题 如何在ASP.NET Core 2.0中由路由引擎来生成网址? 答案 新建一个空项目,修改Startup.cs文件,添加MVC服务和中间件: public void ConfigureServi ...

  7. 学习笔记 intent属性

    Android开发学习笔记:Intent的简介以及属性的详解 2011-08-08 17:20:48 标签:Intent 移动开发 Android 休闲 详解 原创作品,允许转载,转载时请务必以超链接 ...

  8. 用ajax的同步请求解决登陆注册需要根据服务器返回数据判断是否能提交的问题

    最近在写www.doubilaile.com的登陆注册.需要用ajax请求服务器判断用户名是否存在,用户名和密码是否匹配,进而提交数据.碰到的问题是异步请求都能成功返回数据,但是该数据不能作为紧接着的 ...

  9. tarjan求强连通分量+缩点+割点以及一些证明

    “tarjan陪伴强联通分量 生成树完成后思路才闪光 欧拉跑过的七桥古塘 让你 心驰神往”----<膜你抄>   自从听完这首歌,我就对tarjan开始心驰神往了,不过由于之前水平不足,一 ...

  10. POJ 3468 A Simple Problem with Integers(树状数组区间更新) 续

    这个题刚开始的时候是套模板的,并没有真的理解什么树状数组的区间更新,这几天想了一下,下面是总结: 区间更新这里引进了一个数组delta数组,delta[i]表示区间 [i, n] 的共同增量,每次你需 ...