背景

先从日常用户体验说起,用过苹果的iOS系统都知道,凡是音频播放,在下滑菜单都能看到是哪个应用在播放,音频的标题,用户还可以直接在下滑菜单操作,而安卓手机则不然,因为Android系统使用本文介绍的Service进行后台音乐播放,而Service不提供和用户交互接口,因此在安卓手机上,当用户打开音乐程序并后台播放音乐,想关闭音乐只有再打开该播放音乐程序或者直接杀死所有程序才能关闭音乐,如果用户手机上有多个音乐程序(国内用户大概率会这样),有时候可能会忘记打开的是哪个音乐播放程序,勤快点的一个一个打开播放程序,哦,对了,顺便得看一遍开屏广告,懒一点的直接杀死所有程序,不过,有时候直接杀死所有程序方式也没用。

导致这些不好的用户体验,多是因为使用Service不当所致,为了更好的使用Service,还是得对其进去了解。

1.什么是Service

官方对Service定义如下:

服务是可以在后台执行长时间运行的操作的应用组件,并且不提供用户界面。另一个应用程序组件可以启动服务,并且即使用户切换到另一个应用程序时,Service也可以在后台继续运行。此外,组件可以绑定到服务以与其进行交互,甚至可以执行进程间通信(IPC)。例如,一项服务可以从后台处理网络事务,播放音乐,执行文件I/O或与内容提供者进行交互。

从官方的定义,我们可以总结出Service的特性

  • Service是没有UI的Android组件,用户是无法直接和其进行交互;
  • Service用于在后台执行长时间运行的操作。除非明确停止或销毁服务,否则它们将无限期运行,直到系统资源短缺而被Android终止;
  • Service可以由任何其他应用程序组件启动。组件甚至可以实际上绑定到服务以执行进程间通信;

官方文档还提到Service不是什么:

  • Service不是独立的进程。除非另有说明,Service并没有自己独立的进程,而是和所属的应用程序在同一进程中运行。Service是以一个独立程序的形式安装,这样容易让人误认为Service在运行的时候也是一个独立的程序,因此有自己的进程,实际上不是的,Service是在调用它的程序的进程里;
  • Service也不是线程,意味着启动一个Service并没开启另外一个线程。

2.什么时候使用Service

Service的特性是在后台运行,因此去执行需要长期运行的任务都可采用Service,这样任务放在后台运行,用户可以去做其他操作,例如开始提到的音乐播放,还有下载文件,或者提供外置设备的接口,例如外置打印机调用接口等等。

3.如何使用Service

3.1 Service生命周期

不同于Activity,Service具有两种生命周期形式,分别成为Started状态和Bound状态,两种状态生命周期见图1,他们的区别如下;

  • Started

    其他组件通过startService启动Service,一旦启动后,Service可以无限地运行下去,除非Service自身调用stopSelf()方法或者其他组件调用stopService()方法来停止它,当Service被停止时,系统会销毁它,这种状态下,Service和调用它的组件相对独立,一般情况下,两者之间没有太多的交互,形同陌路。Android手机常发生的程序关闭了音乐还在播放的窘态正是Service进入这个状态所导致的。

  • Bound

    其他组件调用bindService来创建,与Started状态不同的是,在这个状态下,组件可以通过IBinder接口和Service进行通信,组件想解绑,则通过unbindService()方法来关闭连接,一个Service可以同时和多个客户绑定,当多个组件都解除绑定之后或者组件销毁后,系统会销毁。和Started形同陌路的状态相比,Bound状态下Service和调用它的组件不仅感情亲密,而且进退共存亡,播放音频的程序如果bindService()调用Service,则不会出现程序已经销毁,音频还在播放的情况。

问题1.什么时候使用startService启动Service比较合适, 什么时候使用bindService绑定Service比较合适?

  • 如果只是要启动Service长时间在后台执行一个任务,期间不需要和它交互,则使用startService比较合适;
  • 如果启动service之后要与它进行交互则使用bindService比较合适;

问题2.想让Service长期在后台执行,期间又想和其进行交互怎么办?

