【android官方文档节选】Service

如转载请注明出处http://blog.csdn.net/ethanchiu/article/details/19413151

Services

一个服务有两种形式

1.Started

     一旦一个组件调用了startService(),service就是“started”状态,一旦开始,即使启动它的组件销毁了,一个service可以无限期的运行在后台。通常一个启动的service执行一个操作,并且不会返回结果给唤起者。比如,可以通过网络下载或者上传一个文件。当操作完成,服务应该停止自己。

2.Bound

     一旦一个组件调用了bindService(),service就是“bound”状态。一个bound service提供一个clientserver接口,这个接口允许组件和service交互,发送请求,获得结果,甚至进程间通信。一个bound service只有在和他绑定的时候才会运行。多个组件可以一次绑定到service,当所有的组件都解绑的时候,service就销毁了。

     虽然文档是分开讨论者两种service的,但是serivce可以同时工作在这两种方式上—即可以启动,又可以绑定。只要看是否实现了一对回调方法:onStartCommand()和onBind()。

警告:一个service运行在主线程中--service不会创建一个自己的线程,不会运行在另一个进程中(除非指定了)。这就意味了,如果service执行cpu敏感的工作或者阻塞操作(比如MP3播放或者网络工作),需要在service中创建一个线程去做那些工作。通过一个单独的线程,可以减少ANR的风险,主线程可以保持只做用户交互先关的任务。

基础

    onStartCommand()
     当其他组件比如activity通过调用startService()请求的service开始了,这个方法将会被调用。一旦这个方法执行,service就会启动,并且一直运行在后台。如果你实现这个方法,service的任务完成的时候,就有责任停止这个服务,通过调用stopSelf()或者stopService()。(如果只是想提供绑定,就没必要实现这个方法)。
      onBind()
     当另一个组件通过调用bindService()想绑定这个service的时候,系统调用这个方法。在这个方法的实现中,通过返回一个IBinder提供一个用户和service交互的接口。如果不想绑定,返回null。
     onCreate()
     service第一次创建的时候,系统调用这个方法,来执行一次安装进程。如果service已经运行,这个方法不会调用。
     onDestroy()
     当service不再使用销毁的时候调用。service应该实现这个方法去清除一些资源比如线程,注册的监听器,接受器,等等。

     通过startService()启动一个组件(会调用onStartCommand),它会一直保持运行,直到自己调用stopSelf()或者另一个组件调用stopService()。
     通过调用bindService()创建一个service(onStartCommand不会回调),service只会在组件绑定的时候运行。如果所有组件都解绑了,这个service就会销毁。

     当内存低的时候,系统会强制停止一个服务,来恢复系统资源给有用户焦点的Activity。如果一个Activity获得焦点并且绑定了service,那么这个service被杀死的可能性比较小,并且如果service被声明前端运行,那么这个service几乎不会被杀死。除此之外,如果service开始了并且长时间运行,那么系统会降低它的后台任务优先级并且很可能被系统杀死--如果service启动了,你就该设计一个让系统重新启动它的机制。如果系统杀死了service,一旦资源可用之后应该重启它。更多关于系统杀死service,看Processes and Threading部分。
     

在manifest声明

     如果要想只在自己的app中使用这个service,需要加入android:exported属性,并且设置为false。这个可以有效停止其他app启动这个service,甚至使用了隐式intent也不可以。

创建一个started service     

 有两种类可以实现一个started service

Service
     是所有services的基类。当集成这个类,最重要的是创建一个新的线程去做所有的service任务,因为这个service使用了程序的主线程。

IntentService
     这是Service的子类,它使用一个工作线程处理所有的开始请求任务。如果不需要service同时处理多个请求,这是最好的选择。实现 onHandleIntent(),可以接收到每个请求返回的intent。

继承自IntentService类

     因为大多数started services 不需要同是处理多个请求(就是危险的多线程方案),IntentService是最好的选择。
     IntentService的功能:
