官方开发文档翻译-Service

这篇博文是我对Google Android开发官方文档Service这一模块的翻译,第一次写翻译有不好的地方尽情谅解,我也是尽自己可能保留英文原版的意思,只作翻译不加自己看法。

服务(Service)是一个可以长期运行在后台但是没有用户界面的应用组件。其他应用组件可以启动服务(Service),用户切换到其他应用还是可以运行。此外,组件可以绑定服务(Service)并且相互影响,并且可以进程间通信(IPC)。例如,网络可以执行网络通信,播放音乐,文件I/O, 和内容提供者的通信,以及所有在后台的活动。

服务从本质上分为两种:
start启动:
应用组件(例如:Activity)通过调用startService()方法来启动服务,服务就处于启动状态。一旦service在后台被起动,无论启动它的组件是否被摧毁都能长期运行。通常一个被启动的service执行一些简单的操作,并且不给调用者返回结果。例如,这个可以通过网络下载或上传文件。当这些操作结束,service会自己停止。
Bound绑定:
当一个应用组件通过调用bindService()绑定一个服务,这个服务就处于bound状态。一个被绑定的service会提供一个客户端-服务端的接口,这个提供组件和serice之间通信,包括发送请求,收到结果,以及进程间的通信。一个被绑定的service存活时间和绑定它的组件一样长。多个组件可以同时绑定一个service,只有当所有组件都解除绑定时,这个service才会被销毁。

尽管这个文档是分开来讨论这两种service的,但是你的service可以同时直接启动和被绑定,这取决于你是否实现这两个方法:onStartCommand()运行组件启动service,和onBind() 方法组件绑定service。
不管service是被启动的,被绑定的还是两者皆有。任何应用组件可以使用它(甚至是另外的应用)。相同的,任何应用组件可以通过Intent启动service。然而你可以在manifest文件里定义一个私有的service, 来阻止其他应用的访问。这个在“在manifest文件中定义service”这一章节中会重点讨论。

注意

service是运行在主进程中的主线程,service不会创建自己的线程也不会运行在分进程中(除非自己指定)。这个意味着当你的service去做一些CPU消耗很大的工作时或者阻塞性的操作(后台MP3播放和网络通信),你需要在service中创建一个子线程来完成这些操作。通过使用子线程,你可以减少应用无响应(ANR)的问题,并且你的应用在Activities上专门用来用户交互。

基本的

创建一个service你必须创建一个类集成自Service类(或者Service类的子类)。在你的应用中,你需要重写一些关于Service生命周期和组件绑定service机制的关键方法,如果合适。需要重写的最重要的方法是:
onStartCommand()
当另外的组件例如Activities,调用startService()需要service启动,这个时候系统会调用onStartCommand()这个方法。一旦这个方法被启动,service就处于启动状态,并且会一直在后台运行。如果你实现了这个方法,在service工作完成后需要调用stopSelf()或者stopService(),来停止service(如果你只需要绑定这个service, 就不需要实现这个方法)
onBind()
另外的组件通过调用bindService()方法来绑定service(例如实现RPC),系统将会调用onBind()方法。在这个实现方法中,你要返回一个IBinder对象来做组件和service之间的通信。一般情况下都需要实现这个方法,如果你不需要绑定,在这个方法中你可以返回null。
onCreate()
服务在第一次被创建和执行一次性操作,系统将会调用onCreate()这个方法(在onStartCommand() 和onBind()两个方法之前调用),如果service, 已经在运行,这个方法就不会执行。
onDestroy()
当service不在被使用并且已经被销毁,系统将会调用onDestroy()这个方法。service需要实现这个方法来清理资源,例如子线程,注册的接口,注册的广播接收器等。这是service最后被调用的方法。

如果一个控件通过调用startService()来启动Service(将会调用onStartCommon()方法),这个Service将会一直运行直到它调用stopService()方法,或者其他控件调用stopService()来停止Service。
如果一个控件通过调用bindService()来创建Service(那么onStartCommon()不会被调用),这个Service将会一直运行知道控件解绑它。一旦这个Service被所有的控件解绑,这个Service将会被系统销毁。

Android系统将会关闭Service只有当内存低的时候,系统会把这些资源提供给有焦点的Activity。如果Service是被绑定在有焦点的Activity,这个Service被销毁的概率将会大大降低。如果Service被定义为运行在前台(run in the foreground这个之后讨论), 这个Service几乎不会被销毁。然而,如果一个Service被启动了并且已经长期运行,经过时间的推移,系统会降低它在后台任务列表中的优先级,这个Service将会很容易受影响被销毁掉。如果Service已经启动,你必须优雅地设计它通过系统来重启。如果系统销毁了Service, 在内存资源有可用的时候将会重新启动它。(当然这个还跟你在onStartCommon中返回的参数有关)。获取更多关于Service被系统销毁的信息,看Processes and Threading 这一章节。

