【Android】Service那点事儿

1.Service简介

服务是一个应用程序组件,可以在后台执行长时间运行的操作,不提供用户界面。一个应用程序组件可以启动一个服务,它将继续在后台运行,即使用户切换到另一个应用程序。此外,一个组件可以绑定到一个服务与它交互,甚至执行进程间通信(IPC)。例如,一个服务可能处理网络通信、播放音乐、计时操作或与一个内容提供者交互,都在后台执行。


2.Service的种类

  • 按运行地点分类:
类别 区别 优点 缺点 应用
本地服务(Local Service) 该服务依附在主进程上 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。 主进程被Kill后,服务便会终止。 如:音乐播放器播放等不需要常驻的服务。
远程服务(Remote Service) 该服务是独立的进程 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。 一些提供系统服务的Service,这种Service是常驻的。
  • 按运行类型分类:
类别 区别 应用
前台服务 会在通知栏显示onGoing的 Notification 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。
后台服务 默认的服务即为后台服务,即不会在通知一栏显示 onGoing的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。
  • 按使用方式分类:
类别 区别
startService启动的服务 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService。
bindService启动的服务 方法启动的服务要进行通信。停止服务使用unbindService。
同时使用startService、bindService 启动的服务 停止服务应同时使用stopService与unbindService。

3.Service的生命周期

不同启动方式下生命周期对比.png

通过这个图可以看到,两种启动Service的方式以及他们的生命周期,bindService的不同之处在于当绑定的组件销毁后,对应的service也就被kill了。

  • 被启动的服务的生命周期
      一个Service被使用startService方法启动,不管是否调用了bindService(绑定服务)或unbindService(解除绑定服务)到该Service,该Service都会在后台运行并不受影响。
      一个Service被使用startService方法启动多少次,onCreate方法只会调用一次,onStartCommand方法将会被调用多次(与startService的次数一致),且系统只会创建一个Service实例(结束该Service也只需要调用一次stopService),该Service会一直在后台运行,直至调用stopService或调用自身的stopSelf方法。

注:在系统资源不足的情况下,服务有可能被系统结束(kill);

  • 被绑定的服务的生命周期
      如果一个Service在某个Activity中被调用bindService方法启动,不论bindService被调用几次,Service的onCreate方法只会执行一次,同时onStartCommand方法始终不会调用。
      当建立连接后,Service会一直运行,除非调用unbindService来接触绑定、断开连接或调用该Service的Context不存在了(如Activity被Finish——即通过bindService启动的Service的生命周期依附于启动它的Context),系统在这时会自动停止该Service。

  • 被启动又被绑定的服务的生命周期
      当一个Service在被启动(startService)的同时又被绑定(bindService),该Service将会一直在后台运行,并且不管调用几次,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数一致(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会停止Service,必须调用stopService或Service自身的stopSelf来停止服务。

官方原文:
If you do allow your service to be started and bound,then when then service has been started,the System does not destory the service when all clients unbind.Instead,you must explicitly stop the service,by calling stopSelf() or stopService().
如果你同意你的服务被开启和绑定,然后当服务被开启的时候,当所有的客户端都解除对服务的绑定android操作系统也不会销毁这个服务,相反的你必须显示的调用stopSelf()或者stopService()方法来停止服务。
什么情况下使用:
如果你想要与正在运行的Service取得联系,那么有两种方法,一种是使用广播,另外一种方法就是使用bindService来建立联系,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且BroadcastReceiver本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用bindService(这个时候你便同时在使用startService和bindService了,这在Activity中更新Service的某些运行状态是相当有用的)。

  • 当服务被停止时
      当一个服务被终止(stopServicestopSelfunbindService)时,onDestory方法将会被调用——所以我们需要在该方法中清除一些工作(依附该Service生命周期的,如:停止在Service中创建并运行的线程)。

特别注意:
1.在使用startService方法启动服务后,一定要调用stopService方法来停止该服务(同上,可以在Activity的onDestory中来停止服务);
2.在某处调用bindService绑定Service的时候,要在对应的某处调用unbindService来解除绑定(如在Activity中绑定了Service,可以在onDestory中来解除绑定——虽然绑定的Service会在Activity结束时自动解除、停止);
3.如果同时使用startServicebindService方法启动Service,需要终止该Service时,要调用stopServiceunbindService方法(unbindService依附于启动它的Context,startServicec并不依附于启动它的Context。如果先调用unbindService,这时服务并不会被终止,当调用stopService后,服务才会被终止;如果先调用stopService,服务也不会被终止,当调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish掉了)服务才会自动停止);
4.当手机屏幕发生旋转时,如果Activity设置的是自动旋转的话,在旋转的过程中,Activity会重新创建,那么之前通过bindService建立的连接便会断开(之前的Context不存在了),服务也会被自动停止。


4.Service的使用

在新建一个Service后,记得在AndroidManifest.xml中注册Service,在application内添加需要注册的Service信息:

<service android:name=".service.PlayerService"
    android:label="PlayerService"
    android:exported="true" />

Service示例如下:

public class PlayerService extends Service{

    @Override
    public void onCreate() {
        super.onCreate(); 
    } 

