Services 综合介绍

Services 综合介绍

官方:http://developer.android.com/guide/components/services.html
简介:Service是android系统的组件之一,它可以用来长时间在后台执行任务,这里需要注意的是Service运行在主线程上,因此不适于用来执行耗时操作,如果存在耗时任务如网络连接、MP3播放,我们应该在Service中另起线程来执行这些任务。 这里需要区分的是长时间在后台运行和耗时这两个概念并不相同。 Service可以用来处理网络交互、音乐播放、文件操作、联系content provider等任务。 Service可以被其他组件启动并保持运行状态,即使用户可能已经切换到其他应用去操作。当然,Service也可以被绑定运行,这样它的生命周期就和绑定的客户端相关联,不仅同应用的组件可以与它绑定,其他应用也可以绑定(IPC InterProcess Communication远程通信)。
*Service的两种运行方式:*
独立运行:这种运行方式是指Service被其他组件(如Activity)通过调用startService方法启动。由这种方式启动的Service可以保持一直运行,而与启动它的组件的状态无关,即使启动它的组建已经被销毁。通常这种独立启动的Service只需要执行单一的任务,而不必与启动它的组件联系。比如,某个Service可能用来下载或者上传一个文件到网络,当这个任务完成以后,Service就应该销毁自己。(这里的特点就是保证了即使启动它的组件已经被销毁,也能保证任务得到完整执行)
绑定运行:Service除了独立启动去执行单一任务,它也可以被其他组件绑定,通过bindService方法启动运行。绑定启动的Service和绑定它的客户端将拥有一个接口,实现发起请求、获得结果等任务,Service不仅可以在某一应用内被绑定,它还能实现跨进程绑定,即其他的应用也可以绑定到这个服务。以绑定启动方式运行的Service的生命周期将与和它绑定的组件生命周期相关联,即“非同年同月同日生,但一定同年同月同日同时死”。 一个Service允许同时被多个组件绑定,当所有与它绑定的组件都被销毁时,这个Service也会被销毁。
这里需要注意的是,Service独立运行和绑定运行两种方式并不是互斥的,我们可以同时调用startService和bindService来使这个Service运行起来。
*创建一个Service:*
我们需要继承Service类或者继承它的一个子类来创建我们自己的类,以下几个方法我们可能需要去覆盖:
onStartCommand() 每次我们通过startService()去启动Service的时候,onStartCommand方法都会得到执行,即使之前已经调用过startService方法。如果这个onStartCommand得到过执行,那么这个Service就是以独立运行方式运行的,因此我们应该在它的任务完成时通过stopSelf()方法或者stopService方法去关闭这个Service,总之,系统是不会去关闭它的(除非内存吃紧)。 当然,如果我们不会通过startService来启动这个Service,我们也就可以不重写这个方法。
onBind() 当我们通过bindService方法来绑定到Service的时候,这个方法会得以执行,并返回一个IBinder对象,这个IBinder对象就是客户端与Service的桥梁,可以通过我们在里面定义的接口,实现我们的交互。 当然,如果我们不会绑定到这个Service,我们也可以不重写此方法,默认它会返回NULL。 这里需要注意的是,onBind方法并不是每次绑定都会运行,它只会在第一次绑定的时候才会执行。
onCreate() 当Service还没运行时,我们调用startService或者bindService启动Service,都会首先调用onCreate。但是如果Service已经在运行了,之后调用startService和bindService都不会再执行onCreate。
onDestory() 当Service将要被销毁时调用这个方法,在这个方法里,我们需要去释放Service所占用的各种资源,如线程、注册的监听等。

一般情况下,Service不会被系统杀死,但是当系统内存极度紧张时,Service将可能被杀死,在此之后,当系统内存不再那么紧张时,系统可能会重启这个Service(具体重启时机和onStartService返回的值有关),因此我们需要注意系统重启Service的应当如何处理。
在AndroidManifest中声明Service:
和acitivty一样,每一个Service都需要在manifest中声明,声明格式如下:

<manifest ... >
  ...
  <application ... >
     <service android:enabled=["true" | "false"]
         android:exported=["true" | "false"]
         android:icon="drawable resource"
         android:isolatedProcess=["true" | "false"]
         android:label="string resource"
         android:name="string"
         android:permission="string"
         android:process="string"> 
<intent-filter android:icon="drawable resource"
               android:label="string resource"
               android:priority="integer" >
       </intent-filter>
