一、 Service的种类 java
按运行地点分类:
类别 区别 优势 缺点 应用
本地服务(Local) 该服务依附在主进程上, 服务依附在主进程上而不是独立的进程,这样在必定程度上节约了资源,另外Local服务由于是在同一进程所以不须要IPC,也不须要AIDL。相应 bindService会方便不少。 主进程被Kill后,服务便会终止。 很是常见的应用如:HTC的音乐播放服务,每天动听音乐播放服务。
远程服务(Remote) 该服务是独立的进程, 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。因为是独立的进程,所以在Activity所在进程被 Kill的时候,该服务依然在运行,不受其余进程影响,有利于为多个进程提供服务具备较高的灵活性。 该服务是独立的进程,会占用必定资源,而且使用AIDL进行IPC稍微麻烦一点。 一些提供系统服务的Service,这种Service是常驻的。
其实remote服务仍是不多见的,而且通常都是系统服务。 android
按运行类型分类:
类别 区别 应用
前台服务 会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有必定的通知做用。常见的如音乐播放服务。
后台服务 默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不须要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。
有同窗可能会问,后台服务咱们能够本身建立 ONGOING 的 Notification 这样就成为前台服务吗?答案是否认的,前台服务是在作了上述工做以后须要调用 startForeground ( android 2.0 及其之后版本 )或 setForeground (android 2.0 之前的版本)使服务成为 前台服务。这样作的好处在于,当服务被外部强制终止掉的时候,ONGOING 的 Notification 任然会移除掉。 服务器
按使用方式分类:
类别 区别
startService 启动的服务 主要用于启动一个服务执行后台任务,不进行通讯。中止服务使用stopService
bindService 启动的服务 该方法启动的服务要进行通讯。中止服务使用unbindService
startService 同时也 bindService 启动的服务 中止服务应同时使用stepService与unbindService
以上面三种方式启动的服务其生命周期也有区别,将在随后给出。 架构
二、Service 与 Thread 的区别
不少时候,你可能会问,为何要用 Service,而不用 Thread 呢,由于用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。 app
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。能够用 Thread 来执行一些异步的操做。
2). Service:Service 是android的一种机制,当它运行的时候若是是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。若是是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。所以请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有! 框架
既然这样,那么咱们为何要用 Service 呢?其实这跟 android 的系统机制有关,咱们先拿 Thread 来讲。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 以后,若是你没有主动中止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。所以这里会出现一个问题:当 Activity 被 finish 以后,你再也不持有该 Thread 的引用。另外一方面,你没有办法在不一样的 Activity 中对同一 Thread 进行控制。 异步
举个例子:若是你的 Thread 须要不停地隔一段时间就要链接服务器作某种同步的话,该 Thread 须要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制以前建立的 Thread。所以你便须要建立并启动一个 Service ,在 Service 里面建立、运行并控制该 Thread,这样便解决了该问题(由于任何 Activity 均可以控制同一 Service,而系统也只会建立一个对应 Service 的实例)。 ide
所以你能够把 Service 想象成一种消息服务,而你能够在任何有 Context 的地方调用 Context.startService、Context.stopService、 Context.bindService,Context.unbindService,来控制它,你也能够在 Service 里注册 BroadcastReceiver,在其余地方经过发送 broadcast 来控制它,固然这些都是 Thread 作不到的。 函数
三、Service的生命周期
onCreate onStart onDestroy onBind
1). 被启动的服务的生命周期:若是一个Service被某个Activity 调用 Context.startService 方法启动,那么不论是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都 在后台运行。若是一个Service被startService 方法屡次启动,那么onCreate方法只会调用一次,onStart将会被调用屡次(对应调用startService的次数),而且系统只会建立 Service的一个实例(所以你应该知道只须要一次stopService调用)。该Service将会一直在后台运行,而无论对应程序的 Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。固然若是系统资源不足,android系统也可能结束服 务。 性能
2). 被绑定的服务的生命周期:若是一个Service被某个Activity 调用 Context.bindService 方法绑定启动,无论调用 bindService 调用几回,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当链接创建以后,Service将会一直运行,除非调用 Context.unbindService 断开链接或者以前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动中止Service,对应onDestroy将被调用。
3). 被启动又被绑定的服务的生命周期:若是一个Service又被启动又被绑定,则该Service将会一直在后台运行。而且无论如何调用,onCreate 始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会中止 Service,而必须调用 stopService 或 Service的 stopSelf 来中止服务。
4). 当服务被中止时清除服务:当一个Service被终止(一、调用stopService;二、调用stopSelf;三、再也不有绑定的链接(没有被启 动))时,onDestroy方法将会被调用,在这里你应当作一些清除工做,如中止在Service中建立并运行的线程。
特别注意:
一、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自 动解除,而且Service会自动中止);
二、你应当注意 使用 startService 启动服务以后,必定要使用 stopService中止服务,无论你是否使用bindService;
三、同时使用 startService 与 bindService 要注意到,Service 的终止,须要unbindService与stopService同时调用,才能终止 Service,无论 startService 与 bindService 的调用顺序,若是先调用 unbindService 此时服务不会自动终止,再调用 stopService 以后服务才会中止,若是先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 以前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)以后服务才会自动中止;
四、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时若是你的 Activity 若是会自动旋转的话,旋转实际上是 Activity 的从新建立,所以旋转以前的使用 bindService 创建的链接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
五、在 sdk 2.0 及其之后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过以前的 onStart 任然有效。这意味着,若是你开发的应用程序用的 sdk 为 2.0 及其之后的版本,那么你应当使用 onStartCommand 而不是 onStart。
四、startService 启动服务
想要用 startService 启动服务,无论Local 仍是 Remote 咱们须要作的工做都是同样简单。固然要记得在 Androidmanifest.xml 中注册 service。
根据上面的生命周期,咱们便会给出 Service 中的代码框架:
package com.newcj.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class LocalService1 extends Service {
/** * onBind 是 Service 的虚方法,所以咱们不得不实现它。 * 返回 null,表示客服端不能创建到此服务的链接。 */ @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); } @Override public void onDestroy() { super.onDestroy(); }
|
|
}
对应生命周期系统回调函数上面已经说明,在对应地方加上适当的代码便可。下面是启动与中止 Service 的代码:
// 启动一个 Activity
startActivity(new Intent(this, LocalService1.class));
...
// 中止一个 Activity
stopService(new Intent(this, LocalService1.class));
对应的 Intent 为标志服务类的 Intent。
五、Local 与 Remote 服务绑定
一样记得在 Androidmanifest.xml 中注册 service
1). Local 服务绑定:Local 服务的绑定较简单,首先在 Service 中咱们须要实现 Service 的抽象方法 onBind,并返回一个实现 IBinder 接口的对象。
Service 中的代码:
package com.newcj.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; } |
|
}
上面的代码关键之处,在于 onBind(Intent) 这个方法 返回了一个实现了 IBinder 接口的对象,这个对象将用于绑定Service 的 Activity 与 Local Service 通讯。下面是 Activity 中的代码:
package com.newcj.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) { if(isBind){ unbindService(sc); isBind = false; } } }); } |
|
}
在 Activity 中,咱们经过 ServiceConnection 接口来取得创建链接 与 链接意外丢失的回调。bindService有三个参数,第一个是用于区分 Service 的Intent 与 startService 中的 Intent 一致,第二个是实现了 ServiceConnection 接口的对象,最后一个是 flag 标志位。有两个flag,BIND_DEBUG_UNBIND 与 BIND_AUTO_CREATE,前者用于调试(详细内容能够查看javadoc 上面描述的很清楚),后者默认使用。unbindService 解除绑定,参数则为以前建立的 ServiceConnection 接口对象。另外,屡次调用 unbindService 来释放相同的链接会抛出异常,所以我建立了一个 boolean 变量来判断是否 unbindService 已经被调用过。
运行结果:
2). Remote 服务绑定:Remote 的服务绑定因为服务是在另一个进程,所以须要用到 android 的 IPC 机制。这将又是一个很长的话题,所以,我打算写另一篇 android 的 IPC 机制分析 ,并在其中进行详述,而后在这里更新连接,敬请关注。
特别注意:
一、Service.onBind若是返回null,则调用 bindService 会启动 Service,但不会链接上 Service,所以 ServiceConnection.onServiceConnected 不会被调用,但你任然须要使用 unbindService 函数断开它,这样 Service 才会中止。
六、建立前台服务
前台服务的优势上面已经说明,但设置服务为前台服务,咱们须要注意在 sdk 2.0 及其之后版本使用的方法是 startForeground 与 stopForeground,以前版本使用的是 setForeground ,所以若是你应用程序的最低运行环境要求是 2.0,那么这里能够直接运用新方法,若是运行环境是2.0如下,那么为了保证向后兼容性,这里必须使用反射技术来调用新方法。
下面是我仿照 ApiDemos 从新敲的代码,对某些地方进行了修改,所以更具备说明性:
特别注意:
一、使用 startForeground ,若是 id 为 0 ,那么 notification 将不会显示。
七、在什么状况下使用 startService 或 bindService 或 同时使用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