Service(服务)是 Android 四大组件之一,它的主要做用是执行后台操做,Activity 提供了 UI 界面来跟用户交互,而 Service 则没有 UI 界面,全部的操做都是在后台完成。java
Service 跟 Activity 同样也能够由其它应用程序启动,即便用户切换到了其它应用,Service 仍然保持在后台运行。android
此外,一个组件能够与 Service 进行绑定(bind)来跟 Service 进行交互,甚至是进行进程间通讯(IPC)。git
一般状况下可使用 Service 进行网络请求、播放音乐、文件 I/O 等操做。github
要建立一个 Service 首先须要继承 Service 来实现一个子类。网络
public class TestService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
复制代码
相似于 Activity,全部的 Service 都要在 Manifest 里面进行声明,以下:app
<manifest ... >
...
<application ... >
<service android:name="xxx.xxxs.TestService" />
...
</application>
</manifest>
复制代码
经过在 <service>
标签里将 android:exported
设置为 false
。能够防止其余的程序来启动你的 Service。框架
一般状况下有两种方式来启动 Service,startService()
和 bindService()
。异步
Intent intent = new Intent(this, TestService.class);
startService(intent); // 开启服务
stopService(intent); // 中止服务
复制代码
当组件经过调用 startService()
启动 Service 后,Service 就能够在后台无限期的运行,即便启动 Service 的组件被销毁也不受影响。ide
通常状况下 startService()
是执行单一操做,而且不会将执行结果返回给调用者。例如,它多是下载文件或者上传文件,一般操做完成后会自动中止。函数
该方式容许多个组件同时对相同的 Service 进行 startService()
操做,可是若是只要有其中有一个组件调用了 stopSelf()
或 stopService()
, 该 Service 就会被销毁。
Intent intent = new Intent(this, TestService.class);
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
// 绑定服务
bindService(intent, connection, Context.BIND_AUTO_CREATE);
// 解绑服务
unbindService(aidlConnection);
复制代码
当组件经过调用 bindService()
启动 Service 后,Service 就处于绑定状态了。这种方式提供了 client - service 的接口,可让调用者与 Service 进行发送请求和返回结果的操做,甚至能够进行进程间的通讯(IPC)。
只要有一个组件对该 Service 进行了绑定,那该 Service 就不会销毁。若是多个组件能够同时对一个 Service 进行绑定,只有全部绑定的该 Service 的组件都解绑后,该 Service 才会销毁。
尽管两种方式是分开讨论的,可是并非互斥的关系,使用 startService()
启动了 Service 后,也是能够进行绑定的。
注意:虽然 Service 是在后台运行的,但其实仍是在主线程中进行全部的操做。Service 启动时除非单独进行了定义,不然没有单独开启线程或者进程都是运行在主线程中。
因此任何能阻塞主线程的操做(例如:播放音乐或者网络请求),都应该在 Service 中单独开启新的线程来进行操做,不然很容易出现 ANR。
在建立一个 Service 时,必需要去继承 Service,而且须要重写父类的一些方法来实现功能。如下是主要方法的介绍。
当另外一个组件(如:Activity)经过调用 startService()
来启动 Service 时,系统会调用该方法。一旦执行该方法,Service 就会启动并在后台无限期执行。
若是实现该方法,在 Service 执行完后,须要调用 stopSelf() 或 stopService() 来停结束Service。
若是只是会经过绑定的方式(bind)的方式来启动 Service 则不须要重写该方法。
系统会调用这个函数当某个组件(例如:activity,fragment)经过调用 bindService()
绑定的方式来启动 Service 的时候。在实现这个函数的时候,必需要返回一个 IBinder 的继承类,来与 Service 进行通讯。
这个函数是默认必需要重写的,可是若是不想经过绑定的方式来启动 Service,则能够直接返回 null
。
系统会调用此方法在第一次启动 Service 的时候,用于初始化一些一次性的变量。若是 Service 已经启动了,则此方法就不会再别调用。
系统在 Service 已经不须要准备被销毁的时候会调用此方法。Service 中若有用到 thread、listeners、receivers 等的时候,应该将这些的清理方法写在此方法内。
与 Activity 相似,Service 也有生命周期回调方法,能够实现这些方法来监控 Service 状态的变化来执行相关操做。
onCreate()
-> onStartCommand()
-> onDestroy()
onCreate()
-> onBind()
-> onUnbind()
-> onDestroy()
当系统内存不足的时候,系统会强制回收一些 Activity 和 Service 来获取更多的资源给那些用户正在交互的程序或页面。当资源充足的时候能够经过 onStartCommand()
的返回值,来实现 Service 自动重启。
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY | START_STICKY | START_REDELIVER_INTENT;
}
复制代码
当系统因回收资源而销毁了 Service,当资源再次充足时再也不自动启动 Service,除非有未处理的 Intent 准备发送。
当系统因回收资源而销毁了 Service,当资源再次充足时自动启动 Service。并且再次调用 onStartCommand()
方法,可是不会传递最后一次的 Intent,相反系统在回调 onStartCommand()
的时候会传一个空 Intent,除非有未处理的 Intent 准备发送。
当系统因回收资源而销毁了 Service,当资源再次充足时自动启动 Service,而且再次调用 onStartCommand()
方法,并会把最后一次 Intent 再次传递给 onStartCommand()
,相应的在队列里的 Intent 也会按次序一次传递。此模式适用于下载等服务。
Service 自己默认是运行在主线程里的,因此若是在 Service 要进行一些会堵塞线程的操做,必定要将这些操做放在一个新的线程里。
为了知足后台运行异步线程的需求,Android 的框架提供了 IntentService。
IntentService 是 Service 的子类,而且全部的请求操做都是在异步线程里。若是不须要 Service 来同时处理多个请求的话,IntentService 将会是最佳的选择。
使用该服务只须要继承并重写 IntentService 中的 onHandleIntent()
方法,就能够对接受到的 Intent
作后台的异步线程操做了。
public class TestIntentService extends IntentService {
public TestIntentService() {
super("TestIntentService");
}
public TestIntentService(String name) {
super(name);
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//TODO: 耗时操做,运行在子线程中
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
复制代码
前台服务是那些被认为用户知道(用户所承认的),且在系统内存不足的时候不容许系统杀死的服务。
前台服务必须给状态栏提供一个通知,它被放到正在运行(Ongoing)标题之下 —— 这就意味着通知只有在这个服务被终止或从前台主动移除通知后才能被解除。
在通常状况下,Service 几乎都是在后台运行,一直默默地作着辛苦的工做。但这种状况下,后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的 Service 就有可能被回收。
那么,若是咱们但愿 Service 能够一直保持运行状态,且不会在内存不足的状况下被回收时,能够选择将须要保持运行的 Service 设置为前台服务。
例如:App中的音乐播放服务应被设置在前台运行(前台服务)——在 App 后台运行时,便于用户明确知道它的当前操做、在状态栏中指明当前歌曲信息、提供对应操做。
新建一个服务。
public class ForegroundService extends Service {
private static final int RESULT_CODE = 0;
private static final int ID = 1;
public ForegroundService() { }
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, RESULT_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder;
// 兼容 Android 8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "foreground_service";
NotificationChannel channel = new NotificationChannel(channelId,
"channel_1", NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(true);
channel.setLightColor(Color.GREEN);
channel.setShowBadge(true);
NotificationManager notificationManager =
getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
builder = new NotificationCompat.Builder(this, channelId);
} else {
builder = new NotificationCompat.Builder(this);
}
builder.setContentIntent(pendingIntent)
.setContentTitle("这是前台通知标题")
.setContentText("这是内容")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher))
.setPriority(NotificationManager.IMPORTANCE_HIGH)
.setDefaults(Notification.DEFAULT_SOUND);
startForeground(ID, builder.build());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return super.onBind(intent);
}
}
复制代码
启动与中止前台服务
Intent foregroundIntent = new Intent(this, ForegroundService.class);
startService(foregroundIntent); // 启动前台服务
stopService(foregroundIntent); // 中止前台服务
复制代码
经过前面的介绍咱们了解到 Service 是后台服务来执行一些特定的任务,可是当后台服务在系统资源不足的时候可能会回收销毁掉 Service。
那么如何让后台服务尽可能不被杀死呢?基本解决思路以下:
为防止 Service 被系统回收,能够尝试经过提升服务的优先级解决。优先级数值最高为 1000,数字越小,优先级越低。
<service android:name=".ui.service.TestService" >
<intent-filter android:priority="1000"/>
</service>
复制代码
在 Manifest.xml 文件中设置 persistent 属性为 true,则可以使该服务免受 out-of-memory killer 的影响。可是这种作法必定要谨慎,系统服务太多将严重影响系统的总体运行效率。
<application android:persistent="true">
</application>
复制代码
该属性的特色以下:
重写 onStartCommand 方法,使用 startForeground(int, Notification)
方法来启动 Service。利用 Android 的系统广播
利用 Android 的系统广播检查 Service 的运行状态,若是被杀掉就重启。系统广播是 Intent.ACTION_TIME_TICK
,这个广播每分钟发送一次。咱们能够每分钟检查一次 Service 的运行状态,若是已经被销毁了,就从新启动 Service。
欢迎关注个人公众号,分享各类技术干货,各类学习资料,职业发展和行业动态。
欢迎加入技术交流群,来一块儿交流学习。