(一)StartService

  运行Service的方法之一。任何继承于android.content.Context的Android组件(component)都可以使用一个Intent(android.content.Intent)来开启一个Service。Intent里面可以以类对象(Class<?>)或者action进行构造,使用action构造对于初学者来说不太直观,而且action构造的适用范围比类对象构造使用范围更广,所以action构造的方法将在本系列文章后面的内容进行介绍,现在就都使用类对象进行构造。

  如果Service并没有运行,则新建一个Service(这里涉及到Service指派Task的问题将在以后的内容中介绍),并运行之,相继Service的回调onCreate(),onStart()/onStartCommand()(onStartCommand()是在Android2.0及之后SDK加入推荐使用的,用来代替老版的onStart()方法)被调用;如果Service已经在运行了,则只有回调onStart()/onStartCommand()被调用。

  参看Android官方文档可以发现,onStart()和onStartCommand()均会传入一个Intent对象参数,这个Intent对象就是使用startService()时传入的同一个Intent对象(同一个表示使用“==”比较返回true)。因为Intent对象可以携带Extra数据,所以启用Service的组件可以随意的向Service传递Extra数据(使用putXXExtra()/putExtra()等方法,最常见的就是使用Java基本类型、String对象或者封装在Bundle对象中作为Extra数据,关于Extra数据的类型限制请自行参见文档)。当组件要传数据到Service时,只需要调用startService()并传入相应的携带了Extra数据的Intent对象,并在Service的onStart()/onStartCommand()中接收取出Extra数据,而不用理会Service是否已经运行,数据始终能够到达Service并进行处理。这样,就可以完成单向的IPC(进程间通信)功能(组件->Service)。

  当不想再让Service运行的时候,只需要(任一)组件调用stopService()并传入相应的Intent对象,如果Service正在运行,则会停止,如果Service没有运行,则系统会自动当什么事也没发生。如果Service自己不想再运行,可以在Service里使用stopSelf()自杀,可以看出,如果可以运行到自杀代码的话,那么Service肯定在运行。

  onStart()/onStartCommand()都会传入参数startId:int,用来标识Service的启用号。每次startService()系统会自动为开启的Service产生一个不同的startId,之前赋予它的startId(如果有)将会被覆盖,并且这个新产生的startId会成为这个Service的新的startId,无论Service是否正在运行。

  考虑如下情况,当多个组件启用了同一个Service,Service提供互斥的服务(使用synchronized关键字),且要保证在Service把所有工作完成之前不能自杀,这个时候,startId就相当有用了,在Service onStart()/onStartCommand()时把startId保存起来,因为互斥的使用服务,则Service是按顺序提供服务的,则Service自杀的时候只用检查当前Service的startId与保存的这个startId是否相同,不同则说明Service之后还有任务,不能自杀,相同则说明正在运行的任务已经是最后一个任务了,运行完后就可以自杀(使用stopSelf(int startId)方法)

启动服务:

Intent intent = new Intent(getApplicationContext(), MyService.class);

    startService(intent);

Service中代码:

    @Override

  public void onCreate() {

  }

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

  //接受传递过来的intent的数据等

  return START_STICKY;

  }

@Override

public void onDestroy() {

  }

(二)bindService

  绑定(bind)Service是开启Service的另一种方法,而且绑定Service几乎可以被认为是专门为IPC(进程间交互)准备的。绑定Serivce是通过Context.bindService()方法实现的,bindService和startService有一定的区别,首先就反应在生命周期上。bindService不会回调onStart()/onStartCommand()方法,而会回调onBind()方法;Service要停止绑定则需要要调用unbindService()方法,而不用stopService()或者stopSelf()方法。

  

/**
 * bindService
 *
 * @param service
 *            用显示的组件名(Class<?>方式)或者逻辑描述(action等)的Service的Intent
 * @param conn
 *            在Service开启或停止时接收信息的组件
 * @param flags
 *            绑定选项,可以是0,BIND_AUTO_CREATE,BIND_DEBUG_UNBIND,BIND_NOT_FOREGROUND,BIND_ABOVE_CLIENT,BIND_ALLOW_OOM_MANAGEMENT或者BIND_WAIVE_PRIORITY  
 * @return 绑定成功为true,否则为false
 */
    public abstract boolean bindService(Intent service, ServiceConnection conn,int flags);

  ServiceConnection可以监听服务的状态,在进行服务绑定的时,其标志位可以为以下几种(这里列出3种):

