Android Service:Creating a Started Service
Creating a Started Service
一个被开始的service(started service)是另一个组件调用startService()开启的,结果是这个service的onStartCommand()方法被调用。
当一个service被start,它就拥有一个独立于开始它的组件的生命周期,这个service可以无限地在后台运行,即使开始它的那个组件被销毁了。
所以,service应该在完成工作后自己停止,通过调用stopSelf(),或另一个组件可以通过调用stopService()来停止它。
简单说,就是:
一个应用组件,比如activity可以开启一个 service,通过调用startService() 方法并传递一个定制service和传递必要数据的intent对象。
Service会在 onStartCommand()方法中获取这个对象。
举个例子,一个activity需要把一些数据存储在一个网络上的数据库里,这个activity可以开启一个service,把要存储的数据通过一个startService()中的intent参数传递给service,service在 onStartCommand()方法中得到这个intent,建立网络连接并执行数据库事务。当事务处理完毕,service自己停止自己并销毁。
注意:默认情况下,service运行在声明它的应用的同一个进程里面,而且在应用的主线程里面。
所以,如果你的service需要执行一些繁重的或者阻塞性的工作,同时用户要和同一个应用中的activity进行交互,service会降低activity的性能。
为了避免妨碍应用性能,你应该在service中新开启一个线程。
可以继承两个类去创建一个started service
这是所有service的基类。
当你继承这个类时,比较重要的一点就是你需要创建一个新的线程,在里面做所有service的工作,因为这个service默认情况下会使用应用的主线程,这样会降低你的应用中正在运行的activity的性能。
这是一个Service类的子类,它使用一个工作线程(worker thread)来处理所有的开启请求,一次一个。
如果你不要求你的service同时处理多个请求的话,这是你最好的选择。
你需要做的仅仅是实现 onHandleIntent()方法,它将接收到每一个start请求的intent,所以你可以做后台的工作。
Extending the IntentService class
因为多数的service不需要同时处理多个请求,所以你可以使用 IntentService来实现你自己的service。
IntentService 做了如下的工作:
1.创建一个默认的worker thread,与应用的主线程分离,处理所有传递给 onStartCommand()方法的intent。
2.建立一个工作队列,一次传递一个intent给 onHandleIntent()
,所以你永远不用担心多线程问题。
3.当所有的start请求都被处理以后,停止service,所以你永远不必去调用 stopSelf()。
4.提供了onBind() 的默认实现,返回null。
5.提供了 onStartCommand()的默认实现,把intent传向工作队列,然后传向 onHandleIntent() 的实现。
所有的这些加起来,说明事实上你需要做的事情就仅仅是实现onHandleIntent()方法去做客户端提供的工作。
当然,你还需要提供一个构造方法,它必须调用基类的IntentService(String)构造方法。
下面是一个实现例子:
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()
, or onDestroy(),请确定一定要调用基类的实现,这样IntentService才能正确地管理worker线程的生命。
比如,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允许绑定的时候需要实现它。
Extending the Service class
如果你需要你的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相比,我们需要做更多的工作,但是,因为你自己处理onStartCommand()的每一次调用,所以你可以同时执行多个请求。
这个例子没有这样做,如果你想要同时处理多个请求,你可以为每个请求创建一个新的线程,然后立即执行它们,而不是等待上一个请求执行完毕再执行。
注意到onStartCommand()方法必须返回一个整形,这个整形描述了系统应该如何继续被系统kill掉的service,
返回值必须是下列常量之一:
如果在 onStartCommand() 方法返回后系统将service销毁,不重建service,除非有pending intent被传递。
这是最安全的一种选择,可以避免运行service在不必要的时候,和当你的应用可以重新开始未完成的工作的时候。
如果在 onStartCommand() 方法返回后系统将service销毁,重建service,并且调用onStartCommand()方法,但是不重新传递上一个intent。
系统调用 onStartCommand()方法时传递一个null intent。
除非有pending intent来开启这个service,在这种情况下,pending intent是被传递的。
这种情况对于多媒体播放器(或者其他类似的service)比较适合,它们不执行命令,但是它们等待工作并无限执行。
如果在 onStartCommand()
方法返回后系统将service销毁,重建service,并且调用onStartCommand()方法,上一个intent将会被传递给service。
任何的pending intent将顺次被传递。
这对于积极执行一项工作的service非常适合,因为它们应该被立即恢复,比如下载文件。
Starting a Service
你可以从一个activity或其他组件通过startService()
方法来开始一个service,需要传递一个Intent对象来指定要开启的service。
Android系统会调用service的onStartCommand()方法然后把Intent对象传递给它。
比如:
Intent intent = new Intent(this, HelloService.class); startService(intent);
startService() 方法会立即返回,Android系统会调用service的onStartCommand()方法,如果service不是已经在运行,那么系统会先调用它的 onCreate()方法,然后调用onStartCommand()。
如果service没有同时提供绑定,那么传递给startService() 方法的intent对象是调用组件和service之间唯一的交流形式。
但是,如果你想要service发送一个结果回来,那么开启service的客户可以为broadcast创建一个PendingIntent,(用getBroadcast()方法),然后把它作为开启service的intent传给service。这个service之后就可以使用这个broadcast来传递一个结果。
多次的开启请求会导致多次的onStartCommand()
方法被调用,然而,只需要一个停止的请求 (with stopSelf() or stopService()) 就可以将service停止。
Stopping a service
一个被开始的service必须管理自己的生命周期。
即,系统不会停止或者销毁service(除非系统必须恢复一些内存),service将会在 onStartCommand() 方法返回之后一直持续运行。
所以,service必须通过stopSelf()来停止自己,或者另一个组件通过stopService()来停止它。
一旦通过 stopSelf() or stopService()方法发送了停止的请求,系统将会尽快销毁service。
然而,如果你的service并行处理多个onStartCommand()请求,那么在你不应该在你处理完一个start请求之后stop这个service。因为你可能得到了一个新的start请求。(在第一个请求之后停止,有可能会终止第二个。)
为了避免这个问题,你可以使用stopSelf(int) 来确保你要停止的请求永远基于最近开始的service。
即是说,当你调用stopSelf(int) 时,你将start请求的ID (the startId
delivered toonStartCommand())传递过去。
这样如果你的service在你的调用stopSelf(int)前得到一个新的start请求,由于ID不匹配,这个service不会被stop。
注意:当service做完它的工作时,你的应用应该停掉它。这样可以避免系统资源浪费和电池能量消耗。
如果有必要,其他组件可以通过stopService()来停止service。
即便你允许绑定service,如果你的service有得到onStartCommand()方法的调用,你就永远必须自己停止它。
参考资料
API Guides:Services