在接下来的章节中,你将会看到不同的创建Service的方法,和在不同应用组件来使用Service。

在manifest中定义一个Service

跟Activity(或者是其他组件)一样,所有的Service你都必须定义在应用的manifest.xml文件中。
定义你的Service,添加一个标签作为标签的子标签,例如:

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

看标签,来获取更多关于在配置文件中定义Service的信息。

还有很多标签可以包含在标签里面来定义各种参数,比如开启Service运行时需要的权限。android:name标签是唯一一个必须定义的标签,这个指出了这个Service的类名。一旦你发布了你的应用,你将不能改这个名字,因为如果你改了,有些Intent的启动或者绑定Service都依赖于这个,将有无法启动的风险。

为了确定APP是安全的,一般使用明确的Intent来启动或绑定Service,并且不要定义Intent的过滤器。这很重要,你允许一下模糊方法来启动Service,你可以提供一些过滤器给Service,从Intent中包含的名字。但是这样你必须使用setPackage()给Intent设置package,这个给那个Service提供了足够的信息来模糊启动。

此外,设置android:exported = “false”标签来确保你的Service只有在你的APP中使用,这样就有效的停止了其他APP来启动这个service,甚至是使用明确的Intent。

创建一个已经启动的Service

一个已经启动的Service是另外组件调用startService()启动的,会调用onStartCommon()方法。

当一个Service已经被启动,它有自己的生命周期,并且独立于启动它的组件,这个Service可以在后台无限的运行,就算启动它的组件已经被销毁。这个Service需要调用stopSelf()来自我销毁,当它的工作已经完成了,其他的组件也可以调用stopService()来停止Service。

一个应用组件像Activity调用startService()来启动Service,可以通过Intent来传递任何参数给Service使用。这个Service在onStartCommon()方法里面可以获取到Intent。

例如,一个Activity需要保存一些数据到线上的数据库。这个Activity可以启动一个关联的Service,并且通过startService()方法把数据传递但Service来保存。Service在onStartCommon()方法里面获取到Intent,连接网络来执行数据库事务。当事务完成的时候,Service会自我停止,然后销毁掉。

注意:默认情况下一个Service运行在应用的主线程所在的同一个进程中,如果你的Service表现加强或阻塞操作当用户在一个Activity中相互影响, Service会降低activity的性能。为了不影响应用性能,你应该在Service里面开启一个子线程。

一般的,有两个类可以供你来继承或者创建一个启动的Service:
Service:
这是所有Service的基类。当你继承这个类,你需要创建一个子线程,来做所有服务工作,因为Service运行在主线程。默认地,这样将会所有运行的Activity在这个应用中的性能。
IntentService:
这是一个Service类的子类,这个将会在工作线程来执行请求工作。这是最好的选择当你的Service不需要同时执行多个请求。你需要做的是要实现onHandlerIntent()方法,这里将会收到Intent来启动请求,这样你就可以在后台工作。

在接下来的章节中,将会讨论怎么来使用这些类。

继承IntentService类

因为大多数的已启动Service不需要去同时执行多种请求(这回变成危险的多线程方案),这个时候最好的方案就是使用IntentService类。

IntentService有以下特性:

  • 创建一个默认的工作线程它会执行所有传递到onStartCommon()方法中Intent的请求,这个跟应用的主线程区分开来
  • 创建一个工作队列会把Intent一个接一个地传递给你实现的onHandlerIntent()方法,所以你永远都不用担心多线程。
  • 当所有请求都结束后将会停止Service,所以你不用去调用stopSelf()方法。
  • 提供了一个默认方法onBind()去实现,它返回null。
  • 提供一个默认的onStartCommon()方法实现,这个会发送Intent到工作队列,然后到你实现的onHandlerIntent()方法中。

所有这些必须要做的就是实现onHandlerIntent()方法,来做客户端提供的工作。(可是,你还需要提供Service构造方法)
下面是实现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.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}

你所需要做的就是一个构造方法和实现onHandleIntent()方法。
如果你决定去重写其他的回掉方法,例如onCreate(), onstartCommon(), onDestroy(), 确定调用了父类的方法。这样IntentService也许会这些生命周期在工作线程。
例如,onStartCommon()必须返回默认的实现方法(这里决定了onHandlerIntent()方法获取Intent传递)