这个需求在日常使用中经常碰到,例如在后台播放音乐时,想获取当前播放音乐的信息。这个时候,可以两种开启Service方式混着用,先startService,然后再bindService。在这种情况下,要终止Service,必须调用了stopSelf或者某个组件调用了stopService,而且所有绑定到该Service的组件都已解绑或者被销毁,理解起来并不复杂,即有怎么样的开始,就得怎么样结束,即以startService开始,必须使用stopSelf或者stopService结束,使用bindService开始,必须使用unbindService结束。

3.2 代码示例

3.2.1 startService方式

1.继承Service定义一个MyService类,并重写方法父类的方法

package com.pm.servicedemo;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;public class MyService extends Service {public static final String TAG = "MyService";@Overridepublic void onCreate() {super.onCreate();Log.d(TAG, "执行onCreate()");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "执行onStartCommand()");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();Log.d(TAG, "执行onDestroy()");}@Overridepublic IBinder onBind(Intent intent) {return null;}}

2.在AndroidManifest.xml中注册Service,否则无法使用Service

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.pm.servicedemo"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>//在这里注册Service服务<service android:name=".MyService"></service></application></manifest>

3.修改activity_main.xml布局文件,在其中添加两个按钮,分别用于开启Service和停止Service

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tvLog"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btnStartService"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="104dp"android:layout_marginTop="36dp"android:text="开启Service"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btnStop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="35dp"android:layout_marginEnd="79dp"android:text="停止Service"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

4.在MainActivity添加开启和停止Service逻辑,具体来说,使用Intent对象来开启和停止Service

package com.pm.servicedemo;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener {Button bStart;Button bStop;TextView tvShow;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvShow=(TextView)findViewById(R.id.tvLog);bStart = (Button) findViewById(R.id.btnStartService);bStop = (Button) findViewById(R.id.btnStop);bStart.setOnClickListener(this);bStop.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btnStartService:tvShow.setText("启动服务");//构建Intent对象,并调用startService()启动ServiceIntent startIntent = new Intent(this, MyService.class);startService(startIntent);break;case R.id.btnStop:tvShow.setText("停止服务");//构建Intent对象,并调用stopService()停止ServiceIntent stopIntent = new Intent(this, MyService.class);stopService(stopIntent);break;default:break;}}
}

完成以上步骤后,这样就可以开始运行了,界面效果如下:

1.点击开启时,logcat打印的log如下:

com.pm.servicedemo D/MyService: 执行onCreate()
com.pm.servicedemo D/MyService: 执行onStartCommand()

此时Service已经创建,如果此时再次点击开启,则只执行onStartCommand,不会再执行onCreate(),logcat只增加一行打印信息:

com.pm.servicedemo D/MyService: 执行onStartCommand()

2.点击停止时,logcat打印的log如下:

com.pm.servicedemo D/MyService: 执行onDestroy()

以上是startService创建Service方式,接下来看bindService方式。

3.2.2 bindService方式

1.在已创建MyService基础上,新建一个子类继承自Binder类、重写父类的onBind和onUnbind,以提供给Activity绑定该服务,并实现一个虚拟的播放音乐函数。

package com.pm.servicedemo;import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;public class MyService extends Service {public static final String TAG = "MyService";private MyBinder mBinder = new MyBinder();@Overridepublic void onCreate() {super.onCreate();Log.d(TAG, "执行onCreate()");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "执行onStartCommand()");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();Log.d(TAG, "执行onDestroy()");}@Overridepublic IBinder onBind(Intent intent) {Log.d(TAG, "执行onBind()");return mBinder;}@Overridepublic boolean onUnbind(Intent intent) {Log.d(TAG, "执行onUnbind()");return super.onUnbind(intent);}//新建一个子类继承自Binder类class MyBinder extends Binder {public void playMusic() {Log.d(TAG,"播放音乐");}}
}

2.修改activity_main.xml布局文件,添加绑定Service和解绑Service按钮,用于给用户操作绑定和解绑操作,布局内容如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tvLog"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btnStartService"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="104dp"android:layout_marginTop="36dp"android:text="开启Service"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btnStop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="36dp"android:layout_marginEnd="56dp"android:text="停止Service"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btnBindService"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:text="绑定Service"app:layout_constraintBottom_toBottomOf="@+id/btnUnbindService"app:layout_constraintStart_toStartOf="@+id/btnStartService" /><Buttonandroid:id="@+id/btnUnbindService"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="56dp"android:layout_marginTop="108dp"android:layout_marginEnd="56dp"android:text="解绑Service"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="1.0"app:layout_constraintStart_toEndOf="@+id/btnStartService"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

