一、服务的基本概念
1.服务是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且要长时间运行的任务。
2.服务虽然不依赖于任何UI界面,但它依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止。
3.虽然服务运行在后台,但服务并不会自动开启线程,所有代码都是默认运行在主线程中的。换言之,如果我们没有在服务内部手动创建子线程,就有可能出现主线程被阻塞住的情况。
二、启动服务的两种方式
2.1.使用startService()方法启动服务
1.新建一个自定义的服务类去继承Service
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
}
//该方法是Service中唯一一个抽象方法,在下一种启动服务的方式中要用到,这里就简单的返回Null
@Override
public IBinder onBind(Intent intent) {
return null;
}
//在服务创建的时候调用
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: 服务被创建,常用于初始化服务中使用到的对象");
}
//在每次服务启动的时候调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: 服务被启动,开始执行任务");
return super.onStartCommand(intent, flags, startId);
}
//在服务销毁的时候调用
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: 服务被销毁,常用于回收那些不再使用的资源");
}
}
2.编辑AndroidManifest.xml文件注册刚才的服务
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
其中enabled属性为true表示启用该服务,exported属性为true表示允许当前程序以外的其他程序访问该服务。
3.通过Context中的startService()方法启动服务,通过Context中的stopService()方法销毁活动
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_start_service:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent);
break;
case R.id.btn_stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent);
break;
default:
break;
}
如果想让服务自己决定销毁的时间点,可以在自定义服务MyService中任何一个位置调用stopSelf()方法来让其停止。
2.2.使用bindService()方法启动服务
1.在之前自定义的服务类中定义一个内部类去继承Binder(实现了IBinder接口)
class DownloadBinder extends Binder{
/**
* 获取Service实例
* */
public MyService getService(){
return MyService.this;
}
}
PS:这里给内部类DownloadBinder添加了一个获取MyService实例的方法,之后将通过该方法获取MyService实例,然后调用其内部方法(下载),从而实现活动与服务之间的通信。
2.编辑自定义服务类MyService,创建一个内部类DownloadBinder的实例,并在onBind()方法中返回该实例,最后添加内部方法(根据情况自行添加,这里以下载为例)
public class MyService extends Service {
private static final String TAG = "MyService";
private DownloadBinder mBinder = new DownloadBinder();
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
......
class DownloadBinder extends Binder{
/**
* 获取Service实例
* */
public MyService getService(){
return MyService.this;
}
}
private void startDownload(){
Log.d(TAG, "startDownload executed");
}
private int getProgress(){
Log.d(TAG, "getProgress executed");
return 0;
}
}
3.在Activity中创建一个ServiceConnection的匿名类,并重写其onServiceConnected()方法以及onServiceDisconnected()方法(假设绑定成功后立即下载并返回下载进度)
private ServiceConnection connection = new ServiceConnection() {
//与服务成功绑定时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.DownloadBinder binder = (MyService.DownloadBinder) service;
//获取MyService实例
MyService myService = binder.getService();
myService.startDownload();
myService.getProgress();
}
//与服务解除绑定时调用
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
4.通过bindService()方法绑定服务,通过unbindService()方法解绑服务
case R.id.btn_bind_service:
Intent bindIntent = new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
break;
case R.id.btn_unbind_service:
unbindService(connection);
break;
其中BIND_AUTO_CREATE是Context中的字段,它表示活动和服务进行绑定后自动创建服务。这会使得MyService中的onCreate()方法得到执行,而onStartCommand()方法不会执行。
PS:任何一个服务在整个应用程序范围内都是通用的,即MyService不急可以和这里的MainActivity绑定,还可以和任何一个其他的活动进行绑定,而且在绑定完成后它们都可以获取到相同的DownloadBinder实例。
三、服务的生命周期
3.1.如果是bindService()方法启动服务
通过Context的bindService()方法来启动服务时,所有的生命周期方法均只会被执行一次,执行顺序如下:
onCreate -> onBind -> onUnbind -> onDestroy
一旦我们调用bindService()方法获取一个服务的持久连接,就会回调服务中的onBind()方法。如果这个服务之前还没有创建过,onCreate()方法便会先于onBind()方法执行。与此同时,调用者会和Service绑定在一起,调用者退出了或者调用了Context的unbindService()方法,Srevice就会调用onUnbind->onDestroy相应退出。
3.2.如果是startService()方法启动服务
通过Context的startService()方法来启动服务时,onStartCommond()方法可以被反复执行许多次,执行顺序如下:
onCreate -> onStartCommond -> onDestroy
一旦我们调用startService()方法,相应的服务就会启动起来,并回调onStartCommond()方法。如果这个服务之前还没有创建过,onCreate()方法便会先于onStartCommond()方法执行。服务启动之后便会一直保持运行状态,直到stopService()或者stopSelf()方法被调用。注意在这里,虽然每调用一次startService()方法,onStartCommond()方法就会执行一次,但实际上每个服务都只会存在一个实例,也就是说,不管我们调用了多少次onStartCommond()方法,只需要调用一次stopService()或者stopSelf()方法,服务便会停止。
PS:我们完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法,这种时候,我们需要同时调用stopService()方法和unbindService()方法才能使服务的onDestory()方法执行。
四、如何使用前台服务
所谓前台服务,它和普通服务的最大区别就在于,它会一直有一个正在运行的图标在系统的状态栏上显示,下拉状态栏后可以看到更加详细的信息,类似于通知的效果。
1.打开之前自定义的服务类MyService,为其onCreate()方法中添加如下代码:
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = "service";
String channelName = "服务";
int importance = NotificationManager.IMPORTANCE_HIGH;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(channelId,channelName,importance);
notificationManager.createNotificationChannel(notificationChannel);
}
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0, intent,0);
Notification notification = new NotificationCompat.Builder(this,channelId)
.setContentTitle("This is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round))
.setContentIntent(pi)
.build();
startForeground(1,notification);
其实这里的操作步骤和我之前有关Notification的学习笔记是差不多的,总的步骤为:先创建通知管理器,然后创建通知渠道,再创建通知,最后一步不同之前,调用了startForeground()方法,该方法接收两个参数:通知的id、通知notification对象。
五、能够自动停止的异步的服务IntentService
1.新建一个自定义的类去继承IntentService
public class MyIntentService extends IntentService
2.提供一个无参构造器,并在其内部调用父类的有参构造器
public MyIntentService() {
super("MyIntentService");
}
3.重写onHandleIntent()这个抽象方法,在这个方法中可以处理一些具体的逻辑(这里只是简单的打印了下当前线程ID)
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//打印当前线程ID
Log.d(TAG, "Thread id is: "+Thread.currentThread().getId());
}
4.启用IntentService
Intent intentService = new Intent(this,MyIntentService.class);
startService(intentService);
5.最后别忘了在AndroidManifest.xml文件中注册服务
<service
android:name=".MyIntentService"
android:enabled="true"
android:exported="true"></service>