@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实现,当继承基础Service类时。这会有很多代码,如果你需要同时处理多种请求。

继承Service类

跟你在之前章节看到的一样,使你使用IntentService来启动Service变得很简单。然而,你需要Service去执行多线程(代替工作队列来执行请求),这样你可以基础Service类来操作Intent。
比较,下面的例子代码是继承Service类,跟之前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'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();
  }
}

跟你看到的一样,这个将会比IntentService做更多工作。
然而,因为你分别执行调用onStartCommon(),你可以同时执行多个请求。这不是这个例子做的,但是如果这是你需要的,你可以为每一请求创建一个新线程,而且立刻运行。(而不是等优先级高的请求结束)
发现onStartCommon()方法必须返回一个整型,这个整型描述了系统怎么继续或是销毁这些Service,这些返回值必须是下面几个中的一个:

  • START_NOT_STICKY 如果系统在onStartCommon()后销毁Service返回,不会重新启动Service, 除非传递了pending Intent。这是一个最安全的方法来禁止Service在不需要的时候和你的应用可以简单重新启动任何没有结束的工作时运行。
  • START_STICKY 如果系统在调用完onStartCommon()后销毁了Service,将会重新创建并且调用onStartCommon()方法。但是不分发最后的Intent,而是系统调用onStartCommon()传递一个Intent。除非这有一个Pending Intent来启动Service,在这些情况下,这些Intent已经分配。这适合给媒体播放器(或简单Service)这些不直接执行,但是需要一直运行并且等待任务。
  • START_REDELIVER_INTENT 如果系统在调用完onStartCommon()后销毁了Service,将会重新创建并且调用onStartCommon()方法,并且最后一个Intent会被传递到Service。这个适合活动任务,需要立即恢复,像下载文件。
    查看跟多关于返回参数的细节,看每个分类的链接。
开启一个Service

你可以从一个Activity或其他组件,调用startService()方法传递Intent参数来启动。Android会调用onStartCommon()方法,并且传递Intent参数进去(你从来不要直接调用onStartCommon()方法)。
例如,一个activity可以使用明确的Intent和startService(),就像之前提到的一样。

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

startService() 方法将会立即返回,Android系统会调用Service的onStartCommon()方法。如果这个Service还没有在运行,系统将会调用onCreate()方法,然后调用onStartCommon()。
如果service也没提供绑定,Intent通过startService()分发是唯一一个方法和启动控件之间的通信。然而,如果你需要Service返回一个结果。你可以使用PendingIntent给broadcast来传递到Service启动。Service可以使用广播来分发结果。
多种请求来启动Service,将会调用onStartCommon()得到多种响应。然而只需要一个请求来停止Service(调用stopSelf()或者stopSevice())。

关闭一个Service

一个已经启动的Service需要自己管理生命周期。系统不会停止或注销Service除非它需要回收系统内存并且Service在调用onStartCommon()后开始运行。所以Service需要通过调用stopSelf()方法或者其他组件调用stopService()来停止它。一旦调用stopSelf()或者stopService()方法来停止,系统会尽快销毁Service。
然而如果你的Service同时在onStartCommon执行了多种请求,这样的当你执行完一个请求后不会销毁Service,因为这个时候可能刚刚收到一个新请求。为了避免这样的问题,你可以使用stopSelf(int)来确保你请求停止Service是基于最近的一个请求。当你调用stopSelf(int)的时候传递一个启动请求时的ID,来确保关闭的是最近一个请求。如果在你调用stopSelf(int)的时候有收到了一个新的请求,这样ID将会不匹配,也就不会停止Service。

注意:当Service工作完成的时候需要关闭自己,这很重要,可以避免浪费系统资源和电量。如果需要的话,其他组件可以通过调用stopService()来停止。甚至你是绑定这个Service的,也需要停止Service。
查看更多关于Service的生命周期,来看接下来 管理Service生命周期这一章节。

创建一个绑定的Service

一个绑定的Service允许应用组件调用bindService(),来创建一个长期连接(一般情况下,不允许组件调用startService()来启动Service)。
你需要创建一个绑定的Service,当你需要它和其他activity交互,或者通过IPC把应用的某些功能开放出来。
创建一个绑定的Service,你必须实现onBind()这个回掉方法来返回一个IBinder对象用作和Service之间的交互。其他应用组件可以调用bindService()来恢复那个接口,并且调用Service上面的方法。这个Service是为了服务绑定它的其他应用组件,所以当没有应用组件绑定它时,系统将会销毁它(你不需要停止一个绑定的Service)。
创建一个绑定的Service,最先要做的就是定义一个接口来指明什么样的组件可以跟这个Service有联系。这个组件跟Service之间联系的接口必须实现IBinder,并且这在你Service中onBind()中是必须返回的。一旦组件收到IBinder对象,它可以和Service通过这个接口来交互。
多个组件可以同时绑定一个Service,当组件和Service之间的交互结束时,需要调用unBindService()来解绑。一旦没有任何组件绑定这个Service,系统将会销毁这个Service。
这有很多种方法来实现绑定Service,并且这比开启一个Service复杂地多。所以绑定一个Service,将会在一个独立的章节讨论。

