一、Service简介

  Android中服务是运行在后台的东西,级别与activity相同(同属四大组件)。既然说service是运行在后台的服务,那么它就是不可见的,没有界面的东西。你可以启动一个服务Service来管理播放音乐(注意不是在Service里面播放),或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作。对于需要长期运行,例如播放音乐、长期和服务器的连接,即使已不是屏幕当前的activity仍需要运行的情况,采用服务方式。服务可用于以下的场景:

  1、用户离开activity后,仍需继续工作,例如从网络下载文件,播放音乐;

  2、无论activity出现(重现)或离开,都需持续工作,例如网络聊天应用;

  3、需要定时连接网络服务,例如微博或者新闻客户端的消息推送;

  4、跨进程的工作。

二、基础知识

  按运行地点分类,Service一般分为两种:

类别 区别  特性 最长生命周期  应用
本地服务(Local) 该服务依附在启动它的程序的主进程上 服务依附在启动它的程序的主进程上而不是独立的进程,也不是独立的线程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。注意,“本地服务”在这里的意思是说,这个服务只为这个程序工作,而不是说这个服务不能联网。 本地服务最终是依附在启动程序的进程上的。也就是说,本地服务的生命周期,最多能延续到启动它的进程被结束之前(注意是进程结束而不是Activity结束)。而实际情况会依据服务类型而定。 音乐播放服务,微博新消息提醒,QQ保持通信,广告后台推送(终于知道广告为啥会突然出现了吧)
远程服务(Remote) 该服务是独立的进程 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 正如特性中所说的,它是独立的进程,不受其它程序的约束。  一些提供系统服务的Service,这种Service是常驻的。一般的应用开发中,极少用到远程服务

  这里要强调一遍:

    当作为Local Service时,Service不是一个单独的进程,也不是一个线程;

    当作为Remote Service时,Service是一个独立的进程。

  在Service中,也跟Activity一样,都有onCreate()、onDestroy()等方法,同样,我们也能在里面自建方法。当作为Local Service启动,我们调用Service中的方法的时候,Service并不会新启动一个进程,亦或者是另开一条线程来运行方法中的命令,而是立即穿插到当前运行的Activity主线程中。也就是说每当Service被调用,都会阻塞当前的Activity主线程,直到Service方法执行完毕,Activity才有机会继续执行。因此,如果在Service中涉及长时间的任务,开发者应当自行另开线程,以确保主线程能够保持畅通。这点有异于平常的思维,因此大家需要花点时间去理解。

  从这里开始,本文章不再讨论远程服务(Remote Service)。因为远程服务涉及到众多的IPC机制与AIDL知识,这又是很长的篇章。以下文段,默认以本地服务(Local Service)为讨论对象。

  按使用方式分类:

类别 区别
startService 启动的服务 主要用于启动一个服务执行后台任务,并且Activity与Service之间不直接进行通信。停止服务使用stopService()。
什么叫“不直接进行通信”?也就是说Activity与Service之间无法通过代码传递的方式来交流,但我们可以通过广播(BroadcastReceiver),或者SharedPreferences文件来交流,这就叫“不直接通信/间接通信”。
bindService 启动的服务 主要用于启动一个服务执行后台任务,并且Activity与Service之间需要直接进行通信。停止服务使用unbindService()。
startService 同时也 bindService 启动的服务 停止服务应同时使用stopService()与unbindService()

  以上面三种方式启动的服务其生命周期也有区别,将在随后给出。

三、Service 与 Thread 的区别

  此时你可能会问:为什么要用 Service,而不用 Thread 呢?因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。

  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 的实例)。

  因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

