Android Service

本文详细介绍了Android中的Service,包括Service的简介、进程类型、生命周期、启动服务与绑定服务的模式,以及如何管理Service的生命周期。重点讲解了Service的生命周期回调函数,如onCreate、onStartCommand、onBind和onDestroy,并讨论了如何通过startService和bindService与Service交互。此外,文章还提到了前台服务、IntentService和如何保证Service不被系统杀死的策略。
摘要由CSDN通过智能技术生成

【Android Service】

Service 简介(★★★)

很多情况下,一些与用户很少需要产生交互的应用程序,我们一般让它们在后台运 行就行了,而且在它们运行期间我们仍然能运行其他的应用。为了处理这种后台进程, Android 引入了 Service 的概念。

  • Service 在 Android 中是一种长生命周期的组件,它不实现任何用户界面,是一个没有界面的 Activity
  • Service 长期在后台运行, 执行不关乎界面的一些操作比如: 网易新闻服务,每隔 1 分钟去服务查看是否有最新新闻
  • Service 和 Thread 有点相似,但是使用 Thread 不安全不严谨
  • Service 和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的操作。(Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建子线程来完成耗时操作

Android 中的进程(★★)

1. Android 中进程的种类

进程优先级由高到低,依次为:

  1. Foreground process 前台进程
  2. Visible process 可视进程, 可以看见, 但不可以交互.
  3. Service process 服务进程
  4. Background process 后台进程
  5. Empty process 空进程(当程序退出时, 进程没有被销毁, 而是变成了空进程)

2. 进程的回收机制

Android 系统有一套内存回收机制,会根据优先级进行回收。Android 系统会尽可能的维持程序的进程, 但是终究还是需要回收一些旧的进程节省内存提供给新的或者重要的进 程使用。

  • 进程的回收顺序是:从低到高
  • 当系统内存不够用时, 会把空进程一个一个回收掉
  • 当系统回收所有的完空进程不够用时, 继续向上回收后台进程, 依次类推
  • 但是当回收服务, 可视, 前台这三种进程时, 系统非必要情况下不会轻易回收, 如果需要回收掉这三种进程, 那么在系统内存够用时, 会再给重新启动进程;但是服务进程如果用户手动的关闭服务, 这时服务不会再重启了。

3. 为什么用服务而不是线程

进程中运行着线程, Android 应用程序刚启动都会开启一个进程给这个程序来使用。
Android 一个应用程序把所有的界面关闭时, 进程这时还没有被销毁, 现在处于的是空进程状态,Thread 运行在空进程中, 很容易被销毁
服务不容易被销毁, 如果非法状态下被销毁了, 系统会在内存够用时, 重新启动。

Service 的生命周期(★★★★)

service 的生命周期,从它被创建开始,到它被销毁为止,可以有两条不同的路径。

A started service(标准开启模式)

  当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。
  一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务。(调用 stopSelf()方法或者其他组件调用 stopService()方法)
   已启动的服务通常是执行单一操作,而且不会将结果返回给调用方
  

A bound service(绑定模式)

  当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。
  绑定服务提供了一个客户端-服务器接口 IBinder 接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。
  仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。(通过 unbindService()方法来关闭这种连接)

Service 的这两种生命周期并不是完全分开的

也就是说,你可以和一个已经调用了 startService()而被开启的 service 进行绑定
比如,一个后台音乐 service 可能因调用 startService()方法而被开启了,稍后, 可能用户想要控制播放器或者得到一些当前歌曲的信息,可以通过 bindService()将一 个 activity 和 service 绑定。这种情况下,stopService()或 stopSelf()实际上并不能停止这个 service,除非所有的客户都解除绑定。

Service 的生命周期图

Service 的生命周期图
这个图说明了 service 典型的回调方法,尽管这个图中将开启的 service 和绑定的service 分开,但是你需要记住,任何service 都潜在地允许绑定。所以,一个被开启的 service 仍然可能被绑定。(你可以看到两层嵌套的 service 的生命周期)

整体生命周期(The entire lifetime)

service 整体的生命时间是从 onCreate()被调用开始,到 onDestroy()方法返回为止
和 activity 一样,service 在 onCreate()中进行它的初始化工作,在 onDestroy()中释放残留的资源。

比如,一个音乐播放 service 可以在 onCreate()中创建播放音乐的线程,在onDestory()中停止这个线程。

onCreate() 和 onDestroy()会被所有的 service 调用,不论 service 是通过startService()还是 bindService()建立。

积极活动的生命时间(The active lifetime)

service 积极活动的生命时间(active lifetime)是从 onStartCommand() 或onBind()被调用开始,它们各自处理由 startService()或 bindService()方法传过来的 Intent 对象。

如果 service 是被开启的,那么它的活动生命周期和整个生命周期一同结束
如果 service 是被绑定的,它们它的活动生命周期是在 onUnbind()方法返回后结束

尽管一个被开启的 service 是通过调用 stopSelf() 或 stopService()来停止的,没有一个对应的回调函数与之对应,即没有 onStop()回调方法。所以,当调用了停止的方法除非这个 service 和客户组件绑定,否则系统将会直接销毁它,onDestory()方法会被调用,并且是这个时候唯一会被调用的回调方法。

Service 的生命周期回调函数

和 activity 一样,service 也有一系列的生命周期回调函数,你可以实现它们来监测 service 状态的变化,并且在适当的时候执行适当的工作。

下面的 service 展示了每一个生命周期的方法:
不像是 activity 的生命周期回调函数,我们不需要调用基类的实现

public class TestService extends Service {
   
    int mStartMode; // indicates how to behave if the service is killed
    IBinder mBinder; // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
// The service is being created
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
        return mStartMode;
    }

    @Override
    public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
        return mAllowRebind;
    }

    @Override
    public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
    }

    @Override
    public void onDestroy() {
// The service is no longer used and is being destroyed
    }
}
onCreate()

  首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次

