Android学习--Service基础篇

   一、概述

    Service是一个应用程序组件,可在后台执行耗时的操作,没有用户界面。其他的应用程序组件可以启动一个service,即使用户跳转到其他的应用程序,这个service仍可在后台继续运行;另外,其他组件还可以绑定到一个service上,并跟这个service进行通信,甚至可以通过service进行线程间通信(IPC)。

    Service有两种存在形式:
    Started:
    这种service是由其他组件(如activity)通过调用startService()启动的。这种service一旦被启动,就会无限期的运行下去,即使启动它的组件已经被销毁。通常这种service用来执行单一操作,并且不向调用者返回任何结果(例如,通过网络下载或上传文件)。当操作完成后,要记得结束该service。
    Bound:
    一个应用程序组件可通过调用bindService()绑定到一个service上。Bound service允许其他组件向该service发送请求、从该service获得返回结果,甚至通过该service进行IPC。一个Bound Service只有被其他组件绑定时才能运行。多个组件可以同时绑定到一个service上,当所有绑定组件都解除绑定时,这个service自动被系统销毁。
    (一个service可以同时以这两种方式存在,可以被启动而无限期的运行、同时接受绑定。)

    注意:一个service运行在其宿主进程的主线程内,除非专门指定,一个service不会创建它自己的线程、也不会运行在一个单独的进程内。这意味着,如果一个service要执行一些阻塞操作或占用大量CPU的工作时,应该在service中另建一个单独的线程,这样可以减少ANR。(如果要执行的工作运行在main thread之外,但此工作只有当用户与应用程序交互时才会执行,这时只需创建一个新线程就可以了,不需要创建service。例如,想在activity运行时播放音乐,应该在activity的onCreate()方法里创建一个新线程,在onStart()里运行它,在onStop()结束它就行了。)

   二、基础

    要创建一个service,必须继承Service类,并在其中复写必要的Service的生命周期方法,一些重要的方法如下:
    onStartCommand()
    当其他应用程序组件通过调用startService()启动一个service时,系统会调用这个函数。一旦这个函数开始执行,service就开始在后台无限期的执行;在service中的工作完成以后,程序员要通过调用stopSelf()或stopService()结束当前service。
    (如果一个service只是用来被绑定的,没有必要实现这个方法。)
    onBind()
    当其他组件通过调用bindService()绑定到一个service上时,系统会调用这个函数。这个函数返回一个IBinder,是客户端与service通信的接口。在Service子类中必须实现这个函数,如果不打算让这个service被绑定,可以让它返回null。
    onCreate()
    当service第一次被创建时调用此方法(在onStartCommand()或onBind()之前被调用)。
    onDestroy()
    service被销毁时调用。可以在其中执行清理操作,如清理线程、注册的监听器、接收器等。

    如果一个service是由startService()启动,这个service会一直运行,直到service通过stopSelf()自行终止、或其他组件调用stopService()将其终止,之后被系统销毁。
    如果一个service是由bindService()创建,一旦所有的clients对其解除绑定,这个service就会被系统销毁。

    只有当内存极少、必须要回收系统资源以保证当前拥有用户焦点的activity运行时,service才会被系统强行销毁。如果一个service绑定在当前拥有用户焦点的activity上,它是不太可能被杀死的;如果一个service被声明在前台运行(后面有对此详细介绍),也是不太可能被杀死的。然而,如果一个service在后台长时间运行,随着时间推移,系统会逐渐降低它在后台任务中的重要性,使其被杀死的可能性增大;但一旦可以获得系统资源,被系统杀死的service会重新启动(当然还跟onStartCommand()的返回值情况有关)。

    像其他组件一样,service必须在manifest文件中声明,格式如下:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

    /*关于<service>元素可包含的更多属性,参看官方文档中的"Android Manifest"部分*/
    像activity一样,service也可定义自己的intent filters。一旦service声明了自己的intent filters,用户设备上的任何应用的组件都有可能启动该service,只要该组件传入到startService()中的intent与service的intent filters匹配。如果一个service只是被所在程序使用,就不应该提供intent filters,只需用intent显示的启动它就行了;或者,为了确保service的私有性,可在manifest中设置android:exported="false" (即使service提供了intent filters,这条属性也会将其屏蔽)。

   三、创建Started Service

    一个应用程序组件通过向startService()中传递一个Intent来启动Started Service,service中的onStartCommand()方法对该Intent进行接收。
  
    要创建一个Started Service,可以继承以下两个类中的其中一个:
    Service:如果继承这个类,要是service中存在可能拖慢activity响应的操作时,需要程序员在该service中创建新线程。
    IntentService:这是Service的子类,它会创建一个工作线程处理所有启动请求(一次处理一个请求)。如果一个service不需要同时处理多个请求,就应该继承这个类。继承这个类时,只需实现它的onHandleIntent()方法,这个方法负责接收每个启动请求的intent。


    下面介绍如何通过继承这两个类创建自己的Started Service:

    继承IntentService类

    IntentService做了如下的事情:
    *创建一个默认的工作线程,用于处理所有传进来的intents;
    *创建一个工作队列,用于存放所有intents,并一次向onHandleIntent()传递一个intent,所以避免了多线程的问题;
    *提供了一个onStartCommand()的默认实现,用于把传入的intent放入工作队列,然后传递到onHandleIntent()中;
    *处理完所有请求后会自动结束该service,因此不需要程序员再调用stopSelf();
    *提供了一个onBind()的默认实现,并返回null。


    所以,继承IntentService时,只需实现onHandleIntent()方法并提供一个简单的构造函数即可,如下:

