它主要用来执行一些不与用户交互且长时间运行的操作
特点:没有用户界面,可以在后台长期运行
必须显式启动,5.0以后不支持隐式启动
Service发生ANR的时间是20秒
生命周期
service主要有两种分别是startService和bindService
生命周期如下图:
Service只有一个实例,也就是说onCreate方法只会调用一次
方法介绍:
- onCreate():服务第一次被创建时调用
- onStartComand():服务启动时调用
- onBind():服务被绑定时调用
- onUnBind():服务被解绑时调用
- onDestroy():服务停止时调用
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的返回值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。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
Service两种状态
启动状态
调用startService(),启动服务生命周期如上图左侧:onCreate()、onStartCommand(),可以多次调用startService,当调用时实例已存在,不会再次调用onCreate而是直接调用onStartCommand(),无论调用多少次startService(),只需调用一次stopService()或stopSelf()方法,服务就会停止了。
绑定状态
- 调用bindService(),启动服务生命周期如上图左侧:onCreate()、onBind()。再次调用bindService()不会再回调onCreate()、onBind()方法,调用unBindService(),会回调onUnBind()->onDestroy()。
Service种类
前台服务
前台服务一般都是用户所认可且内存不足的时候不允许系统杀死的服务,希望可以一直保持运行状态的 优先级高,会在通知栏显示,例如手机的天气预报,墨迹天气
Service几乎是后台服务,优先级低,当系统出现内存不足情况下,可以会回收正在后台执行的Service。如果希望Service可以一直保持运行状态,可以考虑使用前台Service。
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("标题标题标题")
.setContentText("内容内容内容内容内容内容");
startForeground(1, mBuilder.build());
后台服务
特点:执行的操作对用户无感知
Service实现程序后台运行,默认在主线程,所以不适合做耗时操作,除非在Service中创建子线程来完成耗时工作。
而IntentService是在子线程执行的。IntentService在后面会介绍
实例:
清单文件声明:
<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 的情况下服务才会被激活,否则不会激活。
Service
package com.example.lenovo.mpplication.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.example.lenovo.mpplication.R;
public class MyService extends Service {
private static final String TAG = MyService.class.getSimpleName();
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
}
//每startService一次,startId++
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return null;
}
}
开启服务
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
停止服务
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
IntentService
- Service是所有服务的基类,默认情况下在主线程运行。
- IntentService是其子类,使用子线程来处理所有的启动请求,一次一个。无法同时处理多个请求。
- 实现onHandlerIntent(),并在该函数中完成耗时操作即可。在任务执行完毕后IntentService会调用stopSelf()自我销毁
- 适合执行短期耗时任务
实例:
定义IntentService
package com.example.lenovo.mpplication.service;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class MyIntentService extends IntentService {
private final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, "onHandleIntent Thread----"+Thread.currentThread().getName());
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
启动IntentService
Intent intent = new Intent(this, MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("key", "当前值:" + 0);
intent.putExtras(bundle);
startService(intent);
绑定服务(Service和Activity通信)
与启动服务不同的是绑定服务的生命周期通常只在为其他应用组件(如Activity)服务时处于活动状态,不会无限期在后台运行,也就是说宿主(如Activity)解除绑定后,绑定服务就会被销毁。
用到绑定服务和解绑服务,重写Service中的onBind方法,返回IBinder对象。Activity获取IBinder对象,可以通过该接口开始与服务进行交互。
提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:
扩展 Binder 类 :
如果服务是提供给自有应用专用的,并且Service(服务端)与客户端相同的进程中运行(常见情况),则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。如果我们的服务只是自有应用的后台工作线程,则优先采用这种方法。
具体使用详解见:关于Android Service真正的完全详解,你需要知道的一切
使用 Messenger
Messenger可以翻译为信使,通过它可以在不同的进程中共传递Message对象(Handler中的Messager,因此 Handler 是 Messenger 的基础),在Message中可以存放我们需要传递的数据,然后在进程间传递。 因为 Messenger 会在单一线程中创建包含所有请求的队列,也就是说Messenger是以串行的方式处理客户端发来的消息
具体使用详解见:关于Android Service真正的完全详解,你需要知道的一切
使用 AIDL
如果有大量并发请求,这时AIDL(Android 接口定义语言)就派上用场了,
但实际上Messenger 的跨进程方式其底层实现 就是AIDL,只不过android系统帮我们封装成透明的Messenger罢了 。
绑定服务注意点
1.多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
2.通常情况下我们应该在客户端生命周期(如Activity的生命周期)的引入 (bring-up) 和退出 (tear-down) 时刻设置绑定和取消绑定操作,以便控制绑定状态下的Service,一般有以下两种情况:
如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
如果希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。需要注意的是,这意味着 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当提高该进程的权重时,系统很可能会终止该进程。
3.我们应该始终捕获 DeadObjectException DeadObjectException 异常,该异常是在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。
4.应用组件(客户端)可通过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。
5.无论是先绑定服务后启动服务还是先启动服务后绑定服务,都会转成启动服务运行
实际运用
处理耗时任务不是优先使用服务
处理后台任务并没有优先使用后台服务而是自己实现线程池或者用AsyncTack来执行后台服务
原因
- 性能方面:启动service涉及到多次IPC,运行效率不高。 线程池运行效率高,系统开销小,而且随着业务逻辑的复杂度增加,扩展性也更强。
- 灵活性方面:IntentService,受限于系统接口,使用不灵活。线程池配置和使用灵活,缺点是多进程实现不方便
- 进程通信方面: 服务创建进程方便;可以提供给系统内其他App使用;线程池多进程实现不方便
5以上不能隐式启动
解决办法一:设置Action和packageName
final Intent serviceIntent=new Intent(); serviceIntent.setAction("com.android.ForegroundService");
serviceIntent.setPackage(getPackageName());//设置应用的包名
startService(serviceIntent);
解决办法二:将隐式启动转换为显示启动
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
如何保证服务不被杀死
1、START_STICKY或START_REDELIVER_INTENT
可将onStartCommand() 方法的返回值设为 START_STICKY或START_REDELIVER_INTENT ,该值表示服务在内存资源紧张时被杀死后,在内存资源足够时再恢复。
2、 用户通过 settings -> Apps -> Running -> Stop 方式杀死Service 这种情况是用户手动干预的,不过幸运的是这个过程会执行Service的生命周期,也就是onDestory方法会被调用,这时便可以在 onDestory() 中发送广播重新启动。这样杀死服务后会立即启动。这种方案是行得通的,但为程序更健全,我们可开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。
3、设为前台进程
保证最长生命周期的Thread
(Service中保持与服务器端的长连接。)
前台服务。使程序保持运行状态
Android开发中使用Service还是Thread
Service的优先级高于后台挂起的Activity,当然,也高于Activity所创建的Thread,因此,系统可能在内存不足的时候优先杀死后台的Activity或者Thread,而不会轻易杀死Service组件,即使被迫杀死Service,也会在资源可用时重启被杀死的Service
如果你希望整个APP都退出之后依然能运行该Thread,那么就应该把Thread放到Service中去创建和启动了。这是保证最长生命周期的Thread的唯一方式,只要整个Service不退出,Thread就可以一直在后台执行