四、Service的生命周期

  先看startService和bindService的生命周期图:

  在Service中,有四个主要的方法:onCreate()、onStartCommand()、onDestroy()和onBind()。其中,onBind()为抽象类,必须实现。另外注意,onStartCommand()方法是用于取代旧API中的onStart()方法。

  1). 在Activity中使用startService()方法启动:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被使用startService 方法多次启动,那么onCreate方法也只会调用一次,onStartCommand()将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或调用Service自身的stopSelf方法,或者启动程序被毁灭。当然如果系统资源不足,Android系统也可能结束服务,该Service中的onDestroy将被调用。因此,使用此方法启动的服务与onBind()方法没有关系。

  2). 在Activity中使用bindService()方法启动:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,该Service中的onDestroy将被调用。因此,使用此方法启动的服务与onStartCommand()方法没有关系。

  3). 在Activity中使用了上述的两种方法启动:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

  4). 当服务被停止时清除服务:当一个Service被终止时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程(这点很重要,不然那些在Thread中的循环工作就没有机会终止了)。

  流程总结:

    context.startService() ->onCreate()(仅一次) ->onStart() ->Service running--调用context.stopService() ->onDestroy()

    context.bindService() ->onCreate()(仅一次) ->onBind() ->Service running--调用context.onUnbind() ->onDestroy()

    如果没有startService,而调用stopService不会出错。

    如果没有bindService,而调用unbindService就会出错。

  特别注意:

  1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);

  2、你应当注意使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService(除非你打算制造流氓程序);

  3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService都调用,才能终止 Service,不管 startService 与 bindService 的调用顺序;

  4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同;

  5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用onStartCommand 而不是 onStart。

五、注册服务

  想要用 startService  启动服务,不管Local 还是 Remote 我们需要做的工作都是一样简单。当然要记得在 Androidmanifest.xml 中注册 service。注册位置在<manifest></manifest>标签中的<application></application>标签中,与<activity></activity>同级:

<service android:name=".TestService"/>

  简单的注册方式如上。其中,name为该类的类名。

在 AndroidManifest.xml 里 Service 元素的常见选项

  android:name  -------------  服务类名

  android:label  --------------  服务的名字,如果此项不设置,那么默认显示的服务名则为类名

  android:icon  --------------  服务的图标

  android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务

  android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字

  android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false

  ndroid:exported  ----------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false

六、startService 启动服务

  该程序的功能,是每当Activity变得不可见的时候,便在后台进行Log计数输出;而当Activity变得可见,计数停止。详细方法已在注释中体现。

  首先要在 And roidManifest.xml 中绑定Service:

<service android:name=".TestService"/>

  MainActivity.java

package com.plusjun.mytestservice;import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}/* 当该Activity成为焦点(在页面栈顶层)时,onResume()会被调用* 详见Activity生命周期* http://blog.csdn.net/theworldsong/article/details/9121775*/@Overrideprotected void onResume() {super.onResume();//终止服务stopService(new Intent(this,TestService.class));Log.i("Activity onResume()","finish");}/* 当该Activity失去焦点时,onPause()会被调用*/@Overrideprotected void onPause() {super.onPause();//开始服务startService(new Intent(this,TestService.class));Log.i("Activity onPause()","finish");}
}

  TestService.java

package com.plusjun.mytestservice;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;public class TestService extends Service{boolean use = true;int i = 0;/** 若使用startService启动Service* 则完全涉及不到onBind()方法* 但由于onBind()方法是抽象(abstract)方法* 因此必须实现*/@Overridepublic IBinder onBind(Intent intent) {return null;}/** Service启动后* 不断从0开始计数,一秒钟增加一次* 用布尔值“use”来判断是否需要继续计数*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {use = true;new Thread(){@Overridepublic synchronized void run() {while(use){Log.i("i","" + i);i++;try {wait(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}.start();return 0;}/** 使用stopService()方法后* 该方法会被调用* 更改布尔值“use”,表示不再需要计数*/@Overridepublic void onDestroy() {use = false;Log.i("Service onDestroy()","finish");}
}

