这篇文章主要是讲解Service
前言:Service非常适用于去执行那些不需要和用户交互而且还要长期运行在后台的任务。Service默认线程为UI线程,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作.
Service的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开另一个应用程序,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行.
一、Service的基本用法
普通的Service的生命周期很简单,分别为onCreate、onStartCommand、onDestroy这三个。当我们startService()的时候,首次创建Service会回调onCreate()方法,然后回调onStartCommand()方法,再次startService()的时候,就只会执行onStartCommand()。服务一旦开启后,我们就需要通过stopService()方法或者stopSelf()方法关闭服务,这时就会回调onDestroy()
- 创建服务类
创建一个服务非常简单,只要继承Service,并实现onBind()方法
public class BackGroupService extends Service {
@Override
public IBinder onBind(Intent intent) {
Log.e("Service","onBind");
return null;
}
@Override
public void onCreate() {
Log.e("Service","onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("Service","onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e("Service","onDestroy");
super.onDestroy();
}
}
- 配置AndroidManifest
Service也是四大组件之一,所以必须在AndroidManifest中配置
<service android:name=".BackGroupService" />
- 启动服务和停止服务
我们通过两个按钮分别演示启动服务和停止服务,通过startService()开启服务,通过stopService()停止服务
Intent startServiceIntent = new Intent(this, BackGroupService.class);
startService.setOnClickListener((v)-> {
//启动服务
startService(startServiceIntent );
});
stopService.setOnClickListener((v)-> {
//停止服务
stopService(startServiceIntent);
});
运行程序后,我们点击开始服务,然后关闭服务。我们以Log信息来验证普通Service的生命周期:onCreate->onStartCommand->onDestroy
当你开启服务后,还有一种方法可以关闭服务,在设置中,通过应用->找到自己应用->停止
二、Service和Activity进行通信
Service和Activity进行通信指前台页面(Activity)可以调用后台服务的方法,实现步骤是和普通服务实现步骤是一样的,区别在于启动的方式和获得Service的代理对象
- 创建服务类
这里和普通Service不同,在onBind
中返回一个Binder 对象,Activity可以获取该Binder 对象从而执行后台服务的方法.
class MyBind extends Binder {
public void startDownload() {
Log.d("Service", "开始下载");
// 执行具体的下载任务
}
}
@Override
public IBinder onBind(Intent intent) {
Log.e("Service", "onBind");
return new MyBind(); //这里返回服务代理对象
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("Service", "onUnbind");
return super.onUnbind(intent);
}
- 绑定服务和解除绑定服务
我们通过两个按钮分别演示绑定服务和解除绑定服务,通过bindService()开启服务,通过unbindService()停止服务
bindService.setOnClickListener((view)-> {
bindService(startServiceIntent,serviceConnection,BIND_AUTO_CREATE);
});
unbindService.setOnClickListener((view->) {
unbindService(serviceConnection);
});
}
/**
* 创建ServiceConnection
*/
final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//拿到后台服务代理对象
final MyBind myBind = (MyBind) iBinder;
myBind.startDownload();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
这里和startService的区别在于多了一个ServiceConnection对象,该对象是用户绑定后台服务后,可获取后台服务代理对象的回调,我们可以通过该回调,拿到后台服务的代理对象,并调用后台服务定义的方法,也就实现了后台服务和前台的交互
-
运行代码
运行程序后,我们点击绑定服务,然后一段时间后解除绑定服务。我们以Log信息来验证Service的生命周期:onCreate->onBind->onUnBind->onDestroy,其中onbind开启服务重复开启不会执行任何方法,其中也可以看到我们调用后台服务的方法开始下载了....
-
混合性交互的后台服务
如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。
三、Service和Thread的关系
Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?
答案是Service和Thread之间没有任何关系!
之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是Service其实就是运行在主线程里的,那要Service又有何用呢?其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
四、前台服务
Service几乎都是在后台运行的,一直以来它都是默默地做着辛苦的工作。但是Service的系统优先级还是比较低的,当系统出现内存不足情况时,就有可能会回收掉正在后台运行的Service。如果你希望Service可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。当然有时候你也可能不仅仅是为了防止Service被回收才使用前台Service,有些项目由于特殊的需求会要求必须使用前台Service.
- 创建服务类
前台服务创建很简单,其实就在Service的基础上创建一个Notification,然后使用Service的startForeground()方法即可启动为前台服务
private void showNotification() {
final Notification.Builder mBuilder = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("前台服务")
.setContentText("我是前台服务");
final Intent intent = new Intent(this, MainActivity.class);
//创建任务栈Builder
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
//设置跳转Intent到通知中
mBuilder.setContentIntent(pendingIntent);
//获取通知服务
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//构建通知
Notification notification = mBuilder.build();
//显示通知
nm.notify(0, notification);
//启动为前台服务
startForeground(0, notification);
}
- 配置服务
<service android:name=".ForegroundService" /> -
启动和结束服务
现在重新运行一下程序,并点击Start Service或Bind Service按钮,ForegroundService就会以前台Service的模式启动了,并且在系统状态栏会弹出一个通栏图标,下拉状态栏后可以看到通知的详细内容,如下图所示。
五、IntentService
IntentService是专门用来解决Service中不能执行耗时操作这一问题的,创建一个IntentService也很简单,只要继承IntentService并覆写onHandlerIntent函数,在该函数中就可以执行耗时操作了,执行完耗时操作后自动结束服务.
public class TheIntentService extends IntentService {
public TheIntentService() {
super("TheIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.e("IntentService", "耗时前");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("IntentService", "耗时后");
}
}
网友评论