1. 引言概述
Service是android中实现程序后台运行的解决方案,适合去执行那些不需要和用户交互且长期运行的任务。服务的运行不依赖任何用户界面。
Service并不是运行在一个独立的进程当中,依赖于创建服务时所在的应用程序进程。某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止。
1.1 Android多线程编程
相关概念:
- 程序:为了完成特定任务,用某种语言编写的一组指令集合(一组静态代码)
- 进程:运行中的程序,系统调度与资源分配的一个独立单位,操作系统会 为每个进程分配一段内存空间!程序的依次动态执行,经历代码的加载,执行, 执行完毕的完整过程!
- 线程:比进程更小的执行单元,每个进程可能有多条线程,线程需要放在一个 进程中才能执行,线程由程序负责管理,而进程则由系统进行调度!
- 多线程的理解:并行执行多个条指令,将CPU时间片按照调度算法分配给各个 线程,实际上是分时执行的,只是这个切换的时间很短,用户感觉到"同时"而已!
1.2 线程基本用法
创建线程有三种方式:
- 继承Thread类:新建一个类,然后重写父类的run()方法
class MyThread extends Thread {
@override
public void run() {
}
}
......
new MyThread().start();//启动线程
- 实现Runnable接口:这种方法可以降低耦合性。
class Mythread implements Runnable {
@override
public void run() {
}
}
......//启动线程
MyThread mythread = new Mythread();
new Thread(mythread).start();
如果需要采用匿名类的方法:
new Thread(new Runnable() {
public void run() {
}
}).start();
- 实现Callable接口**:与方式2类似
2. Service基本用法
从上图可以看出,Android中使用Service的方式有两种:
StartService()
启动ServiceBindService()
启动Service- 额外还有一种,就是启动Service之后,绑定Service
上述图中Service生命周期的方法介绍如下:
onCreate()
:当Service第一次被创建后立即回调该方法,该方法在整个生命周期 中只会调用一次!onDestory()
:当Service被关闭时会回调该方法,该方法只会回调一次!onStartCommand(intent,flag,startId)
:早期版本是onStart(intent,startId), 当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法, 但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调onStartCommand()
方法!IBinder onBind(intent)
:当一个组件想调用bindService()
与服务绑定是,系统将调用此方法,该方法会返回一个 IBinder对象,app通过该对象与Service组件进行通信!onUnbind(intent)
:当该Service上绑定的所有客户端都断开时会回调该方法!
接下来通过简单实例对图中生命周期进行验证
2.1 StartService 启动Service
- 修改布局文件(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start service" />
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service"/>
</LinearLayout>
- 自定义一个Service,继承Service,
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate executed");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService", "onStartCommand executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService", "onDestroy executed");
}
}
- 修改MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = (Button) findViewById(R.id.start_service);
Button stopService = (Button) findViewById(R.id.stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
//启动服务
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
//停止服务
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
default:
break;
}
}
}
- 在AndroidManifest.xml文件中进行注册(四大组件都要注册)
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
备注:
-
exported
属性表示是否允许除了当前程序之外的其他程序访问这个服务 -
enabled
属性表示是否启用这个服务。 -
服务的启动和停止的方法,主要借助了Intent实现。
-
startService()
和stopService()
方法都是定义在Context类中,可以直接调用这两个方法来**启动和停止服务**。 -
如要服务自行停止,在MyService的任何一个位置调用
stopSelf()
方法就可以停止服务 -
onStartCommand()
必须返回一个整数,用于描述系统应该如何应对服务被杀死的情况,其返回值必须是以下常量之一:START_NOT_STICKEY
如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务START_STICKY
如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用onStartCommand()
,但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)START_REDELIVER_INTENT
如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务
-
在清单文件中进行注册时,有一些属性需要了解以下:
android:name
Service的类名android:label
Service的名字,若不设置默认为Service类名android:icon
Service的图标android:permission
申请此Service的权限,有提供该权限的应用才能控制或连接此应用android:process
表示该服务是否在另一个进程中运行(远程服务),不设置默认为本地服务;remote则设置成远程服务
2.2 BindService启动Servie
前述的方式启动服务之后,活动与服务基本就没有关系了。而用Binder方法可以让活动和服务的关系更加紧密。
使用前基本知识介绍:
- ServiceConnection对象:监听访问者与Service间的连接情况。
- 如果连接成功,回调
onServiceConnected()
- 如果异常终止或其他原因导致Service与访问者断开连接则回调
onServiceDisconnected()
,调用onBindService()
不会调用该方法
- 如果连接成功,回调
onServiceConnected()
方法中有一个IBinder对象,该对象即可实现与被绑定Service 之间的通信!
在开发Service类时,默认需要实现IBinder onBind()
方法,该方法返回的 IBinder对象会传到ServiceConnection对象中的onServiceConnected的参数,我们就可以 在这里通过这个IBinder与Service进行通信!
步骤流程:
- 在自定义的Service中继承Binder,实现IBinder对象
- 通过
onBind()
方法返回自己的IBinder对象 - 绑定该Service的类中定义一个ServiceConnection对象,重写两个方法,
onServiceConnected()
和onServiceDisconnected()
,然后直接读取IBinder传递过来的参数即可。
简单实例:(在MyService里提供一个下载功能,活动中决定何时下载,查看下载进度)
- 修改MyService,创建一个专门的Binder对象对下载功能进行管理
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
new Thread(new Runnable() {
@Override
public void run() {
//执行具体下载任务
}
})
Log.d("MyService", "startDownload");
}
public int getProgress() {
Log.d("MyService", "getProgress executed");
return 0;
}
}
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
...
}
- 在布局中新增两个按钮,用于绑定和解绑:
<Button
android:id="@+id/bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bind Service"/>
<Button
android:id="@+id/unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unbind Service"/>
- 修改MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//绑定该Service的类中定义一个ServiceConnection对象
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = (Button) findViewById(R.id.start_service);
Button stopService = (Button) findViewById(R.id.stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
Button bindService = (Button) findViewById(R.id.bind_service);
Button unbindService = (Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
备注:
bindService()
方法接收3个参数:第一个参数就是刚构建的Intent对象;第二个参数就是前面创建出的ServiceConnection的实例;第三个参数则是一个标志位BIND_AUTO_CREATE
表示活动和服务进行绑定后自动创建服务。这使得MyService中onCreate()
方法得到执行。
- 绑定多客户端情况需要解除所有绑定才会调用
onDestroy()
进行销毁 - 两个实例对服务的销毁方法都很简单,如果我们点击了start Service按钮,又点击了Bind Service按钮,这时候只有将stop Service和unbind Service按钮都点一下才会销毁Service。因为一个Service必须要在既没有和任何Activity关联又处于停止状态的时候才会被销毁。
- Service运行在主线程中,并不是一个新的线程,并不能执行耗时操作
2.3 Service与Thread关系
Service与子线程是完全不同的概念。Android后台是指运行完全不依赖UI。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。因为Service运行在主线程里,可以在Service中再创建一个子线程,在这里处理耗时逻辑。
不在Activity中创建子线程处理耗时操作的原因是Activity很难对Thread进行控制,当Activity被销毁后,就没有其他办法可以再重新获取到之前创建的子线程实例。一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Servic而就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。