onBind()

  当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信
  无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null
  

onStartCommand()

  当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。(启动服务使用startService(Intent intent)方法,仅需要传递一个Intent对象即可,在Intent对象中指定需要启动的服务。)  
  对于启动服务,一旦启动将与访问它的组件无任何关联,即使访问它的组件被销毁了,这个服务也一直运行下去,直到手动调用停止服务才被销毁。在服务外部,必须使用stopService()方法停止,在服务内部可以调用stopSelf()方法停止当前服务。
  第一次调用startService方法时,onCreate方法、onStartCommand方法将依次被调用,而多次调用startService时,只有onStartCommand方法被调用,最后我们调用stopService方法停止服务时onDestory方法被回调,这就是启动状态下Service的执行周期。

onStartCommand的参数

  接着我们重新回过头来进一步分析onStartCommand(Intent intent, int flags, int startId),这个方法有3个传入参数,它们的含义如下:
onStartCommand(Intent intent, int flags, int startId)

  • intent :启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service
  • flags:表示启动请求时是否有额外数据,可选值有 0,START_FLAG_REDELIVERY,START_FLAG_RETRY。
    • 0代表没有
    • START_FLAG_REDELIVERY
      这个值代表了onStartCommand方法的返回值为START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),此时Intent时有值的。
    • START_FLAG_RETRY
      该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。
  • startId : 指明当前服务的唯一ID,与stopSelfResult (int startId)配合使用,stopSelfResult 可以更安全地根据ID停止服务
onStartCommand的返回值

  实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值, START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:

  • START_STICKY
    当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
  • START_NOT_STICKY
    当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  • START_REDELIVER_INTENT
    当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制。
  

onDestroy()

  当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
  

管理生命周期

当绑定 service 和所有客户端解除绑定之后,Android 系统将会销毁它,(除非它同时被 onStartCommand()方法开启)。因此,如果你的 service 是一个纯粹的绑定 service,那么你不需要管理它的生命周期

然而,如果你选择实现 onStartCommand()回调方法,那么你必须显式地停止 service,因为 service 此时被看做是开启的。这种情况下,service 会一直运行到它 自己调用 stopSelf()或另一个组件调用 stopService(),不论它是否和客户端绑定。

另外,如果你的 service 被开启并且接受绑定,那么当系统调用你的 onUnbind() 方法时,如果你想要在下次客户端绑定的时候接受一个 onRebind()的调用(而不是调用 onBind()),你可以选择在 onUnbind()中返回 true

onRebind()的返回值为 void,但是客户端仍然在它的 onServiceConnected()回调方法中得到 IBinder 对象

下图展示了这种 service(被开启,还允许绑定)的生命周期:
service(被开启,还允许绑定)的生命周期

Service在清单文件中的声明

  前面说过Service分为启动状态和绑定状态两种,但无论哪种具体的Service启动类型,都是通过继承Service基类自定义而来,也都需要在AndroidManifest.xml中声明。
  我们先来了解一下Service在AndroidManifest.xml中的声明语法,其格式如下:

<service 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>
  • android:exported:代表是否能被其他应用隐式调用
    其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
  • android:name:对应Service类名
  • android:permission:是权限声明
  • android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。
    注意:“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
  • android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
  • android:enabled:是否可以被系统实例化,默认为 true因为父标签也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。

后台服务

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

不可交互的后台服务(启动服务)

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

① 服务端:创建服务类

创建一个服务非常简单,只要继承Service,并实现onBind()方法

public class BackGroupService extends Service {
   
    /* 綁定服务时调用 */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service", "onBind");
        return null;
    }

    /* 服务创建时调用 */
    @Override
    public void onCreate() {
        Log.e("Service", "onCreate");
        super.onCreate();
    }

    /* 执行startService时调用 */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("Service", "onStartCommand");
        //这里执行耗时操作
        new Thread() {
            @Override
            public void run() {
                while (true){
                    try {
                        Log.e("Service", "doSomething");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        return super.onStartCommand(intent, flags, startId);
    }

    /* 服务被销毁时调用 */
    @Override
    public void onDestroy() {
        Log.e("Service", "onDestroy");
        super.onDestroy();
    }
}
② 配置服务

Service也是四大组件之一,所以必须在manifests中配置

<service android:name=".Service.BackGroupService"/>
③ 客户端:启动服务和停止服务

我们通过两个按钮分别演示启动服务和停止服务,通过startService()开启服务,通过stopService()停止服务

public class MainActivity extends AppCompatActivity {
   
    Button bt_open, bt_close;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt_open = (Button) findViewById(R.id.open);
        bt_close = (Button) findViewById(R.id.close);
        //自定义Service的意图
        final Intent intent = new Intent(this, BackGroupService.class);
        bt_open.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //启动服务
                startService(intent);
            }
        });
        bt_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //停止服务
                stopService(intent);
            }
        });
    }
}

当你开启服务后,还有一种方法可以关闭服务,在设置中,通过应用->找到自己应用->停止

④ 运行代码

运行程序后,我们点击开始服务,然后一段时间后关闭服务。我们以Log信息来验证普通Service的生命周期:onCreate->onStartCommand->onDestroy

11-24 00:19:51.483 16407-16407/com.handsome.boke2 E/Service: onCreate
11-24 00:19:51.483 16407-16407/com.handsome.boke2 E/Service: onStartCommand
11-24 00:19:51.485 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:53.490 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:55.491 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:57.491 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:58.056 16407-16407/com.handsome.boke2 E/Service: onDestroy
11-24 00:19:59.492 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:20:01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值