    /** 
    * onBind 是 Service 的虚方法,因此我们不得不实现它。
    * 返回 null,表示客服端不能建立到此服务的连接。
    */ 
    @Override
    public IBinder onBind(Intent intent) { 
        return new MyBinder();
    }

    // 已取代onStart方法--onStart方法是在Android2.0之前的平台使用的.
    // 在2.0及其之后,则需重写onStartCommand方法,同时,旧的onStart方法则不会再被直接调用
    // (外部调用onStartCommand,而onStartCommand里会再调用 onStart。在2.0之后,
    // 推荐覆盖onStartCommand方法,而为了向前兼容,在onStartCommand依然会调用onStart方法。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId); 
    } 

    @Override
    public boolean onUnbind(Intent intent) { 
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy(); 
    } 
    // IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程
    // 调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。
    // 不要直接实现这个接口,而应该从Binder派生。
    // Binder类已实现了IBinder接口
    class MyBinder extends Binder { 
        /** 
        * 获取Service的方法
        * @return 返回PlayerService
        */ 
        public  PlayerService getService(){
            return PlayerService.this;
        }
    }
}
  • startService启动服务
      通过startService()启动的服务处于“启动”状态,一旦启动,Service就在后台运行,即使启动它的应用组件已经被销毁了。通常started状态的Service执行单任务并且不返回任何结果给启动者(如当下载或上传一个文件,当这项操作完成时,Service应该停止它本身),示例如下:
public class PlayerActivity extends Activity{
  private Intent intent;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    intent = new Intent(this, PlayerService.class);
    startService(intent);// 启动服务
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    stopService(intent);// 在退出Activity时停止该服务
  }
}
  • bindService绑定服务
      一个绑定的Service提供一个允许组件与Service交互的接口,可以发送请求、获取返回结果,还可以通过跨进程通信来交互(IPC)。绑定的Service只有当应用组件绑定后才能运行,多个组件可以绑定一个Service,当调用unbindService()方法时,这个Service就会被销毁了。
  • Local(本地服务)的绑定
public class PlayerActivity extends Activity{
   @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Intent intent = new Intent(this, PlayerService.class);
    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);//绑定目标Service
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    unbindService(serviceConnection);// 解除绑定,断开连接
  }
  // 在Activity中,我们通过ServiceConnection接口来取得建立连接与连接意外丢失的回调
  ServiceConnection serviceConnection = new ServiceConnection() {            @Override
    public void onServiceConnected(ComponentName name, IBinder service){
      // 建立连接
      // 获取服务的操作对象
               PlayerService.MyBinder binder = (PlayerService.MyBinder)service;
               binder.getService();// 获取到的Service即PlayerService
    } 
    @Override
    public void onServiceDisconnected(ComponentName name) {
      // 连接断开
    }
  };
}


Service的onBind如果返回null,则调用bindService会启动Service,但不会连接上Service因此ServiceConnection.onServiceConnected不会被调用,但仍然需要使用unbindService方法来断开连接,这样Service才会停止。


  Service与Activity一样都存在与当前进程的主线程中,所以,一些阻塞UI的操作(如在Service进行网络请求等)不能放在Service里进行。如果在Service里进行一些耗CPU和耗时操作,可能会引发ANR警告,这时应用会弹出是强制关闭还是等待的对话框。所以,对Service的理解就是和Activity平级的,只不过是看不见的,在后台运行的一个组件,这也是为什么和Activity同被说为Android的基本组件。


5. AndroidManifest.xml中Service元素常见属性

  • andorid:name

服务类名。可以是完整的包名+类名。也可使用.代替包名。

  • adroid:exported

其他应用能否访问该服务,如果不能,则只有本应用或有相同用户ID的应用能访问。默认为false。

  • android:enabled

标识服务是否可以被系统实例化。true--系统默认启动,false--不启动。(默认值为true)

  • android:label

显示给用户的服务名称。如果没有进行服务名称的设置,默认显示服务的类名。

  • android:process

服务所运行的进程名。默认是在当前进程下运行,与包名一致。如果进行了设置,将会在包名后加上设置的集成名。
如果名称设置为冒号 :开头,一个对应用程序私有的新进程会在需要时和运行到这个进程时建立。如果名称为小写字母开头,服务会在一个相同名字的全局进程运行,如果有权限这样的话。这允许不同应用程序的组件可以分享一个进程,减少了资源的使用。

  • android:icon

服务的图标。

  • android:permission

申请使用该服务的权限,如果没有配置下相关权限,服务将不执行,使用startService()bindService()方法将都得不到执行。


6.Thread与Service的区别

  • Thread的优先级低于Service的优先级,在系统资源紧张的情况下,优先杀死前者;
  • 在Thread启动后,如果退出当前Activity,则无法对已经启动的Thread进行操作,而服务则可以——因此可以在服务中启动Thread来解决该问题;
  • Service可以进行跨进程访问,而Thread不行;
  • 在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用Service;
  • 同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需需要长时间运行的情况下使用Thread;
  • 如果任务占用CPU时间多,资源大的情况下,要使用Thread。
  • 拓展阅读
    Thread和Service应用场合的区别
    Android中Service与Thread的区别
    android-Service和Thread的区别

7.Service系列拓展阅读

【Android】Service前台服务的使用
【Android】远程服务(Remote Service)的使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容