zoukankan      html  css  js  c++  java
  • Service

    Service

    Service是Android应用程序的基本组件之一。跟Activity不同,Service一般没有提供用户界面,在后台运行。在实际的开发过程中,我们可以将一些任务(例如:从网络上下载文件、播放音乐等)封装到Service中,这样有几个好处:1)减少模块之间的耦合度;2)Service可以被外部应用程序调用,实现进程间通信。

    Service启动后运行在宿主进程的主线程,Service不会创建自己的线程或者进程(除非你特别声明)。如果Service执行的任务耗时较长,阻塞到主线程,你需要在Service中创建一个线程,在新创建的线程中执行这些任务。阻塞主线程会造成用户界面僵死,影响用户体验,阻塞时间超过5秒,系统就会弹出ANR错误对话框。

    组件启动一个Service后,Service将一直在后台运行,直到组件去终止Service或者Service自行退出。系统只会在内存资源不足的情况下,才可能杀死运行中的Service。

    Service的运行模式

    1)  Started模式

    Started模式是组件调用startService(Intent)来启动Service,startService接受一个Intent对象。

    下面是通过startService启动本地Service的例子。在Intent构造函数第二个参数中指定Service的class类型,第一个参数是上下文对象,在Activity启动Service时,第一个参数传入this。

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

    在你创建的Service类中重写onStartCommand()方法。startService调用后,在Service类的内部,onStartCommand()回调函数将被调用,在onStartCommand()中,你可以根据传入的Intent值,执行不同的处理操作。

    1 @Override
    2 public int onStartCommand(Intent intent, int flags, int startId) {
    3 handleCommand(intent);
    4
    5 // We want this service to continue running until it is explicitly
    6 // stopped, so return sticky.
    7
    8 return START_STICKY;
    9 }

    如果你要启动远程的Service,Intent构造函数传入Intent动作(Action)的字符串,系统会查找已经安装的Service,启动满足条件的Service。

    1 Intent it = new Intent("com.tony.han.TEST_SERVICE");
    2
    3 startService(it);

    2)  Bound模式

    Bound模式是外部组件(比如Activity)调用bindService(Intent, ServiceConnection, int)来启动Service,bindeService返回一个IBinder对象,外部组件使用这个IBinder对象与Service通信。由于IBinder特性,外部调用组件和Service组件可以存在于不同的进程中。

    下面是使用bindeService启动Service的例子。Intent对象的构造与startService方式相同。

    1 // Bind to LocalService
    2
    3 Intent intent = new Intent(this, LocalService.class);
    4
    5 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    在你定义的Service类中重写onBind()方法。bindService调用后,在Service类的内部,onBind()回调函数将被调用。

     1 // Binder given to clients
    2 public class LocalBinder extends Binder {
    3 LocalService getService() {
    4 return LocalService.this;
    5 }
    6 }
    7
    8 @Override
    9 public IBinder onBind(Intent intent) {
    10 return mBinder;
    11 }

    客户端使用Bound模式的Service还需要实现ServiceConnection接口。bindService调用成功后,IBinder对象会通过ServiceConnection的回调函数onServiceConnected()传递给客户端,客户端程序将IBinder对象保存起来,后续就可以通过这个IBinder对象与Service通信。

     1     private ServiceConnection mConnection = new ServiceConnection() {
    2 @Override
    3 public void onServiceConnected(ComponentName className, IBinder service) {
    4 LocalBinder binder = (LocalBinder) service;
    5 mService = binder.getService();
    6 mBound = true;
    7 }
    8 @Override
    9 public void onServiceDisconnected(ComponentName arg)
    10 {
    11 mBound = false;
    12 }
    13 };

    在你实现Service时,如果不想让客户端通过Bound模式与你的Service通信,在重写onBind()方法时返回null值。

    3)  Started和Bound混合模式

    Service可以同时以Started和Bound模式运行。外部组件调用了startService(Intent)启动Service后,也可以同时调用bindService()获取IBinder对象,与正在运行的Service通信,只要Service内部实现了onStartCommand()和onBind()回调函数。

    创建Service的步骤

    1)  创建一个类,继承Service类或者Service的子类

    2)  重写一些必要的回调函数(如onStartCommand(), onBind()等)

    3)  在AndroidManifest.xml文件的<Application>元素下添加<Service>子元素,在android:name属性指定Service的名字

    如果外部组件要通过Intent动作来启动Service,需要在AndroidManifest.xml文件中为Service添加<intent-filter>。添加了<intent-filter>后,外部应用程序就可以通过指定Intent动作名称来启动这个Service,也就是说,Service对外可见。

    <service android:name=".Service0325">
    <intent-filter>
    <action android:name="com.tony.han.TEST_SERVICE"></action>
    </intent-filter>
    </service>

    如果你不想外部应用程序使用你的Service,可以添加android:exportd属性

    <service android:name=".Service0325" android:exported=”false”>
    </service>

    关于onCreate()和onDestroy()方法

    onCreate()

    Service在创建时会被调用一次,onCreate()方法的调用发生在onStartCommand()或onBind()钱。因此,可以在onCreate()做初始化的操作(如创建Service的工作线程等)。另外,如果Service已经在运行,另一组件启动这个Service时,onCreate()不会被调用。

    onDestroy()

    在Service销毁前被调用一次,与onCreate()对应。资源清理的工作(如终止Service的工作线程)可以在此方法中完成。

    停止Service的运行

      跟Activity不同的是,Service需要自己管理其生命周期。在Started模式中,停止Service运行的方法是由Service自己调用stopSelf()/stopSelfResult(int)方法或者其他组件调用stopService()方法;在Bound模式中,当只有一个组件绑定了Service,调用unbindService后,Service被销毁,当多个外部组件同时绑定Service时,最后一个组件调用了unbindService()后,Service才被销毁。

      Service以Started模式启动后,只有Service自己调用stopSelf()/stopSelfResult(int)方法或者其他组件调用stopService()方法后,Service才会终止运行。也就是说,在Started和Bound混合模式下,假设组件A是以Started模式启动的Service,组件B以Bound模式绑定到Service上,当组件B调用unBindService()后,Service不会销毁。只有组件A调用了stopService()或Service自己调用了stopSelf()/stopSelfResult(int)后,Service才会销毁。

      多个组件以Started模式启动同一个Service,onStartCommand()会被调用多次,但是只有一个Service实例存在,只需要调用一次stopService()或者stopSelf(),Service就会停止运行。

    关于IntentService

             在Service中创建一个线程执行耗时较长的操作,这样可以解决Service阻塞主线程的问题。引入了多线程,你需要编码时解决线程间同步的问题。Android提供了IntentService类用于简化开发多线程的Service的难度。

             IntentService是Service的子类,IntentService内部创建了一个工作线程来处理用户的请求(Intent),来自客户端的请求保存在队列中,工作线程每次从队列取出一个请求进行处理,当队列中的请求都处理完后,IntentService自动调用stopSelf()终止Service。

             IntentService在onHandleIntent(Intent)方法中处理用户的请求,onHandleIntent(Intent)在工作线程中调用。下面是使用IntentService实现Service的例子,我们只需要重写onHandleIntent(Intent)方法。

     1 public class HelloIntentService extends IntentService {
    2 /**
    3 * A constructor is required, and must call the super IntentService(String)
    4 * constructor with a name for the worker thread.
    5
    */
    6
    7 public HelloIntentService() {
    8 super("HelloIntentService");
    9 }
    10
    11 /**
    12 * The IntentService calls this method from the default worker thread with
    13 * the intent that started the service. When this method returns, IntentService
    14 * stops the service, as appropriate.
    15
    */
    16
    17 @Override
    18 protected void onHandleIntent(Intent intent) {
    19 // Normally we would do some work here, like download a file.
    20 // For our sample, we just sleep for 5 seconds.
    21 long endTime = System.currentTimeMillis() + 5*1000;
    22 while (System.currentTimeMillis() < endTime) {
    23 synchronized (this) {
    24 try {
    25 wait(endTime - System.currentTimeMillis());
    26 } catch (Exception e) {
    27 }
    28 }
    29 }
    30 }
    31 }

    使用IntentService,我们一般情况下只需要重写IntentService中定义的onHandleIntent(Intent)方法即可。如果你想重写其他方法(如onCreate(),onStartCommand()等),在重写的方法中需要调用父类的方法。

    1 @Override
    2 public int onStartCommand(Intent intent, int flags, int startId) {
    3 Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    4 return super.onStartCommand(intent,flags,startId);
    5 }

    关于onStartCommand()的返回值

             onStartCommand()返回int类型的数据,返回值告诉系统当Service被杀死后,系统应当怎样处理。

    onStartCommand()返回值的意义:

    START_NOT_STICKY

    当Service被杀死后,系统不会重启Service。Service只在需要时才会启动。

    START_STICKY

    当Service被杀死后,系统自动重启Service,调用Service的onStartCommand(),传递Intent为null。因为传入的Intent为null,重新启动后的Service什么也不做,仅仅是等待下一次客户端的Intent。这种情况的例子是媒体播放器服务。

    START_REDELIVER_INTENT

    当Service被杀死后,系统自动重启Service,调用Service的onStartCommand()时传递被杀死前正在处理的Intent。也就是说,Service重启后继续上次的任务。这种情况的例子是下载文件服务。

    在实际的开发中,我们需要根据业务需要来决定onStartCommand()的返回值。

    关于Bound模式的IBinder对象

    IBinder定义了远程对象的接口,是Android系统实现轻量级的、高效率的进程间通信的核心部分。这个接口定义了本地对象与远程对象进行通信的抽象协议,本地对象与远程对象可能在不同的进程中。一般情况下,我们实现IBinder接口时不用直接继承IBinder,继承Binder类就可以了。Binder类实现了IBinder接口。为了叙述方面,我们把实现了IBinder接口的对象简称为IBinder对象。简单的说,应用程序利用IBinder对象与远程对象通信。

    在前面我们已经说过,实现Bound模式的Service的关键是实现onBind()方法,onBind()方法要返回一个IBinder对象,问题的关键是怎么实现IBinder对象。

    Android为我们提供了三种方法实现IBinder对象。

    1)  继承Binder类

    如果你开发的应用程序客户端和Service在同一进程中,不涉及跨进程通信。你可以定义一个类继承Binder类,在onBind()方法中返回这个类的实例。前面说过,Binder实现了进程间通信,这里使用Binder是否合适?Binder能够实现进程间通信,进程内的通信更不是问题。

    这种方法是推荐的方式,除非你需要实现进程间通信。

    2)  使用Messenger

    如果你开发的应用程序客户端与Service在不同进程,可以使用Messenger。Service中实现Handler类,Handler中处理客户端发来的Message,Messenger与Handler关联。onBind()方法返回的IBinder对象由Messenger提供。

    3)  使用AIDL

    利用AIDL定义Service的接口,通过Android SDK的相应工具为你自动生成Binder类。前面介绍的Messenger与AIDL方式本质上是相同的。利用AIDL更加灵活,也更加复杂。本文不对AIDL方式详细说明。

    下面是继承Binder类实现Service的实例代码。定义LocalBinder类继承Binder,在LocalBinder中定义一个接口getService(),getService()直接返回Service的引用。在onBind()方法中直接返回LocalBinder对象的引用。

     1 public class LocalService extends Service {
    2 // Binder given to clients
    3 private final IBinder mBinder = new LocalBinder();
    4 // Random number generator
    5 private final Random mGenerator = new Random();
    6
    7
    8 /**
    9 * Class used for the client Binder. Because we know this service always
    10 * runs in the same process as its clients, we don't need to deal with IPC.
    11 */
    12 public class LocalBinder extends Binder {
    13 LocalService getService() {
    14 // Return this instance of LocalService so clients can call public methods
    15 return LocalService.this;
    16 }
    17 }
    18
    19
    20 @Override
    21 public IBinder onBind(Intent intent) {
    22 return mBinder;
    23 }
    24
    25
    26 /** method for clients */
    27 public int getRandomNumber() {
    28 return mGenerator.nextInt(100);
    29 }
    30 }

    由于客户端与Service在同一进程中,客户端在ServiceConnection的回调函数中获得IBinder对象后,强制转换成LocalBinder对象,就可以调用LocalBinder获得Service对象。

     1 /** Defines callbacks for service binding, passed to bindService() */
    2 private ServiceConnection mConnection = new ServiceConnection() {
    3
    4 @Override
    5 public void onServiceConnected(ComponentName className, IBinder service) {
    6 // We've bound to LocalService, cast the IBinder and get LocalService instance
    7 LocalBinder binder = (LocalBinder) service;
    8 mService = binder.getService();
    9 mBound = true;
    10 }
    11
    12 @Override
    13 public void onServiceDisconnected(ComponentName arg0) {
    14 mBound = false;
    15 }
    16 };

    下面是使用Messenger的例子。在onBind()方法中,通过调用mMessenger.getBinder()获得IBinder对象。

     1 public class MessengerService extends Service {
    2 /** Command to the service to display a message */
    3 static final int MSG_SAY_HELLO = 1;
    4
    5
    6 /**
    7 * Handler of incoming messages from clients.
    8 */
    9 class IncomingHandler extends Handler {
    10 @Override
    11 public void handleMessage(Message msg) {
    12 switch (msg.what) {
    13 case MSG_SAY_HELLO:
    14 Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
    15 break;
    16 default:
    17 super.handleMessage(msg);
    18 }
    19 }
    20 }
    21
    22 /**
    23 * Target we publish for clients to send messages to IncomingHandler.
    24 */
    25 final Messenger mMessenger = new Messenger(new IncomingHandler());
    26
    27 /**
    28 * When binding to the service, we return an interface to our messenger
    29 * for sending messages to the service.
    30 */
    31 @Override
    32 public IBinder onBind(Intent intent) {
    33 Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
    34 return mMessenger.getBinder();
    35 }
    36 }

    在客户端的代码中,利用得到的IBinder对象创建Messenger对象,客户端利用Messenger与Service进行通信。

     1 private ServiceConnection mConnection = new ServiceConnection() {
    2 public void onServiceConnected(ComponentName className, IBinder service) {
    3 // This is called when the connection with the service has been
    4 // established, giving us the object we can use to
    5 // interact with the service. We are communicating with the
    6 // service using a Messenger, so here we get a client-side
    7 // representation of that from the raw IBinder object.
    8 mService = new Messenger(service);
    9 mBound = true;
    10 }
    11
    12 public void onServiceDisconnected(ComponentName className) {
    13 // This is called when the connection with the service has been
    14 // unexpectedly disconnected -- that is, its process crashed.
    15 mService = null;
    16 mBound = false;
    17 }
    18 };
  • 相关阅读:
    Java中的File类
    scala语法
    Spark核心原理
    资源调度器
    YARN工作机制
    MapReduce原理和工作过程
    序列化
    Hbase(2)表的设计和Rowkey等的设计
    Hbase(1)架构和工作原理
    Exception in thread "main" java.lang.NoSuchMethodError: io.netty.buffer.PooledByteBufAllocator.metric()Lio/netty/buffer/PooledByteBufAllocatorMetric;
  • 原文地址:https://www.cnblogs.com/tonyhan/p/2416782.html
Copyright © 2011-2022 走看看