1).Context.BIND_AUTO_CREATE

  说明:表示收到绑定请求的时候,如果服务尚未创建,则即刻创建,在系统内存不足需要先摧毁优先级组件来释放内存,且只有驻留该服务的进程成为被摧毁对象时,服务才被摧毁

2).Context.BIND_DEBUG_UNBIND

  说明:通常用于调试场景中判断绑定的服务是否正确,但容易引起内存泄漏,因此非调试目的的时候不建议使用

3).Context.BIND_NOT_FOREGROUND

  说明:表示系统将阻止驻留该服务的进程具有前台优先级,仅在后台运行,该标志位位于Froyo中引入。

Service的绑定方法bindService()中除了用了Service的Intent外,还使用到了ServiceConnection对象,这个对象除了可以为Service绑定者(caller)回调方法,还是解绑定(unbind)时需要提供的参数。bindService()方法中最后一个参数flag则是表明绑定Service时的一些设置,一般情况下可以直接使用0,有关这个问题将在本系列文章以后的内容中介绍。  android.content.ServiceConnection是一个接口,实现(implementate)这个接口有2个方法需要重写(Override)。一个是当Service成功绑定后会被回调的onServiceConnected()方法,另一个是当Service解绑定或者Service被关闭时被回调的onServiceDisconnected()。前者(onServiceConnected()方法)会传入一个IBinder对象参数,这个IBinder对象就是在Service的生命周期回调方法的onBind()方法中的返回值,它对Service的绑定式IPC起到非常重要的作用。  

以下代码是bindService()及unbindService()的惯常用法,代码如下:

boolean mIsBound = false;

/** 绑定服务 */

public void doBindService() {

bindService(new Intent(MainActivity.this, LocalService.class), mConnection,Context.BIND_AUTO_CREATE);

mIsBound = true;

    }

/** 解除绑定服务 */

public void doUnbindService() {

  if (mIsBound) {

  // Detach our existing connection.

unbindService(mConnection);

mIsBound = false;

     }

    }

private ServiceConnection mConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

mBoundService = ((LocalService.LocalBinder) service).getService();

Toast.makeText(MainActivity.this, "服务连接", Toast.LENGTH_SHORT)

.show();

    }

@Override

public void onServiceDisconnected(ComponentName name) {

mBoundService = null;

Toast.makeText(MainActivity.this, "服务未连接", Toast.LENGTH_SHORT)

.show();

    }

  };

服务类中:

@Override

public void onCreate() {

  }

/** 绑定的IBinder */

private final IBinder mBinder = new LocalBinder();

 public class LocalBinder extends Binder {

  public LocalService getService() {

  return LocalService.this;

   }

  }

  @Override

 public IBinder onBind(Intent intent) {

  return mBinder;

  }

  @Override

 public boolean onUnbind(Intent intent) {

  // TODO Auto-generated method stub

  return super.onUnbind(intent);

  }

  需要注意的是,onServiceDisconnected()方法在Service被显示的(explicitly)unbind或者被停止时都会被回调。比如,当Android系统资源(主要是RAM)严重不足时,Service是很有可能被结束(kill)掉的,如果被kill掉,则onServiceDisconnected()方法会被回调,但这个时候Service是没有走完所有的生命周期的(比如不会回调onDestroy()方法)。当然,无论Service的开启是使用bind还是start,一旦当系统资源恢复之后,这些被kill掉的Service会以可能的最短的时间内被系统自动恢复(重新进行新的生命周期,从回调onCreate()方法开始)。

