Service是一个android的四大组件之一,它没有UI界面,可以在后台执行长时间的操作。其他的组件可以start一个Service,service启动以后会在后台持续运行,不管用户是否切换到了其他的应用程序。此外,其他的组件还可以绑定到service上,并和service做交互,甚至还可以执行跨进程的操作(IPC)。比如说,Service可以在后台处理网络请求、播放音乐,执行文件i/o或者跟content provider交互。 Bound 更多信息请参考“在manifest文件中声明service”(http://developer.android.com/gui ... ices.html#Declaring)这一节。 要创建一个service,必须要创建Service(或者是Service子类)的子类。在你的实现中,你要重写一些回调方法,它们用来处理service的一些关键的生命周期,并且提供组件绑定到service的机制,如果合适的话。 要重写的最重要的回调是: 当别的组件通过调用startService()来启动service的时候,系统会调用这个方法。一旦这个方法开始执行,service就启动起来并在后台无限运行。如果你覆盖了这个方法,你还要负责在service的工作结束以后通过调用stopSelf()或者stopService()销毁掉service。(如果你只是想提供绑定就不需要实现这个方法) 这就是你要做的全部的事情:一个构造函数和实现onHandleIntent()。 如果你想覆盖别的回调比如onCreate(), onStartCommand(),或者onDestroy(),一定要记得调用父类的实现,这样IntentService才可以正确的处理工作线程的生命周期。 比如下面,onStartCommand()必须要返回默认的实现(跟intent被传递到onHandleIntent()一样)。 @Overridepublic 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类 正如在上节中看到的那样,使用IntentService让实现一个started 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相比要做很多额外的工作。 但是,因为你在onStartCommand()中处理请求,所以你可以并发处理多个请求。这个就超出这个例子的范围了。但是如果你需要这么做,你可以给每个请求创建一个线程然后立即执行(而不是等待前一个请求执行结束)。 要注意的是,onStartCommand()必须要返回一个整数返回值。返回值描述了service被系统杀掉以后又被restart的时候,系统如何处理service(正如之前讨论的那样,IntentService默认会帮你做这个事,当然你也可以修改)。返回值必须是下面的几个常量: START_NOT_STICKY 如果onStartCommand()返回以后系统杀掉了service,restart的时候不会重新创建service,除非有pending intent要发送。这是最安全的方式来避免运行不必要的service,并且应用可以简单地重新开始任何没完成的任务。 START_STICKY 如果onStartCommand()返回以后系统杀掉了service,restart的时候会重新创建service,然后调用onStartCommand(),但是不会再次发送最后的intent。相反,系统会用null的intent来调用onStartCommand(),除非是有pending intent来启动service,这种情况下,这些intent是会传递进去的。这对媒体播放器(或者类似的service)来说是非常合适的,因为它们不执行命令,只是在无限的运行,等待新的任务。 START_REDELIVER_INTENT 如果onStartCommand()返回以后系统杀掉了service,restart的时候会重新创建service,然后调用onStartCommand(),并且会再次发送最后的intent。pending intents会被依次发送,这适合于那种需要立即被唤醒的情况,比如:下载文件。 了解更多关于返回值的信息,参考每一个常量的文档。 启动Service 你可以在Activity中后者是其他组件中通过给startService()传递intent(指定要启动的service)来启动service。Android系统会调用service的onStartCommand()方法然后把Intent传递进去(禁止直接调用onStartCommand())。 比如,Activity可以在startService()中用明确的intent启动前面章节中的例子service(HelloService)。 Intent intent = new Intent(this, HelloService.class);startService(intent); startService()方法会立即返回,Android系统会调用service的onStartCommand()方法。如果service还没有执行过,系统会首先调用onCreate()然后再调用onStartCommand()。 如果service不提供绑定,startService()方法传递进来的intent是唯一的跟外部组件进行通讯的方式。但是,如果你想让service发送结果出去,启动service的客户端可以创建一个做广播用的PendingIntent,(用getBroadcast()),把PendingIntent传递给service。service就可以使用广播来传递结果。 多个启动service的请求会导致相应的对service的多个onStartCommand()调用。但是,只需要一个停止service的请求就可以停掉service。 停掉service started service必须要自己管理生命周期。也就是说,系统不会停止或者销毁service除非是为了释放系统资源,service在onStartCommand()返回以后会继续运行。因此,service必须要调用stopSelf()停掉自己或者是其他组件调用stopService()来停掉service。 一旦用stopSelf()或者stopService()请求停掉service,系统就会尽快的将service销毁掉。 但是,如果你的service在onStartCommand()中并发的处理请求,当你处理完一个请求以后你不应该就停掉service,因为你可能又收到了一个新的请求(第一个请求结束后停掉service也会停掉第二个请求)。为了避免这样的事情,你可以使用stopSelf(int)来确保停止service的请求总是基于最近的请求。也就是说,当你调用stopSelf(int)的时候,你要传递对应停止请求的启动请求ID(startId会被传递到onStartCommand()中)。然后,如果service在调用stopSelf(int)之前,又收到了一个启动请求,ID匹配不上,service就不会停止。 注意:在service工作完成的时候把service停掉对于避免浪费系统资源和节省电量是很重要的。如果需要的话,其他的组件可以调用stopService()来停掉service。启用了binding的service,如果收到了onStartCommand()调用的话,也要手动停掉service。 了解更多关于service生命周期的知识,查看“管理Service的生命周期”下面的章节。 创建Bound Service bound service允许应用的组件调用bindService()绑定到service上,为了创建长时间存在的连接(一般不允许组件调用startService()来启动)。 当你想从Activity或者是其他组件跟service进行交互,或者是通过IPC暴漏应用的某些功能给其他应用的时候,你应该创建bound service。 要创建一个bound service,必须要实现onBind()回调,并且返回IBinder,它定义了跟service通讯的接口。其他的组件可以调用bindService()来检索出接口,然后调用service的方法。service只存活于它绑定到的组件上,所以,当没有组件绑定的时候,系统会销毁它(你不需要像停掉started service那样来停掉bound service)。 要创建一个bound service,首先要做的是定义客户端跟service交互的接口。service和客户端之间的接口必须是IBinder的实现,并且service必须要在onBind()回调中返回,一旦客户端收到了IBinder,它就可以通过接口跟service交互。 多个客户端可以同时绑定到同一个service上,当一个客户端跟service交互完成以后,它会调用unbindService()来解绑。如果没有客户端绑定到service上,系统就会把service销毁掉。 有很多种实现bound service的方式,一般实现要比started service更复杂,所以bound service会在单独的文章(http://developer.android.com/guide/components/bound-services.html)中做讨论。 给用户发送Notification 一旦运行以后,service可以使用弹出Notification或者是状态栏Notification给用户发送提醒事件。 弹出提醒是出现在当前窗口之上停留一段时间然后消失的消息提示,状态栏提醒在状态栏的消息中提供了一个icon,用户可以选中做一些操作(比如启动一个Activity)。 一般来说,当后台任务完成以后,用状态栏提醒是最佳的方式(比如:文件下载完成),然后用户可以做一些动作。当用户在展开的视图中选择一个提醒以后,提醒可以开启一个Activity(比如跳转到下载文件的view)。 前台运行service 注意: ID一定不能为0. 要从前台移除service,需要调用stopForeground()。这个方法接收一个boolean参数,用以表明是否移除状态栏的提醒。这个方法不会停掉service,但是,如果你想在service运行在前台的时候停掉它,提醒也会被移除掉。 管理service的生命周期 service的生命周期要远比Activity的生命周期简单。但是,你需要更加注意service的创建和销毁,因为service可以在用户不知晓的情况下在后台运行。 service生命周期-从创建到销毁-遵循下面的2中不同的形式: (1)started service 当其他组件调用startService()的时候service被创建出来,然后就无限的运行,它必须要手动调用stopSelf()来停掉自己。其他的组件可以调用stopService()来停掉它。当service停掉以后,系统会销毁它。 注意:跟Activity的生命周期回调不同的是,这些回调并不要求调用父类的实现。 通过实现这些方法,你可以监控service生命周期内部的2个循环: (1)service的完整的生命周期是从调用onCreate()到onDestroy()返回。跟activity类似,service也是在onCreate()中做初始化,在onDestroy()中释放资源。比如,音乐播放器service可以在onCreate()中创建播放音乐的线程,在onDestroy()中停掉这个线程。所有的service都会调用onCreate()和onDestroy(),不管service是用startService()还是bindService()创建出来。 (2)service的活动的时间是从调用onStartCommand()或者onBind()开始的.与此对应,这两个方法会处理通过startService()或者bindService()传递进去的intent。如果service是被started,当service的生命周期结束的时候也就是活动时间结束的时候(从onStartCommand()返回以后,service仍然是活动的)。如果service是bound的,onUnbind()返回的时候,service的生命也就结束了。 注意:如果一个started service是通过调用stopSelf()或者stopService()被停掉的,这时候并没有与之对应的回调(没有onStop()回调)。 因此,除非service是被绑定到了一个客户端上,否则一旦service停掉,系统就会去销毁它,onDestroy()是service收到的唯一的回调。 |
Android-Service组件
最新推荐文章于 2023-02-16 14:39:07 发布