3.修改MainActivity,实现绑定Service和解绑Service逻辑,代码如下:

package com.pm.servicedemo;import androidx.appcompat.app.AppCompatActivity;import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener {Button bStart;Button bStop;Button bBind;Button bUnbind;TextView tvShow;private MyService.MyBinder myBinder;//创建ServiceConnection的匿名类,该连接用于本Activity和Service沟通private ServiceConnection connection = new ServiceConnection() {//重写onServiceConnected()方法和onServiceDisconnected()方法@Overridepublic void onServiceDisconnected(ComponentName name) {}//在Activity与Service解除关联的时候调用@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//实例化Service的内部类myBindermyBinder = (MyService.MyBinder) service;//在Activity调用Service类的方法myBinder.playMusic();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvShow=(TextView)findViewById(R.id.tvLog);bStart = (Button) findViewById(R.id.btnStartService);bStop = (Button) findViewById(R.id.btnStop);bBind = (Button) findViewById(R.id.btnBindService);bUnbind = (Button) findViewById(R.id.btnUnbindService);bStart.setOnClickListener(this);bStop.setOnClickListener(this);bBind.setOnClickListener(this);bUnbind.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btnStartService:tvShow.setText("启动服务");//构建Intent对象,并调用startService()启动ServiceIntent startIntent = new Intent(this, MyService.class);startService(startIntent);break;case R.id.btnStop:tvShow.setText("停止服务");//构建Intent对象,并调用stopService()停止ServiceIntent stopIntent = new Intent(this, MyService.class);stopService(stopIntent);break;case R.id.btnBindService:tvShow.setText("绑定服务");//仍然需要构建Intent对象Intent bindIntent = new Intent(this, MyService.class);//参数说明//第一个参数:Intent对象//第二个参数:ServiceConnection实例//第三个参数:标志位//这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service//这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行bindService(bindIntent, connection, BIND_AUTO_CREATE);break;case R.id.btnUnbindService:tvShow.setText("解绑服务");//不需要构建Intent对象//调用unbindService()解绑服务//参数为ServiceConnection实例unbindService(connection);break;default:break;}}
}

从代码可以看出,首先需要创建一个ServiceConnection匿名类,从名字可以知道,这是一个Activity和Service的连接,类似一个通讯机制,类中的onServiceConnected()方法和onServiceDisconnected()方法分别用于绑定和解绑,而绑定后Activity指挥Service操作则通过MyBinder里的方法。

绑定中,除了连接类,仍然是构建出了一个Intent对象,然后调用bindService()方法将Activity和Service进行绑定。这和上面的startService一样,bindService方式比startService方式更紧密正是因为多了一个ServiceConnection。

我们注意到还有第三个参数BIND_AUTO_CREATE,该参数是一个标志位,BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。

到这里代码准备好了,现在来测试一下:
1.点击绑定Service按钮,logcat打印如下信息:

com.pm.servicedemo D/MyService: 执行onCreate()
com.pm.servicedemo D/MyService: 执行onBind()
com.pm.servicedemo D/MyService: 播放音乐

2.此时再点击一遍绑定Service按钮则不会新增信息打印出来,因为已经绑定了服务;

3.点击解绑Service打印信息如下,表示执行了解绑并销毁Service:

com.pm.servicedemo D/MyService: 执行onUnbind()
com.pm.servicedemo D/MyService: 执行onDestroy()

4.如果先点击开启Service,再点击绑定Service,根据上面的讨论,应该是先执行startService生命周期,然后在进入bindService生命周期,logcat打印信息如下,说明我们的理解是对的

com.pm.servicedemo D/MyService: 执行onCreate()
com.pm.servicedemo D/MyService: 执行onStartCommand()
com.pm.servicedemo D/MyService: 执行onBind()
com.pm.servicedemo D/MyService: 播放音乐