(三)ForegroundService

  Foreground Service(意译为前台服务)并不完全像其意译的意思那样是工作在前台的Service,因为Service实际上始终是工作在后台的。由于Service工作在后台的原因,使用者并不知道它在运行,有时候开发者需要使用者知道某个Service在运行时,就需要设计一种方案来解决这个问题,Foreground Service就出现了。Foreground Service说简单点其实就是在Service开启的时候使用通知(Notification),这也是Android官方推荐的方式,或者一些其它的方式(甚至可以是Activity,但使用Activity是基本没有必要的)来告知用户这个Service正在运行。

  在程序开启了Service,则使用一个“正在运行”的通知表明服务正在运行就可以了,也就是在Service的onCreate()回调或者onStart()/onStartCommand()回调中即可。虽然通知并不是一定需要的,或者说故意不提示用户有服务正在运行(稍稍流氓一点的程序就会这样),但是某些应用商场的应用审核就把通知提示做为了审核项目的。为了在Service周期(Life Cycle)结束的时候通知也能自动消失,所以需要在Service的onDestroy()回调里面写上取消通知的代码。以上就是配合通知自己实现的Foreground Service了。

  当然,除了自己处理通知的方法外,Google在Android 2.0(SDK level 5)以上的SDK提供了一个直接而简单的方法。直接使用Service.startForeground()和Service.stopForeground()进行处理(注意,这两个方法是Service类的)。下面看下Google提供的两个接口:

/**

 * 让service成为Foreground Service,并且产生一个“正在运行”

 * 的通知。默认情况下,service是后台的,这意味着service在系统

 * 回收内存(比如在浏览器里显示大图片)的时候可以被毫无顾忌的

 * kill掉。如果你比较在意这个service的挂掉,比如像后台音乐播放

 * 器这种突然挂了会影响用户的情况,就可以使用Foreground

 * Service来提示用户。

 *

 * 参数

 * id   The identifier for this notification as per

 * NotificationManager.notify(int, Notification).

 * notification The Notification to be displayed.

 */

publicfinalvoid startForeground (int id, Notification notification)

/**
 * 去掉service的foreground属性,允许在低内存时被kill掉
 *
 * Parameters
 * removeNotification  If true, the notification previously provided to startForeground(int,Notification)
 * will be removed. Otherwise it will remain until a later call removes
 * it (or the service is destroyed).
 */
publicfinalvoid stopForeground (boolean removeNotification)

  使用startForeground()之后,给出的Notification对象会发布,使用stopForeground()之后,通知会被撤销,当Service销毁(比如stopService()被调用)之后,通知也会被撤销。stopForeground()仅仅只是去掉service的foreground属性,并不会让service停止。

android官方描述如下:

Running a Service in the Foreground


A foreground service is a service that's considered to be something the
user is actively aware of and thus not a candidate for the system to
kill when low on memory. A foreground service must provide a
notification for the status bar, which is placed under
the "Ongoing" heading, which means that the notification cannot be
dismissed unless the service is either stopped or removed from the
foreground.

For example, a music player that plays music from a service should be
set to run in the foreground, because the user is explicitly aware of
its operation. The notification in the status bar might indicate the
current song and allow the user to launch an activity
to interact with the music player.

To request that your service run in the foreground, call startForeground().
This method takes two parameters: an integer that uniquely identifies the notification and the Notification for
the status bar. For example:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());

Intent notificationIntent = new Intent(this, ExampleActivity.class);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(this, getText(R.string.notification_title),getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION, notification);

To remove the service from the foreground, call stopForeground(). This method takes a boolean, indicating whether to remove the status bar notification as well. This method does not stop the service. However, if you stop the service while it's still running in the foreground, then the notification is also removed.

Note: The methods startForeground() and stopForeground() were introduced in Android 2.0 (API Level 5). In order to run your service in the foreground on older versions of the platform, you must use the previoussetForeground() method—see the startForeground() documentation for information about how to provide backward compatibility.

For more information about notifications, see Creating Status Bar Notifications.

  我们只需要在onStartCommand里面调用 startForeground方法让服务前台运行,然后再onDestroy里面调用stopForeground解除前台运行既可!

引申阅读:

1.检查Android后台服务是否正在运行?

通过系统级提供的方法ActivityManager.getRunningServices获取到当前正在运行的服务

private boolean isServiceRunning() {

ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);

  for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {

    if ("com.example.MyService".equals(service.service.getClassName())) {

        return true;

     }

    }

    return false;

  }

2.Service和Thread的区别?

  我们拿服务来进行一个后台长时间的动作,为了不阻塞线程,然而,Thread就可以达到这个效果,为什么我们不直接使用Thread去代替服务呢?

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
做不到的。

最后感谢分享,转载于:https://www.juwends.com/tech/android/android-service-1.html(1-3)