<meta-data android:name="string"
           android:resource="resource specification"
           android:value="string" /> 
</service>

      ...
  </application>
</manifest>

可以看到,Service可以配置许多属性,但是只有android:name是必须的,他指定了Service的类名。
为了保证应用的安全性,你应该使用指定的Intent(类名限定)来启动或者绑定到Service,而不要去声明intent filters。如果允许一定的歧义性,让用户选择具体哪一个Service,也可以声明intent filters,但是你必须在intent中通过setPackage()方法来减少这个歧义。
另外,如果你不希望Service提供跨进程服务,可以设置android:exported为false。
创建一个独立方式运行的Service:
对于以独立方式运行的Service,如果我们需要在启动它的时候传入一些数据,可以在startService(Intent)中的intent对象中传入,它可以包含少量的数据或者一个可以获取到数据的链接。
通常情况下,我们可以通过继承以下两个类来创建一个以独立方式运行的Service。
直接继承Service类: Service类是一个抽象类。在继承这个类时,我们需要另外创建创建线程来处理我们的任务操作。
继承IntentService类: IntentService类是Service类的子类,它使用一个工作线程来统一处理所有的请求。当你不需要多个线程处理任务时,直接继承IntentService类是最好的选择。所有你需要做的就是重写onHandleIntent()方法,这个方法用来处理每个包含请求的Intent。
*下面具体介绍如何继承上面两个类:*
继承IntentService:
IntentService的特点重点内容
1、 创建一个工作线程,用于处理onStartCommand()传递的intents对象(onStartCommand多次被调用,就会传递多个过来);
2、 创建一个工作队列来将onStartCommand传递的intent按序组织起来,并依次提交给onHandleIntent,每次提交都是在上一个intent已经处理完成后;
3、 当将工作队列中所有intent处理完以后,它会自动关闭Service,而不用我们去关闭它;
4、 onBind()的默认实现会是返回null;
综合IntentService的所有特点,继承Service时,我们的唯一工作就是重写onHandleIntent()方法。

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 withthe 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) {
       }}}}}

继承Service:
当我们需要通过多线程来完成我们提交的任务时,我们可以通过继承Service类来实现,我们只需要在onStartCommand中每次调用都启动一个线程来处理任务就可以了。但是官方文档,仅仅提供了一个单线程处理任务的示例,它实现的功能和上面继承IntentService是一样的,但是这里需要更多的代码:

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'smain 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();
  }}

以独立方式运行的Service如何返回一个结果给客户端呢?官方文档介绍了一个方法,在startService(Intent)传递的Intent中加入一个PendingIntent对象,使Service在完成以后可以通过它发布一个广播来返回结果。
创建一个绑定方式运行的Service:
当你需要实现与Service实现交互,或者你希望把本应用的某些功能发布给其他应用时,你应该创建一个以绑定方式运行的Service。
创建绑定运行的Service,我们必须重写IBinder onBind (Intent intent)方法,返回的Ibinder对象定义了客户端与Service交互的接口。我们通过bindService()去绑定一个Service,通过unBindService解绑,我们不必去关心这个Service合适关闭,因为当所有与它绑定的客户端解绑以后,它会自动关闭。
我们有多种方式去实现Service与绑定它的客户端实现交互,实现绑定方式运行的Service比独立方式运行的Service要复杂许多,官方文档另外有一篇文章专门介绍,我们这里也将在后续文章中专门介绍。
发送通知给用户:
在Service执行的各个阶段,我们可以发送Toast通知或者状态栏通知,其中状态栏通知,我们可以引导用户执行一些操作,比如启动一个Activity。
运行前台Service:
一个前台Service表示这个Service执行的任务会受到用户的关注,因此它应该极不容易被系统杀死。一个前台Service必须提供一个状态栏通知,直到它退出前台或者已经关闭。
比如一个播放音乐的Service应该作为前台Service运行,因为用户需要意识到他自己的操作,看到当前播放的状态,或者依靠它启动一个Activity。
Service调用startForeground方法即可启动前台运行,这个方法有两个参数:1、一个integer类型的notification Id(非0)和一个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_ID, notification);

要移除Service的前台运行,调用stopForeground (boolean removeNotification)即可,当removeNotification为true时则移除通知,为false则保留通知,它只会停止前台运行,但并不会停止Service。 当Service停止时,它会自动移除通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值