Service 详细解析

Service 是 Android 中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service 默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在 UI 线程中,因此,不要在 Service 中执行耗时的操作,除非你在 Service 中创建了子线程来完成耗时操作

Service 的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开另一个应用程序,Service 仍然能够保持正常运行,这也正是 Service 的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的 Service 也会停止运行。

Service的生命周期

Service lifecycle

通过这个图可以看到,两种启动 Service 的方式以及他们的生命周期, bindService 的不同之处在于当绑定的组件销毁后,对应的 Service 也就被 Kill 了。

  • 被启动的服务的生命周期

    一个 Service 被使用 startService 方法启动,不管是否调用了 bindService(绑定服务)或 unbindService (解除绑定服务)到该 Service ,该 Service 都会在后台运行并不受影响。

    一个 Service 被使用 startService 方法启动多少次,onCreate 方法只会调用一次,onStartCommand 方法将会被调用多次(与startService的次数一致),且系统只会创建一个 Service 实例(结束该 Service 也只需要调用一次 stopService ),该 Service 会一直在后台运行,直至调用 stopService 或调用自身的 stopSelf 方法。

    注:在系统资源不足的情况下,服务有可能被系统结束(Kill);

  • 被绑定的服务的生命周期

    如果一个 Service 在某个 Activity 中被调用 bindService 方法启动,不论 bindService 被调用几次, Service 的 onCreate 方法只会执行一次,同时 onStartCommand 方法始终不会调用。
      
    当建立连接后,Service会一直运行,除非调用 unbindService 来接触绑定、断开连接或调用该 Service 的 Context 不存在了(如 Activity 被 Finish ——即通过 bindService 启动的 Service 的生命周期依附于启动它的 Context ),系统在这时会自动停止该

  • 被启动又被绑定的服务的生命周期

    当一个 Service 在被启动 (startService) 的同时又被绑定 (bindService) ,该 Service 将会一直在后台运行,并且不管调用几次, onCreate 方法始终只会调用一次, onStartCommand 的调用次数与 startService 调用的次数一致(使用 bindService 方法不会调用 onStartCommand )。同时,调用 unBindService 将不会停止 Service ,必须调用 stopService 或 Service 自身的 stopSelf 来停止服务。

  • 当服务被停止时

    当一个服务被终止(stopService、stopSelf、unbindService)时,onDestory 方法将会被调用——所以我们需要在该方法中清除一些工作(依附该 Service 生命周期的,如:停止在 Service 中创建并运行的线程)。

特别注意:

  1. 在使用 startService 方法启动服务后,一定要调用 stopService 方法来停止该服务(同上,可以在 Activity 的onDestory 中来停止服务);
  2. 在某处调用 bindService 绑定 Service 的时候,要在对应的某处调用 unbindService 来解除绑定(如在 Activity 中绑定了 Service ,可以在 onDestory 中来解除绑定——虽然绑定的 Service 会在 Activity 结束时自动解除、停止);
  3. 如果同时使用 startService 与 bindService 方法启动 Service ,需要终止该 Service 时,要调用 stopService 和 unbindService 方法( unbindService 依附于启动它的 Context ,startServicec 并不依附于启动它的 Context 。如果先调用 unbindService ,这时服务并不会被终止,当调用 stopService 后,服务才会被终止;如果先调用 stopService ,服务也不会被终止,当调用 unbindService 或者之前调用 bindService 的 Context 不存在了(如 Activity 被 finish 掉了)服务才会自动停止);
  4. 当手机屏幕发生旋转时,如果 Activity 设置的是自动旋转的话,在旋转的过程中, Activity 会重新创建,那么之前通过 bindService 建立的连接便会断开(之前绑定该服务的 Context 不存在了),服务也会被自动停止。

Service 使用

在新建一个 Service 后,记得在 AndroidManifest.xml 中注册 Service ,在 application 内添加需要注册的 Service 信息:

<service
    android:name=".service.PlayerService"
    android:label="PlayerService"
    android:exported="true" />

AndroidManifest.xml中Service元素常见属性:

  • andorid:name

    服务类名。可以是完整的包名 + 类名。也可使用.代替包名。

  • adroid:exported

    其他应用能否访问该服务,如果不能,则只有本应用或有相同用户 ID 的应用能访问。默认为 false 。

  • android:enabled

    标识服务是否可以被系统实例化。 true --系统默认启动, false --不启动。(默认值为 true )

  • android:label

    显示给用户的服务名称。如果没有进行服务名称的设置,默认显示服务的类名。

  • android:process

    服务所运行的进程名。默认是在当前进程下运行,与包名一致。如果进行了设置,将会在包名后加上设置的集成名。
    如果名称设置为冒号 :开头,一个对应用程序私有的新进程会在需要时和运行到这个进程时建立。如果名称为小写字母开头,服务会在一个相同名字的全局进程运行,如果有权限这样的话。这允许不同应用程序的组件可以分享一个进程,减少了资源的使用。

  • android:icon

    服务的图标。

  • android:permission

    申请使用该服务的权限,如果没有配置下相关权限,服务将不执行,使用 startService() 、 bindService() 方法将都得不到执行。