七、bindService启动服务

  bindService有三个参数,第一个是用于区分 Service 的Intent,与 startService 中的 Intent 一致,第二个是实现了 ServiceConnection 接口的对象,最后一个是 flag 标志位。有两个flag,BIND_DEBUG_UNBIND 与 BIND_AUTO_CREATE,前者用于调试(详细内容可以查看javadoc 上面描述的很清楚),后者默认使用。unbindService 解除绑定,参数则为之前创建的 ServiceConnection 接口对象。另外,多次调用 unbindService 来释放相同的连接会抛出异常,因此我创建了一个 boolean 变量来判断是否 unbindService 已经被调用过。

  ServiceConnection 一旦生成,就会开始监听bindService的启动,然后便会接收onBind()所返回的IBinder接口继承的对象。其中,onServiceConnected()方法会在绑定成功后立即调用,onServiceDisconnected()方法会在解除绑定后,不一定马上调用。你可能会想,如果执行unbindService之后不就自动调用ServiceConnection接口里的onServiceDisconnected方法了吗。 其实不然,查看api,你会发现,ServiceConnection接口里的onServiceDisconnected方法只会在Service被停止或者被系统杀死以后调用。 也就是说你执行unbindService 只是告诉系统你已经和这个服务没有关系了,系统在内存不足的时候可以优先杀死这个服务而已。 换言之,执行unbindService方法后,Service可能还在运行。同样,通过onBind()传来的IBinder接口继承的对象,仍然可以操作,但这是极为不安全的,因为这个Service可能随时消失。

同样,首先注册Service:

<service android:name=".TestService2"/>

MainActivity.java

package com.plusjun.mytestservice2;import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;public class MainActivity extends Activity {Button bt1,bt2,bt3,bt4;ServiceConnection sc;TestService2.MyBinder mb;TestService2 ts;//作是否已经绑定的标记boolean isBind = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();/** ServiceConnection对象生成后* 便会开始监听bindService()方法的调用* 一旦bindService()方法调用* onServiceConnected方法便会接收Service中onBind()方法返回的IBinder数据*/sc = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//记得要做类型转换mb = (TestService2.MyBinder)service;//取出TestService2类的对象ts = mb.getService();}};bt1.setOnClickListener(new OnClickListener() {public void onClick(View arg0) {bindService(new Intent(MainActivity.this, TestService2.class), sc, Context.BIND_AUTO_CREATE);isBind = true;}});bt2.setOnClickListener(new OnClickListener() {public void onClick(View arg0) {if(isBind){unbindService(sc);isBind = false;}}});bt3.setOnClickListener(new OnClickListener() {public void onClick(View arg0) {/** 前面已经提到过,解绑后的短时间内* Service可能仍能使用* 但这是极为不安全的*/if(isBind){ts.logApple();}}});bt4.setOnClickListener(new OnClickListener() {public void onClick(View arg0) {if(isBind){ts.logOrange();}}});}private void init(){bt1 = (Button)findViewById(R.id.bt1);bt2 = (Button)findViewById(R.id.bt2);bt3 = (Button)findViewById(R.id.bt3);bt4 = (Button)findViewById(R.id.bt4);}
}

TestService2.java

package com.plusjun.mytestservice2;import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;public class TestService2 extends Service{MyBinder binder = new MyBinder();@Overridepublic IBinder onBind(Intent intent) {/** 返回一个实现IBinder接口的对象*/return binder;}//自定义的方法public void logApple(){Log.i("LogApple","LogApple");}//自定义的方法public void logOrange(){Log.i("LogOrange","LogOrange");}/** 实现Binder接口,在里面添加自己需要的方法* 这里我们提供一个返回TestService2对象的方法* 这样就能在Activity中对TestService2进行操作了*/public class MyBinder extends Binder{TestService2 getService(){return TestService2.this;}}
}

  特别注意:

  Service.onBind如果返回null,则调用 bindService 会启动 Service,但ServiceConnection.onServiceConnected 不会被调用,但你任然需要使用 unbindService 函数断开它,这样 Service 才有机会停止。

还有一点,onBind的绑定过程与Activity是异步的,看这里:http://stackoverflow.com/questions/10184159/android-service-mboundservice-null-in-onresume


以上部分内容转载或参考来源如下:

http://www.cnblogs.com/newcj/archive/2011/05/30/2061370.html

http://stackoverflow.com/questions/10184159/android-service-mboundservice-null-in-onresume

在此表示感谢。
转载请注明来源,版权归原作者所有,未经同意严禁用于任何商业用途。
微博:http://weibo.com/theworldsong
邮箱:theworldsong@foxmail.com