如果想销毁Service,还是前面讨论的,怎样开始,就得怎样结束,如果直接点停止Service按钮,logcat不会打印销毁服务的信息,证明销毁不成功,需先点解绑Service按钮(也只显示onUnbind(),没有onDestory()),再点停止Service按钮打印onDestory(),说明此时才正常销毁Service

com.pm.servicedemo D/MyService: 执行onUnbind()
com.pm.servicedemo D/MyService: 执行onDestroy()

Android Service介绍相关推荐

  1. Android Service服务的相关介绍

    文章目录 Android Service服务的相关介绍 创建方式 启动方式 生命周期 onStartCommand参数及返回值的理解 ServiceConnection 场景说明 前台服务 问答 st ...

  2. Android生命周期帮助类,Android Service类与生命周期详细介绍_Android_脚本之家

    Android  Service类与生命周期 Service是Android四大组件与Activity最相似的组件,都代表可执行的程序,区别在于Service一直在后台运行且没有用户界面. 1.Ser ...

  3. android 浏览器源码分析,从源码出发深入理解 Android Service

    原标题:从源码出发深入理解 Android Service 原文链接: 建议在浏览器上打开,删除了大量代码细节,:) 本文是 Android 系统学习系列文章中的第三章节的内容,介绍了 Android ...

  4. android service 学习(上)

    转载自:http://www.cnblogs.com/allin/archive/2010/05/15/1736458.html Service是android 系统中的一种组件,它跟Activity ...

  5. Android Service完全解析,关于服务你所需知道的一切(下)

    转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...

  6. 【Android】 Android Service生命周期及用法

    原文来自: 首先我们要知道Service具体是干什么的,什么时候用到?以及它的生命周期等. Service概念及用途: Android中的服务,它与Activity不同,它是不能与用户交互的,不能自己 ...

  7. android demo示例代码,Android Service demo例子使用详解(示例代码)

    Android Service demo例子使用详解\ 概述 Service 是 Android 的四大组件之一,它主要的作用是后台执行操作,Activity 属于带有 UI 界面跟用户进行交互,而 ...

  8. Android HIDL 介绍学习之客户端调用

    应上一篇文章Android HIDL 介绍学习_Super Jang的博客-CSDN博客_安卓hidl读者的要求,本文更新客户端调用方法. hidl的客户端调用相比服务端的实现要简单很多,本次我们通过 ...

  9. android service是什么,Android service是什么 Android service详解

    Android service是什么 Android service详解 时间:2017-04-06     来源:Android开发学习网 什么是Android Service? service是A ...

最新文章

  1. 这些 Python 不为人知的「坑」,躲都躲不开
  2. 英飞凌AI越野组入门教程
  3. asp中sub与function的区别
  4. epoll_wait被signal信号中断时的处理
  5. Chrome 浏览器降级后浏览网站不保留用户数据问题原因及解决方法
  6. java的几_Java的几种时间
  7. SpringBoot返回json和xml
  8. ActiveReports 报表应用教程 (15)---报表换肤
  9. DNS原理及其解析过程 精彩剖析
  10. shell支持loop吗_Shell脚本case语句和loop语句,与,循环
  11. vscode java settings设置_兼容vscode插件的主题服务
  12. 文件隐藏工具Funter for Mac使用方法
  13. 论坛勋章动态特效制作流程
  14. LZJ流体质量计算机价格,LZJ-ⅢC型流体质量计算机技巧.doc
  15. 人脸识别会被留底吗_人脸识别会保存我们的照片吗?
  16. 联想拯救者Y9000P和华硕ROG幻16哪个好
  17. 看机器学习精准预测:谁是iphone的忠实玩家?
  18. 传奇GOM引擎登录器配置教程
  19. SSM配置地狱?一篇整合模板迅速解决!【建议收藏】
  20. Python实践4-守护线程

热门文章

  1. boost::mp11::mp_list相关用法的测试程序
  2. boost::geometry::compress_variant用法的测试程序
  3. boost::function_types::is_function_pointer用法的测试程序
  4. Boost:bimap双图项目的测试程序
  5. ITK:重采样DICOM系列
  6. ITK:二进制或两张图片
  7. VTK:Math之VectorNorm
  8. VTK:相互作用之MoveAGlyph
  9. Qt CMake变量参考
  10. C语言二叉树一个节点的所有祖先节点(附完整源码)