创建一个默认的线程去处理所有通过onStartCommand()传过来的intents。
创建一个工作队列,在 onHandleIntent()中一时间传过来一个,队列处理,所以不要担心多线程。
所有请求处理完成之后,自己停止服务,不需要调用stopSelf()。
提供返回null的onBind() 的默认实现
提供onStartCommand()的默认实现,它发送intent到工作队列。

     所以只要实现了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 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) {
              }
          }
      }
  }
}

     如果想重写onCreate(),onStartCommand(),或者onDestroy(),确保调用了父类实现,这样IntentService可以在工作线程中处理任务。
     比如,onStartCommand()必须返回默认实现(这就是intent传递到onHandleIntent()的原因):
@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);
}
     除了onHandleIntent()不需要调用父类的实现,还有的就是onBind()。

继承自Service类

     如果要处理多线程的任务,需要使用Service类去处理每一个intent。
     
     以下是实现和IntentService一样功能,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     
     如果想service发送一个结果返回,启动service的组件可以为broadcast(使用getBroadcast())创建一个PedingIntent并且在Intent中将它传递给service。service可以使用broadcast传递一个结果。

停止一个Service
     在service中停止服务使用stopSelf(),在其他组件中停止服务调用stopService()。
     如果service在onstartcommand处理多个请求,不应该停止服务当处理完一个请求的时候,因为你可能接收到了一个新的请求(在一个请求完毕之后,停止操作可能终止了第二个请求)。为了避免这种情况,可以使用stopSelf(int)来确保停止的请求是之前发起的对应请求。

创建一个Bound Service     

一个bound Service是允许组件为了创建一个长时间连接的的服务。

     
     这个service的存在是服务于和它绑定的组件的,所以当没有组件绑定它的时候,这个service就是销毁。
     
     创建一个bound service,第一件事就是定义一个接口,这个接口决定组件如何和service交互。而这个接口需要通过IBinder实现,并且这个接口通过onBind()返回。

     一次有多个组件绑定到service。组件和service交互完,需要调用unbindService()去解绑。

运行一个Service在前端进程

     一个前端进程的service是用户特别关心的,因此当内存低的时候,不希望系统杀死他。一个前端service必须提供一个通知给状态栏,在“前进栏”的下面,意味着,这个通知不能消失,直到service停止或者从前端状态移除。
     比如,一个音乐播放器,播放音乐的的服务应该在前端进程,因为使用者明确关心这个操作。通知栏可以显示当前的歌曲并且允许使用者登录到Activity操作播放器。

     运行在前端的操作是调用startForeground()。这个方法带有两个参数:一个是整型对应通知,还有一个是状态栏的通知。     比如:

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

警告:startForeground的整型ID必须为0

     调用stopForeground()将service从前端进程移除。有个boolean变量,指明这条状态是否也移除。这个方法不会停止service。然而,如果当在前端进程运行的时候停止了service,这个通知也会移除。

 管理Service的生命周期

     service的生命周期—从何时创建到何时销毁--遵循以下两种不同的途径。
     
started service
     调用startService()创建service。service一直 运行在后台,直到调用stopSelf()。或者其他组件调用stopService()。service停止了,系统会销毁他。
     
bound service
     调用bindService()创建service。所有绑定这个service的组件都解绑了,service就销毁。

     这两种方式不是完全独立的。可以通过startService启动。比如,一个后台音乐service通过startService启动。后来,用户想通过测试这个播放器或者获得歌的信息,一个Activity和这个service通过bindService()绑定。这种情况下,stopService()后者stopSelf()不会停止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
    }
}


通过实现这些方法,可以监测两个内部循环的service生命周期:

entire lifetime
     entire lifetime发生在onCreate()和onDestroy()之间。     
active lifetime
     active lifetime开始于onStartCommand()或者onBind()。每一个方法处理的Intent被传递到startService()或者bindService()。
     如果service开始了,active lifetime结局了,那么entire lifetime也会结束。(onStartCommand()返回后,service任然活动的)。如果service解绑了,当onUnbind()返回,active lifetime就结束了。

图上阐明了service的回调的特征。虽然图将startService和bindService区别开来,记住,任何service它是怎么启动的,组件都可以绑定它。所以一个service以onStartCommand启动(通过调用startService())任然可以接受onBind()(当一个组件调用了bindService)。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章