给用户发送通知

一旦运行Service,它可以使用Toast Notification和Status Bar Notifications来通知用户。一个Toast Notification是把消息显示在当前窗口,显示一段时间然后消失。然而,Status Bar Notifications是在Status Bar显示一个图标和消息。可以设置一个点击的动作(比如开始一个activity)。
通常,在后台工作完成后Status Bar Notifications是最好的方法来提醒(比如一个文件下载完毕),而且用户可以立即来操作这个。当用户通过扩展view点击这个通知,这个通知可以打开一个activity(比如去查看下载文件)

运行一个在前台的Service

一个前台运行的Service,可以被认为是用户可以活跃的,并且在内存低的时候不会考虑被销毁。一个前台Service必须提供一个Status Bar Notifications, 并被定义为“Ongoing”。这意味着这个通知将不会消失,除非这个Service被销毁或者不在前台显示。
例如,音乐播放器从Service播放音乐,需要被定义为前台运行。因为用户会明确去操作它。这个通知指出了当前播放的音乐,并且允许用户打开一个activity。
可以调用startForeground(),来开启一个前台运行Service。这个方法需要两个参数,一个integer类型的参数表示Notifications的类型。类如:

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()方法的integer类型的ID不能为0.
调用stopForeground(),来把Service从前台移除。这个方法需要传一个boolean参数,表示是否把Status Bar Notifications也移除。这个方法不会停止Service,然而如果当这个Service还在前台运行时你停止它,那通知也会被移除。

管理Service的生命周期

Service的生命周期比Activity的简单得多。然而在创建和销毁它的时候更加需要注意,因为它可以在用户不注意的情况下在后台运行。
Service的生命周期,它的创建和销毁可以分为两种:

  • 启动的Service
    当其他组件调用startService()时,可以创建Service。Service将会立即运行,必须调用stopSelf()来停止它。其他组件也可以调用stopService()方法来停止。当Service已经停止,系统会被销毁。
  • 绑定的Service
    这个Service是通过bindService()来启动的,组件和Service之间是通过IBinder接口交互的。组件可以通过unbindService()方法来断开交互。多个组件可以绑定同一个Service,当所有组件都解绑,系统将会销毁这个Service(这时Service不需要自己停止)。

这两种方法也不是完全分开的,你也可以绑定一个通过startService()方法启动的Service。例如,一个后台运行的音乐播放Service可以通过startService()来启动播放,之后用户希望操作这个播放器或者获取一些关于这个音乐的信息,可以调用bindService()将它和activity绑定。在这种情况下,调用stopService()或是stopSelf()都不能停止Service,直到所有组件都解绑后才会被销毁。

实现生命周期的回调方法

就像activity,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生命周期。

  • Service的整个存活时间是在onCreate()和onDestroy()之间。像activity一样,Service可以在onCreate()中进行一些初始化设置,并且在onDestroy()中进行资源的回收。例如一个音乐播放Service,可以在onCreate()中创建一个线程,然后在onDestory()中停止。
    onCreate()和onDestory()在任何Service中都会调用,不论是启动的还是绑定的。
  • Service的运行时间是在onStartCommand()方法或onBind()方法开始的,任何一个方法都会传递一个Intent参数进去。如果Service是启动的,它的活动时间结束和整个存活时间相同。如果Service是绑定的,活动时间将会在onUnbind()方法调用后结束。

小贴士:不管启动的Service是通过stopSelf()或者stopService()结束的,他们都没各自的回调,除非是绑定的,不然Service被系统销毁的时候都只能收到onDestroy()一个回调。

上面那个图阐述了Service典型的回调方法,尽管上图中将两种启动方法区分开来了,但是无论是哪种方法启动的,其他组件都可以来绑定它。所以,Service可以在onStartCommon()进行初始化(组件调用startService()方法),然后也可以继续有onBind()回调(当组件调用bindService()方法)。

以上就是对Google对Android开发官方文档Service这一模块的翻译,第一次写翻译有不好的地方尽情谅解,我也是尽自己可能保留英文原版的意思,只作翻译不加自己看法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值