Service(服务)之 Local Service(本地服务)相关推荐

  1. 本地服务(local Service)的实现

    定义; --后台运行.不可见.没有界面 --优先级高于Activity 用途: --播放音乐.记录地理信息位置的改变.监听某种动作 注意: --运行在主线程,不能用来做耗时的请求活动 --可以在服务中 ...

  2. android应用层服务——binderService,StartService,aidl,本地服务和远程服务

    android中应用层的服务级别与activity差不多.service都是运行在后台的服务,那么它就是不可见的,没有界面的东西.你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改 ...

  3. 【Service】bindService:绑定本地服务和远程服务示例

    绑定本地服务 AndroidManifest.xml中声明服务: <service android:name=".TestLocalService"><inten ...

  4. dubbo+zk+apollo微服务,联调调用本地服务

    今天遇到个好玩的事情,和前端联调.每次查出的结果不一样,swagger测试的结果也不一样.debug后,有好几次不进入断点 啥情况?我没改吗.可是有时候查询结果又是对的. 后来,才反应过来.部署了测试 ...

  5. 本地计算机上的mysql服务启动停止工作,本地服务开启MySQL57提示本地计算机上MySQL服务启动后停止。。。。...

    1.首先以管理员身份启动cmd,要不然服务禁止访问. 2.然后在cmd中输入 mysqld --remove mysql或者mysqld --remove mysql57来移除服务. 3.然后进入My ...

  6. Pro Android学习笔记(七七):服务(2):Local Service

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ Local Service的目的是更容易实 ...

  7. 如何给Android应用创建本地服务

    Android系统给应用提供了两种类型的服务:启动型本地服务和绑定型本地服务,这两种服务的详细信息请参考"Android Service开发指南" Android Service开 ...

  8. Windows 服务介绍(本地系统、网络服务、本地服务以及相关的power shell命令

    一.Windows服务概述 服务与进程 Windows服务是指系统自动完成的,不需要和用户交互的过程,可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何 ...

  9. 本地计算机t3服务服务启动后停止,T3服务启动后又停止了

    右击"我的电脑"-"管理"-"服务"-找到"用友通"服务,选"启动"的时候仍然提示错误:" ...

最新文章

  1. 云服务和独立服务器 我们应该怎么选?
  2. 条件、循环、函数定义、字符串操作练习
  3. ORA-00904 的解决
  4. 设计模式之策略模式(strategy)--游戏角色使用武器
  5. tfds.load()和tf.data.Dataset的简介
  6. Solr7.3 Cloud On HDFS搭建
  7. 在Linux环境下mysql的root密码忘记解决方法 1.首先确认服务器出于安全的状态,也就是没有人能够任意地连接MySQL数据库。 2.修改MySQL的登录设置: # vi /etc/my.c
  8. 黄聪:PHP去掉转义后字符串中的反斜杠\函数stripslashes
  9. Ogre 1.7 SDKTRAY 初探
  10. 2019118_四个化学数据分析(2)
  11. cv2中函数名的规则
  12. 使用 PDO 方式将 Session 保存到 MySQL 数据中
  13. 小程序发布文章-微信小程序视频教程28
  14. host smbus controller not enabled解决方法(主机smbus控制器未启用)
  15. 如何提高阅读源代码能力
  16. 【猿说VUE】初试模板语法,开启VUE编码之旅
  17. python 词云 教程
  18. 多目标优化详解【转载】
  19. python循环输出1到10_用Python编写一个程序,使用for循环输出0~10之间的整数
  20. 关于DBA或SA这个职业的讨论

热门文章

  1. java开发中推荐的防御sql注入方法_Java防止SQL注入
  2. LPC2141芯片解密 芯片解密干什么
  3. 在Vue中使用svg格式字体图标
  4. 《Java语言程序设计与数据结构》编程练习答案(第三章)(三)
  5. [FROM LUOGU]排兵布阵
  6. 一招解决windows电脑禁用笔记本自带键盘问题
  7. 基于ATMEGA16单片机,MQ-3酒精传感器,LCD1602液晶显示的酒精浓度检测阈值报警仪
  8. Linux Command—— Wildcard
  9. 教你根据情况快速导入单号查询快递单号物流
  10. 【Python 数据科学】分组group by基础