Service之三种服务方式的更多相关文章

  1. Service组件 总结 + 绑定理Service三种实现方式 Messager + Binder + AIDL

    在Android中进程按优先级可以分为五类,优先级从高到低排列: - 前台进程 该进程包含正在与用户进行交互的界面组件,比如一个Activity - 可视进程 该进程中的组件虽然没有和用户交互,但是仍 ...

  2. android service两种启动方式

    android service的启动方式有以下两种: 1.Context.startService()方式启动,生命周期如下所示,启动时,startService->onCreate()-> ...

  3. WCF - Hosting WCF Service 四种托管方式

    https://www.tutorialspoint.com/wcf/wcf_hosting_service.htm After creating a WCF service, the next st ...

  4. Android为TV端助力 Service 两种启动方式的区别

    服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务.这两个方法都 可以启动Service,但是它们的使用场合有所不同.使 ...

  5. 一起学Spring之三种注入方式及集合类型注入

    本文主要讲解Spring开发中三种不同的注入方式,以及集合数据类型的注入,仅供学习分享使用,如有不足之处,还请指正. 概述 Spring的注入方式一共有三种,如下所示: 通过set属性进行注入,即通过 ...

  6. 安卓服务(Service)的两种开启方式以及服务的生命周期

    安卓中服务的开启方式 一:採用start的方式开启服务 调用函数:startService(Intent)->onCreate()->onStart()/onStartCommand()- ...

  7. 浅淡Webservice、WSDL三种服务访问的方式(附案例)

    Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...

  8. Android(java)学习笔记227:服务(service)之服务的生命周期 与 两种启动服务的区别

    1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.Service 的两种启动方法和区别: (1)Servi ...

  9. 微软 WCF的几种寄宿方式,寄宿IIS、寄宿winform、寄宿控制台、寄宿Windows服务

    WCF寄宿方式是一种非常灵活的操作,可以在IIS服务.Windows服务.Winform程序.控制台程序中进行寄宿,从而实现WCF服务的运行,为调用者方便.高效提供服务调用.本文分别对这几种方式进行详 ...

随机推荐

  1. CSS高效开发实战:CSS 3、LESS、SASS、Bootstrap、Foundation --读书笔记(1)设定背景图

    技术的新发展,除计算机可以接入互联网之外,平板电脑.智能手机.智能电视等其他设备均可访问互联网.在多设备时代,构建多屏体验也不是听说的那么难. 但是这也增加了学习CSS的难度?不知道如何上手,只懂一点 ...

  2. APP注释代码

    <meta name="viewport" content="width=device-width,height=device-height,inital-scal ...

  3. window跳转页面

    1.直接的事件跳转 window.location.href="你所要跳转的页面"; 2.新窗口跳转 window.open('你所要跳转的页面'); 3.返回上一页 window ...

  4. js框架设计1.1命名空间笔记

    借到了司徒正美的写的js框架设计一书,司徒大神所著有些看不太懂,果然尚需循序渐进,稳扎js基础之中. 第一张开篇司徒阐述了种子模块的概念 种子模块亦为核心模块,框架最先执行模块,司徒见解应包含:对象扩 ...

  5. Linux pthread

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h& ...

  6. ASP.NET MVC随想录——漫谈OWIN

    什么是OWIN OWIN是Open Web Server Interface for .NET的首字母缩写,他的定义如下: OWIN在.NET Web Servers与Web Application之 ...

  7. js指定分隔符连接数组元素join()

    指定分隔符连接数组元素join() join()方法用于把数组中的所有元素放入一个字符串.元素是通过指定的分隔符进行分隔的. 语法: arrayObject.join(分隔符) 参数说明: 注意:返回 ...

  8. 开发ios的语言

    iOS发展这么多年了,很多第三方语言都向开发一种自己的iOS,于是多种跨平台诞生了! Object-c.swift: 当然是开发iOS的首先,毕竟是苹果自己的东西,也是最流行.最适合开发ios的,无论 ...

  9. 初识ZooKeeper

    最近在看Apache下的一个开源项目ZooKeeper(http://zookeeper.apache.org/doc/trunk/),用百度百科介绍的来说,Zookeeper是一个针对大型分布式系统 ...

  10. HDU 2045 不容易系列之(3)―― LELE的RPG难题(递推)

    题意:有排成一行的n个方格,用红(Red).粉(Pink).绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法. 题解:本来当n=1时, ...