public class HelloIntentService extends IntentService {
  /**必须的构造函数,必须在其中调用父类的IntentService(String)构造函数,并向其中传入工作线程的名字。 */
  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.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}

    如果需要复写其他的回调方法,一定要记得调用父类IntentService对该方法的实现,以确保IntentService能够正确处理工作线程的声明周期;例如,复写onStartCommand()时,必须返回父类默认的实现,如下:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}

除了onBind()(当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.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // 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()函数必须返回一个整数;当service被系统杀死,该整数指示系统接下来如何处理该service。返回的值必须是下列常量之一:
START_NOT_STICKY
    如果在onStartCommand()返回后service被系统杀死,不会重建该service。
START_STICKY
    如果在onStartCommand()返回后service被系统杀死,系统会重建service并且调用onStartCommand(),但系统调用onStartCommand()用的是一个空的intent,而不是service被杀死前传入的最后一个intent。
START_REDELIVER_INTENT
    如果在onStartCommand()返回后service被系统杀死,系统会重建service,并用service被杀死前传入的最后一个intent再次调用onStartCommand()。

   四、启动Started Service 

    可以在activity或其他组件中通过调用startService()启动service,系统会调用service的onStartCommand()方法(如果该service尚未运行,会先调用其onCreate()方法),例如:

Intent intent = new Intent(this, HelloService.class);
startService(intent);

    如果一个Started Service没有提供绑定功能,那么startService()就是应用组件与service之间通信的唯一函数。如果此时想让service向其他组件返回信息,可以在调用者组件中调用PendingIntent.getBroadcast()创建一个PendingIntent,并把它放在启动service的intent中传递给service;service就可以用这个broadcast向外返回结果。

    通过startService()多次启动service,onStartCommand()会被相应的调用多次;但是,要结束此service,只需调用一次stopSelf()或stopService()就行了。

  五、 结束Started Service

    一个Started Service必须管理自己的生命周期(系统只有在必须回收内存、并且在onStartCommand()返回后service仍在运行时才强行将其销毁),因此必须通过调用stopSelf()或其他组件调用stopService()结束服务。
    然而,如果同时有多个启动请求发送到onStartCommand(),不应该在处理完一个请求后调用stopSelf();因为在调用此函数销毁service之前,可能service又接收到新的启动请求,如果此时service被销毁,新的请求将得不到处理。为了避免这个问题,应该用stopSelf(int startId)代替stopSelf(),因为前者只有在其参数startId跟最后启动该service时生成的ID相等时才会执行;因此,当试图执行stopSelf(int startId)时,如果service已经收到了新的启动请求,startId和新生成的启动ID将不匹配,函数不会执行,service不会被销毁,就可保证新的请求能够得到处理。
    注意:为了避免系统资源浪费和消耗电量,当service完成它的工作后,一定要记得将其停止;对于被绑定的service,如果其onStartCommand()曾被调用过,也要记得将其停止。

   六、创建Bound Service          

    详见Android学习--Service之Bound Service

阅读(4) 评论(0) 编辑 删除
  七、 向用户发送Notifications    

    详见关于"Toast Notifications"和"Status Bar Notifications"的文档。

  八、 在前台运行Service

    Foreground Service不会再内存不足时被系统杀死。一个foreground service必须向status bar发送一个通知;例如,一个播放音乐的service应该被放在前台运行,在status bar里的通知指示正在播放的歌曲、并可以通过它启动一个activity使用户能跟音乐播放器交互。
    让service在前台运行,需调用startForeground()函数,它有两个参数:一个用于标示notification的整数、和一个Notification。例如:

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);

    要将service从前台移除,需调用stopForeground(boolean removeNotification)方法(参数指示是否将notification从status bar中移除);但是这个方法并没有结束此service。
    如果当service在前台运行时将其结束,notification也会同时被移除。

九、管理Service的生命周期

    Service的生命周期有两种情况:
    *Started Service:当其他组件调用startService()时被创建,当调用其stopSelf()或其他组件调用stopService()时停止,之后被系统销毁。
    *Bound Service:  当其他组件调用bindService()时被创建,当所有绑定的组件都解除绑定时,系统将其销毁。
    这两种情况并不是截然分开的,一个通过startService()启动的service也可以接受绑定;在这种情况下,单有stopService()或stopSelf()并不能终止该service,而是得等到所有组件都解除对该service的绑定。 

    实现生命周期方法
    Service生命周期方法的简单实现如下:

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used
    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

    注意:和Activity的生命周期方法不一样的是,在实现Service的生命周期方法时不需要调用父类对这些方法的实现。
 

    通过实现这些生命周期方法,可以对service的以下两种寿命进行监管:
    *entire lifetime:即从onCreate()被调用、到onDestroy()返回之间的时间。
    *active lifetime:从onStartCommand()或onBind()开始。如果service是被启动的,就随entire lifetime一起结束;如果service是被绑定的,当onUnbind()返回时结束。

 


                                Figure 2. The service lifecycle.   

   
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值