zoukankan      html  css  js  c++  java
  • Android高级开发第四讲API之Service

    博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 



    今天主要跟大家翻译一下Service的内容,请慢慢阅读!


    Service是一个应用组件,它可以在后台长时间的运行,而不产生用户界面。
    一个应用程序可以开启一个Service,即使切换到另外一个应用程序,它依然会继续执行。
    此外,一个组件可以绑定一个Service并与它进行交互,甚至进行进程间的通信(IPC)。
    例如,一个Service可以处理网络传输,播放音乐,执行文件I/0流,或者与Cotnent Provider进行交互,这所有一切都是在后台进行的。

    Service基本上有两种形式
    Started
    当一个应用程序组件(如Activity)调用"startService()"时,Service被开启。一旦开始,它就会一直在后台运行下去,即使创建它的组件已经被销毁。
    通常情况下,启动的Service会执行一个单一的操作,不会返回结果给调用者。例如,它可以通过网络上传下载文件,操作结束后,服务就应该被停止。
    Bound
    当一个应用程序组件调用"onBind()"时,Service被绑定。一个绑定的Service提供客户端-服务端接口,允许组件与Service进行交互,发送请求,得到结果,进行进程间的通信(IPC)。
    当一个应用程序组件绑定它的时候,它才正式开启。多个组件可以同时绑定到一个Service上,当所有组件解绑("unbind()")后,Service会被马上销毁。
    虽然这篇文章分析了Service的两种类型,但两者也可以同时使用,它可以被启动(无限期运行),也可以绑定运行。
    这是一个简单的问题,在于你是否实现了一个双向回调方法。通过"onStartCommand()"方法可以开户并绑定当前的Service。


    无论你的Service被开启,绑定或者两者都进行了,每一个应用程序组件都可以使用它(甚至是两个不同的应用程序),同时每一个应用程序都可以用一个activity通过Intent来启动它。
    然而,你可以通过mainifest文件将Service设为私有,阻止其他程序访问。接下来是更多讨论在mainifest文件声明Service的一部分。
    注意:Service运行在设备进程的主线程中,Service不会创建自己的线程,也不运行在一个单独的进程里(除非你另有指定)。这就意味着如果你的Service要进行CPU密集型工作或阻塞操作(如播放MP3或网络服务),你需要为这个Service提供一个新的线程来继续工作。使用一个新的线程,就可以减少应用程序不反应(ANR)错误和使主线程能致力于Activity与用户之间的交互。
    最基本的
    你应该用一个Service还是一个线程
    Service是一个可以在后台运行甚至用户没有与当前的应用程序交互的组件。因此,当你需要时你可以创建这样一个Service。如果你需要在主线程外执行工作,但只在用户交互时,这时你最好创建一个线程而不是一个Service。举例:如果你想播放一些音乐,只有当这些Activity在运行的时候,你可以在onCreate()方法里创建一个线程,在onStart()方法里运行,在onStop()方法里结束。也可以考虑AsyncTask和HandlerThread,取代传统的线程类。参考进程和线程文档获得更多有益信息。
    记得当你使用Service时,它仍然默认在你应用主线程里运行,所以当你要处理一些密集或阻塞操作时,要在Service里再创建一条线程。为创建一个Service,你必须创建一个Service子类(或已存在的子类)。在你的实现方法里,你需要重写一些处理Service生命周期关键方面的回调方法,如果适应的话。最重要的回调方法莫过于以下几个:
    onStartCommand()
    当另一个组件像Activity需要通过startService()来启动Service的时候,系统会调用这个方法。一旦这个方法执行了,Service就立马启动,并在后台开始运行。当你实现它们,你要在工作停止时停止Service,就要调用stopSelf()或者stopService()方法(如果你只想提供绑定,那你就不需要实现这个方法)。
    onBind()
    当另一个组件如执行RPC需要通过bindService()方法绑定Service时,系统会调用这个方法。在你的实现方法里,你必须提供一个接口,用作客户端与Service进行交流,并返回一个IBinder对象。你必须实现这个方法,但如果你不允许绑定,那么你可以返回null。
    onCreate()
    当Service第一次被创建时,系统会调用它,来执行一次启动程序(在调用onStartCommand()或onBind()方法之前)。如果你的Service已经存在,则这个方法不会被调用。
    onDestroy()
    当Service不再被使用时,系统会调用这个方法来销毁Service。你的Service需要实现这个方法用来清除如线程,注册的监听器,通知等等一些资源。这是Service最后接收的一个回调方法。如果一个组件通过startService()来启动一个Service(同时也会调用onStartCommand()),那Service仍然会保持运行直到调用stopSelf()来停止它或另一个组件调用stopService()来停止它。
    如果一个组件调用bindService()方法来创建一个Service(同时onStartCommand()方法没有被调用),那么Service只在这个组件处于激活状态时才运行。一旦客户端解绑了Service,系统就会销毁它。
    查看进程和线程文档
    在接下来的章节中,你会看到如何创建每种类型的服务,以及如何从其他应用程序组件中使用它。在manifest中声明一个服务类似的Activity(和其他组件),你必须声明所有的服务在您的应用程序的manifest中。
    声明你的服务,要添加一个<service>的元素,作为<application>元素的子元素。例如:
    <manifest ... >
      ...
      <application ... >
          <service android:name=".ExampleService" />
          ...
      </application>
    </manifest>
    还有其他可以包括在<service>元素中定义属性,如启动该服务所需的权限,服务应该运行的过程。android:name是唯一必需的属性,它指定的类名的服务。一旦你发布你的应用程序,你不应该更改此名称,因为如果你这样做,你可能会打破一些明显与它关联的意图(阅读博客文章,也是无法改变的事情)。
    请参阅<service>元素,获得在manifest中声明的更​​多信息。

    就像一个活动,一个服务可以定义意图过滤器,允许其它组件使用隐式意图来调用服务。如果您的服务定义了一个意图过滤器与另一个应用程序传递给startService()的意图相匹配,在用户的设备上安装任何应用程序,都可以通过声明的意图过滤器,来启动服务。如果您打算仅在本地使用您的服务(其他应用程序不使用它),那么你就不需要(也不应该)提供任何意图过滤器。没有任何意图过滤器,您必须使用意图准确的启动该服务。有关启动服务的更多信息,将在下面讨论。

    此外,您还可以确保您的服务是私有的,只有当你的应用程序的Andr​​oid:exported的属性设置为“false”。这也是有效的,即使你的服务提供的意图过滤器。
    要获得更多关于为service创建意图过滤器的信息,请参阅的意图和意图过滤器文件。

    创建一个启动的服务
    Andr​​oid 1.6或更低
    如果你正在建立一个为Android 1.6或更低版本的应用程序,你需要实现OnStart(),而不是onStartCommand()(在的Andr​​oid 2.0 onStart()被弃用,转而使用的onStartCommand())。对于兼容Android的版本低于2.0的更多信息,请参阅onStartCommand()的文档。

    启动一个服务:另一个组件开始调用startService(),引起在服务中调用onStartCommand()方法。当服务启动时,它有一个生命周期,独立于启动它的组件并可以无限期地在后台运行,即使启动它的组件被销毁。因此,服务完成了自己的工作就应该调用stopSelf(),或让其他组件调用它的stopService()方法。
    一个应用程序组件如Activity可以通过调用startService()启动一个服务,并可传递一个区分此Service的意图,外带一些Service所需的数据。该服务在onStartCommand()方法中能过intent来接收数据。

    例如:假设一个activity需要保存一些数据到线上数据库,那么这个activity 可以通过自己启动一个service,然后在service的startServie()方法里来保存传过来的数据。service会在onStartCommand()里接收到intent,连接网络,执行数据库事务,当事务执行完毕,service自动结束,然后销毁。
    注意:service默认与当前应用运行在同一个进程中,所以当用户正与activity进行的时候,如果你的service执行密集或者阻塞的操作台,就会明显降低activity的活性。为了避免这种情况的发生,你应该在service里启动一个新的线程。
    一般来说,在创建启动service时有两个类可以继承。
    Service
    这是所有service的基类。当你继承自这个类时,最好创建一个新的线程来操作service的任务,因为默认下它会在应用的主线程中运行,这可能会降低应用中正在运行activity的活性。
    IntentService
    这是service的子类,它使用工作线程来一次处理一个请求,如果你不一次性处理多个请求,那这个类是极好的。你该做的就是实现onHandlerIntent(),来接收每次请求的intent,在后台工作。接下来的部分是来说明怎么使用上面两个基类的。
    继承IntentService
    因为大多数service不用一次性处理多个请求(这实际是一个危险的多线程情况),所以这时最好使用IntentService。
    介绍IntentService
    在onStartCommand()创建一个默认的工作线程来执行应用主线程传递过来的所有intent。创建一个工作队列,每次发送一个intent到onHandlerInent()中,这样你就不用担心多线程的情况。所有请求都被处理完后,就会结束这个service,所以不用调stopSelf()。
    提供一个默认的onBind(),返回值为null。
    提供一个默认的onStartCommand()来把intent发到工作队列里,然后执行onHandleIntent()。
    最终所有的落实工作都由onHandleIntent()来做(你还需要为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(),onStartCommand(),onDestroy(),确保要调用super()来加载父类方法,这样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(),剩下的所有方法都要调用super方法,onBind()除外(但如果你的service要求实现bind(),那还是要实现滴)
    继承Service
    在接下来的部分,你可以看到各种继承service的类,一些有很多代码,但可以同时处理多个请求。
    正如你看到的上个部分,使用IntentService来实现一个service是很简单的。但是,如果你需要service来执行多线程(而不是通过一个工作队列来启动请求),那么你就可以继承service来处理每一个intent。
    为了比较,下面的例子都是继承自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(); 
      }
    }
    正如你所见到的,service中所做的工作比IntentService中多的多。
    但是,因为你在onStartCommand()中处理每个调用,你可以一次处理多个请求。上面的例子不是这么干的,但是如果你想,你也可以为每个请求创建一个线程来处理(而不是等上一个请求结束)
    注意onStartCommand()必须返回一个integer类型,这个integer值用来描述当系统杀死service时,应不应该在事件中重复这个service。
    (正如上面所讨论的,默认的intentService为你处理这个,虽然你也可以修改它)。onStartCommand()的返回值必须是下面的几个值之一。
    START_NOT_STICKY
    如果系统在onStartCommand()返回后就杀死service,那么不要重建service,除非有很多需要处理的intent。这是最安全的选项来避免不必要时运行service,此时你的应用可以简单的简单的重新启动未完成的工作。
    START_STICKY
    如果系统在onStartCommand()返回后就杀死service,重建service同时调用onStartCommand(),但不传递上一个intent。相反,系统调用带空intent的onStartCommand(),除非有正要启动service的intent存在,在这种情况下,这些intent会被传达。这适用于媒体播放器(或简单的服务),不执行命令,但无限执行并等待工作。
    START_REDELIVER_INTENT
    如果系统在onStartCommand()返回后就杀死service,重建servic同时调用onStartCommand(),带着传递给service的上一个intent。任一有待处理的intent都会顺序执行。这适用于service所有动态做一个马上被恢复活动的工作,如下载一个文件。有关返回值的更多信息量,可以参考API文档。
    启动一个service
    你可能通过activity或其他应用组件,发送intent来启动一个service,安卓系统调用service的onStartCommand(),并传递给它一个intent(不能直接调用onStartCommand())。
    Intent intent = new Intent(this, HelloService.class);
    startService(intent);
    startService()方法执行很快,然后执行onStartCommand()。如果service没有运行机制,系统还会先调用onCreate(),然后接着执行。
    如果service不提供bind方法,那么startService()里的intent是组件与service之间的唯一通信。然而,如果你想让这个servicev发送一个返回值,那么启动service的客户端可以为一个方法创建一个PendingIntent,放在itnent里面,然后传送给service,service可以用这个广播发送结果。
    多个请求来启动service,结果是在onStartCommand()中有多个响应,但只有一个停止方法。
    停止service
    一个启动的service必须管理自己的生命周期。这就是说,系统不会停止或销毁service,除非必须重启系统,那么service仍然可以在onStartCommand()后执行。因此,service必须通过调用stopService()或者其他组件调用stopService()来停止它的工作。
    一旦调用stopSelf()或stopService(),那么service会被尽快销毁。
    但是,如果service在onStartCommand()同时处理多个请求,当你处理完一个请求时,你不能停止这个service。因为你可能收到一个新的请求(在第一个请求结束后调用stopService()会结束第二个请求)。为了避免这个问题,你可以调用stopSelf(int)来确保停止的service是基于最近的start请求。原理是这样的,当你调用stopSelft(int)时,你把启动请求的ID(传给onStartCommand()的ID)给停止请求的ID。那当你在stopSelf(int)之前接收一个新的请求时,这个ID就不符合,service也不会停止工作。
    注意:service执行结束关闭它是很有必要的,来避免浪费系统资源和电池。如果可以,用其他组件一样可以通过stopService()来停止这个service。即使你能绑定这个service,如果它的onStartCommand()被调用了,你也必须亲自关闭它。
    获得更多有关service生命周期的信息,请参见service生命周期管理一文。
    创建一个绑定的服务
    一个绑定服务意味着应用组件可以通过bindService()来绑定服务并建立一个长连接(并且直接禁止其他组件通过startService()来启动)
    当你想让service与其他activties和其他应用组件进行沟通,或者扩展你应用的功能到其他应用,那应该创建一个绑定服务通过IPC来实现。
    要创建一个绑定服务,你必须实现onBind()回调方法,返回一个定义service通信接口的IBinder对象。其他应用组件可以通过调用bindService()检索接口来调用service里的方法。service依赖于绑定它的应用组件,所以当没有可绑定的组件时,系统就会销毁它(你不必像通过onStartCommand()启动service一样必须关闭serevice)。
    要创建一个绑定服务,第一步要做的是定义客户端与service通信的接口。它是IBinder的实现类并且返回onBind()回调方法。一旦客户端收到这个IBinder,它就可以通过接口来与service进行通信。
    多个客户端可以同时绑定一个service。规则是这样的:当一个客户端与service做完交互后,它调用unbindService()来解绑。一旦没有客户端与service绑定时,它就会被系统销毁。
    有多种途径来实现一个绑定service,实现方法也比单纯启动一个service复杂的多,所以绑定服务在另一个叫Bound Services的文档有更详细的表述。
    向用户发送通知
    一旦运行,service会通过Toast通知或状态栏事件来通知用户。
    Toast通知以消息的形式短暂的显示在当前屏幕,状态栏通知则会在状态条里提供图标和消息一起显示,用户可以自由选择。
    通常,状态栏是通知用户任务执行完毕的最好技术,而且用户可以点击它。当用户选择它时,它会启动一个Activity(如查看下载文件)。
    在前台启动服务
    前台service是用户所知道的,并不会在内存不足时销毁。前台服务必须为状态栏提供一个通知,放在“正在运行”下面,意味着它只能被停止或清除,不然不会消失。
    例如,音乐播放器的service被放在前台,因为用户要操作它。状态栏的通知表明用户可用播放器对当前歌曲进行操作。
    为了让你的service运行在前台,要调用startForeground()。这个方法使用两个参数,integer类型标识通知和通知栏的唯一性。例如:
    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, notification);
    要清除前台service,就要调用stopForeground()。使用一个布尔值表示是否同时也清除通知栏。这个方法不会停止service,但是如果你在service运行在前台时停止它,通知也会被清除。
    注:startForeground()和stopForeground()都是在2.0(API Level 5)引进的。为了让service运行在前台,你必须提前使用setForeground(),详见startForeground()文档。
    管理service的生命周期
    service的生命周期要远比activity简单。但是你要非常重视service的创建和销毁,因为它可以在用户不可知的情况下运行在后台。
    service生命周期,从创建到销毁有两种不同的路线。
    a started service
    当其他组件调用startService()时service被创建,service会无限期执行,因此要调用sotpSelf()。另一个组件也可以通过调用stopService()来停止这个service,当service停止后,系统就会销毁它。
    a bound service
    当其他组件调用bindService()时service被创建,客户端与service之间通过IBinder接口来沟通。客户端可以通过unbindService()来关闭连接。多个客户端可以绑定同一个service,当所有都解绑时,系统会销毁它(这个service不用自我回收)。
    这两种路线不是完全独立的。也就是说,你可以bind一个通过startService()启动的service。例如,后台音乐service可以通过调用startService()来启动并无限期播放。晚一会儿,可能用户对歌曲进行操作,或者获得当前歌曲的一些信息,那么activity可以通过bindService()来绑定这个service。在这种情况下,stopService()或stopSelf()不会完全停止这个service,直到客户端解绑。
    实现生命周期回调方法
    就像一个activity,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的生命周期那样,你可以不调用superclass实现的那些方法。


    图2.service生命周期.下面图1表示用startService()来启动一个service,图2表示用bindService()来启动一个service。
    通过实现这些方法,你可以监控service生命周期的两种嵌套循环。
    整个生命周期从onCreate()开始,到onDestroy()结束。就像activity,service在onCreate()里初始化,在onDestroy()里释放资源。例如,音乐播放的后台service可以在onCreate()创建一个线程来播放音乐,在onDestroy()里停止这个线程。
    onCreate()和onDestroy()无论是startService()还是bindService()启动的service都要被调用。
    service的活动周期起于onStartCommand()或onBind()的调用。每个方法都分别通过startService()或bindService()来传递intent。
    注:即使一个service被stopSelf()或stopService()停止了,却没有一个回调方法返回给service。所以,即使客户端绑定了service,service停止时系统会销毁它,onDestroy()是唯一收到回调方法的。
    图2.演示的是典型的service回调方法。虽然要区分创建service的startService()和bindService,但不管怎样记住一点,service还是允许被绑定的。所以,一个通过startService()启动的service,一样可以绑定。
    获得更多创建service的信息,请参阅Bound Services文档,里面有讲关于绑定生命周期管理的onRebind()回调方法。

  • 相关阅读:
    PE系统盘
    python常用方法手记
    python tushare
    idea导入导出java jar包
    javaweb项目配置tomcat启动
    window安装tomcat
    解决idea控制台乱码
    maven手记
    java redis常见问题
    java mysql常见问题
  • 原文地址:https://www.cnblogs.com/fengju/p/6174478.html
Copyright © 2011-2022 走看看