Android 四大组件之Service
上篇博客,我们讲解了关于Activity我们应该知道的知识,这篇文章,我们继续往下学习,了解关于Service的“前世今生”。
一 Service的种类
按照运行地点,运行类型,以及使用方式上,我们可以将Service这样划分。
a.按照运行地点
类别 | 区别 | 优点 | 缺点 | 应用 |
本地服务(Local) | 该服务依附在主进程上 | 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,相应bindService会简单很多。 | 主进程被Kill后,服务便会终止。 | 常见的应用如:音乐播放服务。 |
远程服务(Remote) | 该服务是独立的进程 | 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 | 该服务是独立的进程,会占用一定资源,并且IPC稍微麻烦一点。 | 一些提供系统服务Service,这种Service是常驻的。 |
b.运行类型
类别 | 区别 | 应用 |
前台服务 | 会在通知一栏显示 ONGOING 的 Notification, | 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。 |
后台服务 | 默认的服务即为后台服务,即不会在通知一栏显示 ONGOING的 Notification。 | 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。 |
服务需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为前台服务。
c.使用方式
类别 | 区别 |
startService 启动的服务 | 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService |
bindService 启动的服务 | 该方法启动的服务要进行通信。停止服务使用unbindService |
startService 同时也 bindService 启动的服务 | 停止服务应同时使用stopService与unbindService |
二 Service 与 Thread 的区别
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。
我们可以将Service理解成比Thread更高级的东西。Thread在一个Activity中启动后,无法与另外一个Activity通信,因为其他Activity无法知道Thread的存在。而Service是与Activity同等重要的组件,它在后台默默的位应用提供服务,任何Activity需要“接收”它的服务都可以。另外Service更不容易被系统杀死,可以长期的在后台执行任务。
三 Service的生命周期
Service常用的方法有onCreate,onStart,onDestroy,onBind 。根据不同的使用情况,我们可以这样理解:
(1). 被启动的服务的生命周期:如果一个Service被Activity调用startService启动的话,onCreate,onStart方法会依次调用,而如果该Activity多次调用startService的话,由于该Service已经存在,onCreate不会再被调用,而onStart会被再次调用。并且系统只创建一个实例。该Service会一直存在在后台,而与调用它的Activity无关,直到stopService或者stopSelf被调用或者被系统杀死。
(2).被绑定的服务的生命周期:如果Service被一个Activity调用bindService方法启动,不管调用bindService几次,onCreate方法都只会调用一次,因为Service已经存在,而onStart方法始终不会调用(需牢记).当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
(3).被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
(4).当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动)时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程(若服务先被启动,然后绑定到一个Activity上,则该Activity的销毁不会造成服务被清除)。
(5).注意:同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;另外,当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
四 Local 与 Remote 服务绑定
记得在 Androidmanifest.xml 中注册 service。
(1).Local 服务绑定:Local 服务的绑定较简单,首先在 Service 中我们需要实现 Service 的抽象方法 onBind(所以该方法一定要重写),并返回一个实现 IBinder 接口的对象。Binder可以理解为C#中的代码块。Binder对象在Service中实例化,当与Activity建立连接后,可以在Activity中调用返回到Activity中的binder,从而可以调用binder包含的方法。
Service的代码:
package cn.tyssen.test;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class LocalService extends Service {
/**
* 在 Local Service 中我们直接继承 Binder 而不是 IBinder,因为 Binder 实现了 IBinder 接口,这样我们可以少做很多工作。
* @author newcj
*/
public class SimpleBinder extends Binder{
/**
* 获取 Service 实例
* @return
*/
public LocalService getService(){
return LocalService.this;
}
public int add(int a, int b){
return a + b;
}
}
public SimpleBinder sBinder;
@Override
public void onCreate() {
super.onCreate();
// 创建 SimpleBinder
sBinder = new SimpleBinder();
}
@Override
public IBinder onBind(Intent intent) {
// 返回 SimpleBinder 对象
return sBinder;
/*Service.onBind如果返回null,则调用 bindService 会启动 Service,但不会连接上 Service,因此 ServiceConnection.onServiceConnected 不会被调用,但你任然需要使用 unbindService 函数断开它,这样 Service 才会停止。*/
}
}
上面onBind(Intent) 这个方法 返回了一个实现了 IBinder 接口的对象,这个对象将用于绑定Service 的 Activity 与 Local Service 通信。
下面是 Activity 中的代码:
package cn.tyssen.test;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
public class Main extends Activity {
private final static String TAG = "SERVICE_TEST";
private ServiceConnection sc;
private boolean isBind;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sc = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalService.SimpleBinder sBinder = (LocalService.SimpleBinder)service;
Log.v(TAG, "3 + 5 = " + sBinder.add(3, 5));
Log.v(TAG, sBinder.getService().toString());
}
};
findViewById(R.id.btnBind).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
bindService(new Intent(Main.this, LocalService.class), sc, Context.BIND_AUTO_CREATE);
isBind = true;
}
});
findViewById(R.id.btnUnbind).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//unbindService 解除绑定,参数则为之前创建的 ServiceConnection 接口对象。//另外,多次调用 unbindService 来释放相同的连接会抛出异常,因此我创建了一个 //boolean 变量来判断是否 unbindService 已经被调用过。
if(isBind){
unbindService(sc);
isBind = false;
}
}
});
}
}
(2).Remote 服务绑定:Remote 的服务绑定由于服务是在另外一个进程,因此需要用到 android 的 IPC 机制。我会再写另一系列文章讲Android的IPC机制与常用IPC方法。
五 如何使用startService 或 bindService
如果你只是想要启动一个后台服务长期进行某项任务那么使用 startService 便可以了。如果你想要与正在运行的 Service 取得联系,那么有两种方法,一种是使用 broadcast ,另外是使用 bindService ,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且 BroadcastReceiver 本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用 bindService(这个时候你便同时在使用 startService 和 bindService 了,这在 Activity 中更新 Service 的某些运行状态是相当有用的)。另外如果你的服务只是公开一个远程接口,供连接上的客服端(android 的 Service 是C/S架构)远程调用执行方法。这个时候你可以不让服务一开始就运行,而只用 bindService ,这样在第一次 bindService 的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是Remote Service,那么该效果会越明显(当然在 Service 创建的时候会花去一定时间,你应当注意到这点)。
在 AndroidManifest.xml 里 Service 元素的常见选项
android:name ------------- 服务类名
android:label -------------- 服务的名字,如果此项不设置,那么默认显示的服务名则为类名
android:icon -------------- 服务的图标
android:permission ------- 申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process ---------- 表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字
android:enabled ---------- 如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false
android:exported --------- 表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false