View体系之四大组件——Service详解
在学习Service之前,我们先提出以下几个问题:
1、什么是Service?Service的作用是什么?
2、Service有哪几种状态,其生命周期是什么?
3、Service如何使用?(同一进程/跨进程)
4、Service如何保活(进程保活)?
5、对IntentService的理解
好了,正题开始。
一、什么是Service?为什么Android会使用Service?
Service顾明思义就是服务,是Andorid提供的实现后台运行的解决方案,主要用于执行后台任务。
比如:通常我们在微信或者qq聊天时,电话进来,然后在打电话的过程中qq或者微信也会一直挂载,这就是Android的后台功能。
服务并不是运行在独立的进程中,而是会依赖创建服务时的应用程序进程,在这个应用程序进程被杀死后,依赖该进程的服务也会被杀死。
又比如:我们在一个应用中一边下载视频,一边继续浏览其他内容。
需要注意的是,服务(除IntentService外)并不会默认开启线程,服务的执行是在主线程中执行,因此如果在服务中执行耗时任务,也会导致主线程被阻塞,导致ANR,所以如果需要执行耗时任务,可以通过使用IntentService或者是开启线程来执行。
二、Service的状态和生命周期
1、Service的状态
根据Service是否与组件进行交互,可分为两种状态:启动状态和绑定状态。
-
启动状态(不交互)
使用范围:
通常用于那些不需要交互、无限期后台运行的任务。比如从服务器下载数据
如何使用:
调用startService()启动,服务即可在后台无限期的运行,即使启动Service的组件已经销毁,也不影响Service的运行。
需要停止Service时,可以通过组件调用stopService()方法或者在Service的子类中调用 stopself()方法即可。
-
绑定状态
使用范围:
通常使用与那些需要与后台Service进行交互的情形
如何使用:
在绑定状态时,组件调用bindService()启动该Service,同时实现Service与组件的绑定。而绑定的服务中提供了一个IBinder()接口,用于组件和服务进行交互,发送请求,获取结果。
而当所有的组件都与它解绑后,该Service才会被销毁。
2、 Service的生命周期分析,
具体如下图 1:
图 1
通过图片可以发现,启动状态和绑定状态的声明周期是不同的。
需要注意,在绑定状态时,同一个组件对该Service多次绑定,只需要调用一次unbindService()即可解绑。即多次绑定一次解绑
三、Service的具体使用
1、Manifest文件中声明
在AndroidManifest.xml中声明,如下:
<service
android:enabled=[
"true"
| "
false
"]
//是否被系统实例化,默认为true
android:exported=[
"true"
| "
false
"]
//是否可以被隐式调用
android:icon=
"drawable resource"
android:isolatedProcess=[
"true"
| "
false
"]
android:label=
"string resource"
android:name=
"string" //Service类名
android:permission=
"string"
//权限
android:process=
"string"
> //是否在单独的进程中运行
. . . </service>
这里其他值采用默认,声明如下:
<service android:name=".activity.LocalService"/>
2、继承Service类。具体如下:
package com.sky_wf.de
moutils.activity;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* @Date : 2018/2/24
* @Author : WF
* @Description :
*/
public class LocalService extends Service
{
@Nullable
@Override
public IBinder onBind(Intent intent)//所有继承Service都必须实现onBind()方法
{
return null;
}
@Override
public void onCreate()//Service创建时调用,只调用一次
{
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy()//Service销毁时调用
{
super.onDestroy();
}
}
四大组件包括Application其实根本上来说,都继承了Context。因此我们经常在组件中这样写:
public class LoginActivity extends Activity
{
private Context context;
@override
public void onCreate()
{
context = this;
}
}
其实在这里,this代表的就是当前LoginActivty的一个实例,而Context则是Activity的父类,context = this。这本身就是一种向上转型。
这里也就可以理解,为什么很多方法或者组件实例化时参数需要Context,而我们传入一个this就可以。又或者我们自己实例化一个Context传入。
Service本身就是一个抽象类,继承ContextWrapper,而ContextWrapper则是继承与Context,
在Service这个抽象类中有唯一的一个抽象方法onBind()。
-
onBind(Intent intent)
onBind()方法是Service中唯一的一个抽象方法,因此继承Service类时必须实现这个类。通常用于进行通信,尤其是后面我们会讲到的跨进程通信。
-
onCreate()
在首次创建Service时调用,当重复执行startService()或者bindService()时,onCreate()不再执行。
-
onStartCommand(Intent intent,int flags,int startId)
当组件调用startService()启动服务时,则系统会调用此方法,如果是第一次调用startService()则会先调用onCreate()再调用onStartCommand(),重复启动服务,只会调用 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后,则会重建服务,之后调用onStartCommand()。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。
适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
-
onDestory()当服务停止且要被销毁时,则会调用此方法。
3、布局文件和Activity
(1)布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_start"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="启动服务"
/>
<Button
android:id="@+id/btn_stop"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="停止服务"
/>
</LinearLayout>
(2)Activity
package com.sky_wf.demoutils.activity;
import android.content.Intent;