Service之常用方法

(一)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)

时间: 2024-11-03 01:40:25

Service之常用方法的相关文章

Java修炼之道--并发编程

原作地址:https://github.com/frank-lam/2019_campus_apply 前言 在本文将总结多线程并发编程中的常见面试题,主要核心线程生命周期.线程通信.并发包部分.主要分成 "并发编程" 和 "面试指南" 两 部分,在面试指南中将讨论并发相关面经. 参考资料: <Java并发编程实战> 第一部分:并发编程 1. 线程状态转换 新建(New) 创建后尚未启动. 可运行(Runnable) 可能正在运行,也可能正在等待 CPU

Android 启动Service服务和发送Broadcast广播的常用方法

一.先说Service服务. 1.利用setAction()方法来指定启动的Service服务 1 Intent intent = new Intent(); 2 intent.setAction("ServiceAction"); 3 startService(intent); 2.使用Intent的构造函数类添加Activity内容 1 Intent intent = new Intent("ServiceAction"); 2 startService(int

Android 综合揭秘 —— 全面剖释 Service 服务

引言 Service 服务是 Android 系统最常用的四大部件之一,Android 支持 Service 服务的原因主要目的有两个,一是简化后台任务的实现,二是实现在同一台设备当中跨进程的远程信息通信. Service 服务主要分为 Local Service 本地服务与 Remote Service 远程服务两种,本地服务只支持同一进程内的应用程序进行访问,远程服务可通过AIDL(Android Interface Definition Language)技术支持跨进程访问.服务可以通过C

Android笔记二十七.Service组件入门(一).什么是Service?

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 一.Service 1.Service简介 Service为Android四大组件之一,Service与Activity组件相似,都代表可执行的程序且有自己的生命周期,唯一的区别是Activity组件提供界面方便人机交互而Service只在后台运行且没有交互界面.Service是android 系统中的一种组件,它们都是从Context派生出来的,但是它不能自己运行,只能在后台运行,并且可以和其

Android开发6:Service的使用(简单音乐播放器的实现)

前言 啦啦啦~各位好久不见啦~博主最近比较忙,而且最近一次实验也是刚刚结束~ 好了不废话了,直接进入我们这次的内容~ 在这篇博文里我们将学习Service(服务)的相关知识,学会使用 Service 进行后台工作, 学会使用 Service 与 Activity 进行通信,并在此知识基础上学会使用 MediaPlayer和简单的多线程编程.使用 Handle 更新 UI,并设计成功一个简单的音乐播放器. 是不是很高大上呢~一起来学习~ 基础知识 Service作为Android四大组件之一,在每

spring aop + xmemcached 配置service层缓存策略

Memcached 作用与使用 基本介绍 1,对于缓存的存取方式,简言之,就是以键值对的形式将数据保存在内存中.在日常业务中涉及的操作无非就是增删改查.加入缓存机制后,查询的时候,对数据进行缓存,增删改的时候,清除缓存即可.这其中对于缓存的闭合就非常重要,如果缓存没有及时得到更新,那用户就会获取到过期数据,就会产生问题. 2,对于单一业务的缓存管理(数据库中只操作单表),只需生成一个key,查询时,使用key,置入缓存:增删改时,使用key,清除缓存.将key与表绑定,操作相对简单. 3,但是在

Android blueZ HCI(一):hciconfig实现及常用方法

关键词:hciconfighcitool  hcidump作者:xubin341719(欢迎转载,请注明作者,请尊重版权,谢谢!)欢迎指正错误,共同学习.共同进步!! Android blueZ HCI(一):hciconfig实现及常用方法Android blueZ hci(二):hcitool hcidump常用方法 一.Hciconfig1.adb shell 下,hciconfig 执行文件的位/system/xbin/hciconfig 相应目录下Android.mk文件,生成hcic

使用mybatis完成通用dao和通用service

使用通用dao和通用service可以减少代码的开发.可以将常用的增删改查放到通用dao中.对不同的or框架,基本上都有自己的实现如SpringJPA的Repository就提供了常用的增删改查方法.而MyBatis借助代码生成工具也可以生成常用方法的映射 这里只针对Mybatis.如果使用代码生成工具,会有一个问题:每个Mapper里面都有一些方法(增晒改查).维护起来的话还好.只是在写service的时候会有一个问题.比如UserMapper里面有 insert(User user) , f

Android学习笔记二十三.Service组件入门(一).什么是Service?

什么是Service? 一.Service 1.Service简介 Service为Android四大组件之一,Service与Activity组件相似,都代表可执行的程序且有自己的生命周期,唯一的区别是Activity组件提供界面方便人机交互而Service只在后台运行且没有交互界面.需要注意的是,Service不是一个单独的进程或为了防止应用出现无反应错误单独的线程,它像其他应用对象一样运行在其托管进程的主线程中.当然,如果我们希望自己的Service能够在后台运行MP3或者网络下载,我们可