Android服务Service

来源:互联网 发布:心神战斗机 知乎 编辑:程序博客网 时间:2024/04/29 07:21

1)概述

在Android中服务是一个很重要的组件,它是四大组件之一,它适合长时间运行在后台而不需要与用户打交道,虽然说它运行在后台,但是服务并不会自动去创建一个新的线程,而是默认运行在主线程中,所以在服务中执行耗时操作,也需要开一个新线程,否则出现ANR异常。即使是另外一个应用被切换到前台,服务依然能够运行在后台,此外,一个组件能够与服务绑定进行IPC通信。比如,处理网络操作,播放音乐,I/O操作或者与内容提供者交互,都可以工作在后台。

需要注意的是服务并不会运行在独立进程中,而是依赖于创建服务所在进程,所以当应用所在进程被杀掉时,所有依赖于该进程的服务也会被杀掉 。

2)服务形式

一个服务通常有两种形式:全局式(Started)的和绑定式(Bound)的。

  • Started服务

当用户调用startService()方法即可以启动一个Started式的服务。一旦启动,这个服务就立马运行在后台,即使activity销毁,它依然存在。这种服务通常只做单向操作,也就是不返回一个结果给调用者。要想停止服务,除非在调用者里面调用stopService或者在服务里面调用stopSelf 才能停止服务。这种服务,它可以通过网络下载或者上传文件,播放音乐,监听电话状态等操作。

  • Bound服务

当用户调用bindService() 这种服务可以开启。一个Bound式服务
可以供用户与服务进行交互。通过这,可以向服务发送请求,获取请求结果,可以进程通信(IPC)。这种服务只能与组件绑定之后才能运行,并且只能绑定一次,调用unbindService()可以与服务解绑。

3)生命周期

这里写图片描述

相信这张图已经可以很清晰的描述服务的生命周期。简单说下,左边的是started式的服务,右边的是bound式服务。started式服务通过startService()启动,其中onCreate()只会调用一次,而onStartCommand()每次调用startService()都会调用一次。Bound式服务也只会调用onCreate()一次,而onBind()会被调用多次

4) Started Service

通常,这种服务根据继承的类有两种创建方式

  • 继承Service

直接看下官方案例:

public class HelloService extends Service {  private Looper mServiceLooper;  private ServiceHandler mServiceHandler;  // Handler that receives messages from the thread  private final class ServiceHandler extends Handler {      public ServiceHandler(Looper looper) {          super(looper);      }      @Override      public void handleMessage(Message msg) {          // Normally we would do some work here, like download a file.          // For our sample, we just sleep for 5 seconds.          try {              Thread.sleep(5000);          } catch (InterruptedException e) {              // Restore interrupt status.              Thread.currentThread().interrupt();          }          // Stop the service using the startId, so that we don't stop          // the service in the middle of handling another job          stopSelf(msg.arg1);      }  }  @Override  public void onCreate() {    // Start up the thread running the service.  Note that we create a    // separate thread because the service normally runs in the process's    // main thread, which we don't want to block.  We also make it    // background priority so CPU-intensive work will not disrupt our UI.    HandlerThread thread = new HandlerThread("ServiceStartArguments",            Process.THREAD_PRIORITY_BACKGROUND);    thread.start();    // Get the HandlerThread's Looper and use it for our Handler    mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);  }  @Override  public int onStartCommand(Intent intent, int flags, int startId) {      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();      // For each start request, send a message to start a job and deliver the      // start ID so we know which request we're stopping when we finish the job      Message msg = mServiceHandler.obtainMessage();      msg.arg1 = startId;      mServiceHandler.sendMessage(msg);      // If we get killed, after returning from here, restart      return START_STICKY;  }  @Override  public IBinder onBind(Intent intent) {      // We don't provide binding, so return null      return null;  }  @Override  public void onDestroy() {    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();  }}

这里注意下onStartCommand的返回值

在Android开发中,调用Context的startService方法启动Service的生命周期时,如果Android面临内存匮乏,可能会销毁掉你当前运行的Service,然后待内存充足的时候可以重新创建Service,Service被Android系统强制销毁并再次重建的行为依赖于Service中onStartCommand方法的返回值。而Service onStartCommand方法中,常用的返回值主要有:START_NOT_STICKY、START_STICKY和START_REDELIVER_INTENT三种,这三个值都是Service中的静态常量。来看下它们的具体含义:

START_NOT_STICKY:如果返回START_NOT_STICKY,表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,当然如果在其被杀掉之后一段时间又调用了startService,那么该Service又将被实例化。那什么情境下返回该值比较恰当呢?如果我们某个Service执行的工作被中断几次无关紧要或者对Android内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受,那么我们便可将 onStartCommand的返回值设置为START_NOT_STICKY。举个例子,某个Service需要定时从服务器获取最新数据:通过一个定时器每隔指定的N分钟让定时器启动Service去获取服务端的最新数据。当执行到Service的onStartCommand时,在该方法内再规划一个N分钟后的定时器用于再次启动该Service并开辟一个新的线程去执行网络操作。假设Service在从服务器获取最新数据的过程中被Android系统强制杀掉,Service不会再重新创建,这也没关系,因为再过N分钟定时器就会再次启动该Service并重新获取数据。

START_STICKY:如果返回START_STICKY,表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,但是onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一个用来播放背景音乐功能的Service就适合返回该值。

START_REDELIVER_INTENT:如果返回START_REDELIVER_INTENT,表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。只要返回START_REDELIVER_INTENT,那么onStartCommand重的intent一定不是null。如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。

  • 继承IntentService

再来看下官方案例

public class HelloIntentService extends IntentService {  /**   * A constructor is required, and must call the super IntentService(String)   * constructor with a name for the worker thread.   */  public HelloIntentService() {      super("HelloIntentService");  }  /**   * The IntentService calls this method from the default worker thread with   * the intent that started the service. When this method returns, IntentService   * stops the service, as appropriate.   */  @Override  protected void onHandleIntent(Intent intent) {      // Normally we would do some work here, like download a file.      // For our sample, we just sleep for 5 seconds.      try {          Thread.sleep(5000);      } catch (InterruptedException e) {          // Restore interrupt status.          Thread.currentThread().interrupt();      }  }}

我们可以看到要实现的代码比Service还是很少的,其特点如下:

(1)会创建独立的worker线程来处理所有的Intent请求;

(2)会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;

(3)所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;

5)前台服务

通常如果我们想让一个服务不容易被杀死,我们可以通过通知让其工作在前台

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_ID, notification);

如果我们想让其不工作在前台,可以通过调用stopForeground()。

6)最后

其实还有Bound式服务没有介绍,其里面知识点还是有点多的,具体下次再总结。本次总结基本到此为止,参考的是android官方文档服务那一章。

0 0
原创粉丝点击