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的工作,因为这个service默认情况下会使用应用的主线程,这样会降低你的应用中正在运行的activity的性能。

 

IntentService

  这是一个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的完全相同:

直接继承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();
    }
}

 

  可以看到,与使用IntentService相比,我们需要做更多的工作,但是,因为你自己处理onStartCommand()的每一次调用,所以你可以同时执行多个请求。

  这个例子没有这样做,如果你想要同时处理多个请求,你可以为每个请求创建一个新的线程,然后立即执行它们,而不是等待上一个请求执行完毕再执行。

 

  注意到onStartCommand()方法必须返回一个整形,这个整形描述了系统应该如何继续被系统kill掉的service,

  返回值必须是下列常量之一:

START_NOT_STICKY

  如果在 onStartCommand() 方法返回后系统将service销毁,不重建service,除非有pending intent被传递。

  这是最安全的一种选择,可以避免运行service在不必要的时候,和当你的应用可以重新开始未完成的工作的时候。

START_STICKY

  如果在 onStartCommand() 方法返回后系统将service销毁,重建service,并且调用onStartCommand()方法,但是不重新传递上一个intent

  系统调用 onStartCommand()方法时传递一个null intent。

  除非有pending intent来开启这个service,在这种情况下,pending intent是被传递的。

  这种情况对于多媒体播放器(或者其他类似的service)比较适合,它们不执行命令,但是它们等待工作并无限执行。

START_REDELIVER_INTENT

  如果在 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

  http://developer.android.com/guide/components/services.html

posted @ 2013-03-24 15:28  圣骑士wind  阅读(799)  评论(0编辑  收藏  举报