Service 种类

服务是一个应用程序组件,可以在后台执行长时间运行的操作,不提供用户界面。一个应用程序组件可以启动一个服务,它将继续在后台运行,即使用户切换到另一个应用程序。此外,一个组件可以绑定到一个服务与它交互,甚至执行进程间通信 (IPC) 。例如,一个服务可能处理网络通信、播放音乐、计时操作或与一个内容提供者交互,都在后台执行。总共可分为后台服务前台服务IntentService跨进程服务(远程服务)、无障碍服务、**系统服务 **。

  1. 后台服务

后台服务可交互性主要是体现在不同的启动服务方式, startService() 和 bindService() 。 bindService() 可以返回一个代理对象,可调用 Service 中的方法和获取返回结果等操作,而 startService() 不行。

  • 不可交互的后台服务

    不可交互的后台服务即是普通的 Service , Service 的生命周期很简单,分别为 onCreate、onStartCommand、onDestroy 这三个。当我们 startService() 的时候,首次创建 Service 会回调 onCreate() 方法,然后回调 onStartCommand() 方法,再次 startService() 的时候,就只会执行一次 onStartCommand() 。服务一旦开启后,我们就需要通过 stopService() 方法或者 stopSelf() 方法,就能把服务关闭,这时就会回调 onDestroy() 。

  • 可交互的后台服务

    可交互的后台服务是指前台页面可以调用后台服务的方法,可交互的后台服务实现步骤是和不可交互的后台服务实现步骤是一样的,区别在于启动的方式和获得 Service 的代理对象。区别在于多了一个 ServiceConnection 对象,该对象是用户绑定后台服务后,可获取后台服务代理对象的回调,我们可以通过该回调,拿到后台服务的代理对象,并调用后台服务定义的方法,也就实现了后台服务和前台的交互。

  1. 前台服务

由于后台服务优先级相对比较低,当系统出现内存不足的情况下,它就有可能会被回收掉,所以前台服务就是来弥补这个缺点的,它可以一直保持运行状态而不被系统回收。例如:墨迹天气在状态栏中的天气预报。

前台服务创建很简单,其实就在 Service 的基础上创建一个 Notification ,然后使用 Service 的 startForeground() 方法即可启动为前台服务

startService(new Intent(this, ForegroundService.class));

  1. IntentService

IntentService 是专门用来解决 Service 中不能执行耗时操作这一问题的,创建一个 IntentService 也很简单,只要继承 IntentService 并覆写 onHandlerIntent 函数,在该函数中就可以执行耗时操作了。

public class MyIntentService extends IntentService {
    public MyIntentService(String name) {
      super(name);
    }
    
    @Override
    protected void onHandleIntent(Intent intent) {
      // 在这里执行耗时操作
    }      
}

在 IntentService 内有一个工作线程来处理耗时操作,当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,执行完自动结束。

  1. 跨进程服务(远程服务)

远程服务为独立的进程,对应进程名格式为所在包名加上你指定的 android:process 字符串。由于是独立的进程,因此在 Activity 所在进程被 Kill 的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。由于是独立的进程,会占用一定资源,并且使用 AIDL 进行 IPC 稍微麻烦一点,这种 Service 是常驻的。

> 关于 AIDL 推荐大家看看[Android使用AIDL实现跨进程通讯(IPC)](http://blog.csdn.net/ydxlt/article/details/50812559)
  1. AccessibilityService无障碍服务

    无障碍服务旨在帮助身心有障碍的用户使用 Android 设备和应用。无障碍服务在后台运行,当无障碍事件被激活时系统会执行 AccessibilityService 的 onAccessibilityEvent(AccessibilityEvent event) 方法。这些事件表示在用户界面中的一些状态的改变,例如:焦点的改变、按钮被点击等。这类服务可以有选择性地请求查询活动窗口的内容。无障碍服务的开发需要继承 AccessibilityService 和实现它的抽象方法。
    跟详细的介绍推荐阅读 Android 无障碍辅助功能AccessibilityService(1)

  2. 系统服务

    系统服务提供了很多便捷服务,可以查询 Wifi 、网络状态、查询电量、查询音量、查询包名、查询 Application 信息等等等相关多的服务,具体大家可以自信查询文档,这里举例几个常见的服务:

    • 判断Wifi是否开启

      WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
              boolean enabled = wm.isWifiEnabled();
      
    • 获取系统最大音量

      AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
              int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
      
    • 获取当前音量

      AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
              int current = am.getStreamMaxVolume(AudioManager.STREAM_RING);
      
    • 判断网络是否有连接

      ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
              NetworkInfo info = cm.getActiveNetworkInfo();
              boolean isAvailable = info.isAvailable();
      
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,783评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,396评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,834评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,036评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,035评论 5 362
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,242评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,727评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,376评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,415评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,463评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,140评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,734评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,809评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,028评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,521评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,119评论 2 341

推荐阅读更多精彩内容