Android之Service

515 查看


startService(): run indefinetly, 需要在适当时候stopSelf()
onBind(): 提供一个CS模式的服务,runs as long as the component bound to it. 当所有组件都unBind() 时,Service也就destroy了。

Service运行于主进程,因此勿做耗时工作,如果需要,可以开启新的线程来做耗时的work,以避免ANR。
声明周期lifecycle
onStartCommand()
当用户调用startService()请求启动服务,系统就会调用该方法。当该方法运行后,service就会在后台runs indefinitely.如果自己实现该方法,则需要自己调用stopSelf()或者stopService()来关闭服务。
onBind()
其他component调用bindService(),系统就会调用该方法。如不想被bind,则返回null,否则返回一个IBinder()。
onCreate()
当Service第一次被调用时,系统将调用该方法进行初始化,然后才会调用onStartCommand或者onBind. 如果该Service已经启动则跳过该步骤。
onDestory()
清理资源,诸如线程,注册的listeners,receivers等。

Caution: always use an explicit intent when starting or binding your Service

可将android:exported属性设为false, 这样就可以组织其他应用,即使是使用explicit intent也无法启动Service。

Traditionally,有两种Create Service的方式:
Service, 如有Intensive work,需要手动创建线程进行处理,否则容易造成ANR, as it is hosted in main thread
IntentService, Service的子类,使用worker thread 依次处理所有请求,需要继承onHandleIntent(),然后处理从onStartCommand收到的intent。

Extending the IntentService class

优点:

  • create 默认的worker thread, 处理所有来自onStartCommand收到的Intent请求。
  • Create a work queue, 依次传递收到intent到onHandleIntent(),依次处理。
  • 在处理完所有请求后,就会自动销毁Service,无需自己调用stopSelf()
  • 提供默认的onBind()实现,返回null。
  • 提供一个默认的onStartCommand实现,它会将intent发送到work queue,并依次传递到onHandleIntent()

实例:
All you need: a constructor and an implementation of 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.
   * 一个构造函数,且需调用super,并传入该service名
   */
  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) {
              }
          }
      }
  }
}

注意,如果override 其他的方法,则必须调用super,让其帮助管理声明周期。比如对于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);
}

Extending the Service class

如果需要同步处理多个请求,而不是按照队列依次处理,则继承Service,然后再对每个请求启动一个thread进行处理。
实例代码(有许多需要说明)

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

第一点,新建HandleThread,与普通Thread无异,但是多了一个Looper成员。
第二点,正常来讲,Service是可以使用handler的,因为它是主线程,默认已经具有了Looper。但是官方demo还是根据新建的HandlerThread的成员looper,传入了Handler。
第三点,对于onStartCommand返回值START_STICKY,总共可以返回三个值,定义了在系统杀掉服务后系统如何继续服务。

  1. START_NOT_STICKY
    当系统杀掉Service后,不进行recreate.

  2. START_STICKY
    当系统杀掉Service后,recreate and call onStartCommand(), 不传递last intent.适合于media play, 不执行commands,只是runs indefinitely.

  3. START_REDELIVER_INTENT
    当系统杀掉Service后,recreate and call onStartCommand(), 携带last intent. 适合于正在执行任务的需要被立刻被恢复的,如下载。

Application Component 与Service交互,除了startService,无其他活动。若想得到Service的返回结果,可用PendingIntent 携带一个getBroadcast()对象,这样在Service使用完成后,即可发送发送广播。

Creating a Bound Service

可通过调用bindService()
To Create a Bound Service,必须实现onBind()返回一个IBinder对象,该对象定义了与Service通信的接口。
当Client不再需要Service,需要调用unbindService() 进行解绑,使得服务科正常关闭。

Sending Notifications to the User

使用Toast 或者状态栏notification。
状态栏Notification时best practise, 可在service结束后,发送通知,然后用户可点击通知栏的通知,然后启动一个Activity。

Running a Service in the Foreground

A foreground service必须提供一个notification for the status bar, 将被放置在正在运行的条目下,不可移除,除非Service运行完毕或者被移除前台。
实例:

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

现在Notification已经使用Notification.Build进行建立了。
可使用stopForeground(),但不会停止Service。但是stopService()可以移除notification。

Managing the Lifecycle of a Service