Service的启动、绑定以及生命周期

概述

  • Service是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件。
  • 服务可由其他应用组件启动,且即使用户切换到其他应用,服务仍将在后台继续运行。
  • 组件可绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。
  • 服务可处理网路事务、播放音乐、执行文件IO或与内容提供程序交互。

Service类

  1. 要创建服务,必须创建Service的子类或使用它的一个现有子类。

  2. 声明服务

    <manifest ... >
        ...
        <application ... >
            <service 
                android:name="服务的包类名"
                android:exported="false" />
            ...
        </application>
    </manifest>
    
  3. 重写生命周期的回调方法

    1. onCreate()
      • 何时:首次创建服务时。
      • 注意:若服务已在运行,则不会调用此方法。
    2. onDestroy()
      • 何时:当服务不再使用且将被销毁时。
      • 作用:清理所有资源,如线程、注册的侦听器、接收器等。
      • 注意:这是服务接收的最后一个调用。
    3. int onStartCommand(Intent intent, int flags, int startId)
      • 何时:当另一个组件调用startService()请求启动服务时。
      • 参数:
        1. intent:startService()启动服务时传入的Intent;
        2. startId:唯一id标识此次服务的启动请求。
      • 返回值:描述系统应该如何在服务终止的情况下继续运行服务。
    4. IBinder onBind(Intent it)
      • 何时:当另一个组件调用bindService()与服务绑定时。
      • 返回值:供客户端与服务进行通信。
  4. 服务的销毁

    1. 调用startService()启动服务,则服务将一直运行,直到其自身使用stopSelf()或由其他组件调用stopService()来停止。
    2. 调用bindService()创建并绑定服务,则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统会销毁它。
    3. 同时被启动和绑定的服务,要经历上面两种才能被销毁。
    4. 仅当内存过低且必须回收系统资源以供具有用户焦点的Activity使用时,系统才会强制停止服务(前台运行的服务除外)。
  5. onStartCommand()的返回值

    1. START_NOT_STICKY
      • 默认情况下,系统不会重新创建服务。除非有将要传递来的Intent时,系统重新创建服务,并调用onStartCommand(),传入此Intent。
      • 这是最安全的选项,可以避免在不必要的时候运行服务。
    2. START_STICKY
      • 系统重新创建服务,并调用onStartCommand(),默认是传入空Intent。除非存在将要传递来的Intent,那么就传递这些Intent。
      • 适合播放器一类的服务。独立运行,但无需执行命令,只等待任务。
    3. START_REDELIVER_INTENT
      • 系统重新创建服务,并调用onStartCommand(),传入上次传递给服务执行过的Intent。
      • 适合像下载一样的服务。立即恢复,积极执行。

启动服务

创建启动服务

1. 扩展Service类

  • 与APP同进程的服务是运行在主线程中,不可作耗时操作,但可以创建子线程来完成耗时操作。

  • 创建Service的子类,重写onCreate()onStartCommand()onDestroy()等方法。

2. 扩展IntentService类

  • IntentService执行以下操作:

    1. 创建工作子线程,用于执行传递给onStartCommand()的所有Intent;
    2. 创建工作队列,用于将Intent一个个传递给onHandleIntent()处理;
    3. 在处理完所有启动请求后停止服务。
    4. 提供onBind()的默认实现,返回null;
    5. 提供onStartCommand()的默认实现,将Intent发送到工作队列。
  • 创建IntentService的子类,只需要一个构造函数和重写onHandleIntent()即可。若重写其他方法,要确保调用超类实现,因为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) {
              } 
          } 
      } 
  }
  
  @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    );
    } 
}

启动服务

  • startService(Intent it)启动服务。
Intent intent = new Intent(this, HelloService.class);
startService(intent);

停止服务

  • 服务必须通过调用stopSelf()自行停止运行,或者由另一个组件通过调用stopService()来停止它。

绑定服务

  • 创建Service的子类,必须重写onBind()以返回IBinder,这个对象定义了组件与服务之间交互的编程接口。

  • 组件可通过调用bindService(Intent service, ServiceConnection conn, int flags)绑定到服务。

    1. 参数service:能标识要绑定服务;
    2. 参数conn:监控服务的连接和断开;
      1. 服务连接时,会回调conn的void onServiceConnected(ComponentName name, IBinder service)
      2. 服务因crash或killed而断开时,会回调conn的void onServiceDisconnected(ComponentName name)
    3. 参数flags:绑定操作标识。可选有0, BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND,BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, BIND_WAIVE_PRIORITY
  • 组件可通过调用unbindService(ServiceConnection conn)解绑。

  • 多个应用组件可以同时绑定同一个服务。不过,只有在第一个组件绑定时,系统才会调用服务的onBind()方法来创建IBinder。

  • 当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非startService()也启动了该服务)。

IBinder接口实现

  • 绑定服务提供给客户端组件,用来组件与服务进行交互的编程接口。

1.扩展IBinder类

  • 若你的服务仅供本地APP使用,不需要跨进程,则可实现自有Binder类,让你的客户端通过该类直接访问服务中的公共方法。

  • 这个实现方案只有在客户端和服务位于同一个APP同一个进程内才有效。

  • 设置方法

    1. 在服务类中,创建Binder实例,要满足以下任一要求:
      • 包含客户端可调用的公共方法;
      • 返回当前服务实例,该服务要包含客户端可调用的公共方法;
      • 返回由服务承载的其他类的实例,该实例要包含客户端可调用的公共方法。
    2. 在服务的onBind()回调方法中返回此Binder实例。
    3. 在客户端中,从onServiceConnected()回调方法中接收Binder,并使用其提供的公共方法操作服务。
public class LocalService extends Service {
    // Binder given to clients 
    private final IBinder mBinder = new LocalBinder();
    // Random number generator 
    private final Random mGenerator = new Random();
 
    /** 
     * Class used for the client Binder.  Because we know this service always 
     * runs in the same process as its clients, we don't need to deal with IPC. 
     */ 
    public class LocalBinder extends Binder {
        LocalService getService() { 
            // Return this instance of LocalService so clients can call public methods 
            return LocalService.this;
        } 
    } 
 
    @Override 
    public IBinder onBind(Intent intent) {
        return mBinder;
    } 
 
    /** method for clients */ 
    public int getRandomNumber() { 
      return mGenerator.nextInt(100);
    } 
} 
public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;
    
    @Override 
    protected void onStart() { 
        super.onStart(); 
        // Bind to LocalService 
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    } 
 
    @Override 
    protected void onStop() { 
        super.onStop(); 
        // Unbind from the service 
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        } 
    } 

    /** Defines callbacks for service binding, passed to bindService() */ 
    private ServiceConnection mConnection = new ServiceConnection() {
 
        @Override 
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance 
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        } 
 
        @Override 
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        } 
    }; 
} 

2.使用Messenger

  • 如需让服务与远程进程通信,则可使用Messenger。
    1. 服务类中实现一个Handler,由其接收来自客户端的每个调用的回调;
    2. 创建Messenger对象时需传入Handler;
    3. onBind()时返回Messenger对象.getBinder()给客户端;
    4. 客户端在onServiceConnected()中接收到IBinder,并将其强制类型转换为Messenger对象。调用Messenger的send(Message msg)来给服务发送消息;
    5. 服务类中的Handler的handleMessage()接收客户端发来的消息。
public class MessengerService extends Service {
    /** Command to the service to display a message */ 
    static final int MSG_SAY_HELLO = 1;
 
    /** 
     * Handler of incoming messages from clients. 
     */ 
    class IncomingHandler extends Handler { 
        @Override 
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break; 
                default: 
                    super.handleMessage(msg);
            } 
        } 
    } 
 
    /** 
     * Target we publish for clients to send messages to IncomingHandler. 
     */ 
    final Messenger mMessenger = new Messenger(new IncomingHandler());
 
    /** 
     * When binding to the service, we return an interface to our messenger 
     * for sending messages to the service. 
     */ 
    @Override 
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    } 
} 
public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */ 
    Messenger mService = null;
 
    /** Flag indicating whether we have called bind on the service. */ 
    boolean mBound;
 
    /** 
     * Class for interacting with the main interface of the service. 
     */ 
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been 
            // established, giving us the object we can use to 
            // interact with the service.  We are communicating with the 
            // service using a Messenger, so here we get a client-side 
            // representation of that from the raw IBinder object. 
            mService = new Messenger(service);
            mBound = true;
        } 
 
        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been 
            // unexpectedly disconnected -- that is, its process crashed. 
            mService = null;
            mBound = false;
        } 
    }; 
 
    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what' value 
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try { 
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        } 
    } 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    } 
 
    @Override 
    protected void onStart() { 
        super.onStart(); 
        // Bind to the service 
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    } 
 
    @Override 
    protected void onStop() { 
        super.onStop(); 
        // Unbind from the service 
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        } 
    } 
} 

3.使用AIDL

绑定与解绑服务

  • 流程简述

    1. 客户端实现ServiceConnection
      1. 重写onServiceConnected()
      2. 重写onServiceDisconnected()
      3. 客户端调用bindService()绑定服务
    2. 与服务连接时,系统会回调onServiceConnected,要保存IBinder对象,并使用其调用服务。
    3. 客户端调用unbindService()解绑服务。注意,此时不会回调onServiceDisconnected(),这个方法只会在服务crash或killed才会被回调。
  • 何时绑定与何时解绑

    1. 若只需要在Activity可见时与服务交互,则应在onStart()期间绑定,在onStop()期间解绑。
    2. 若希望Activity在后台停止运行时仍可接收响应,则在onCreate()期间绑定,在onDestroy()期间解绑。
    3. 切勿在onResume()期间绑定和onPause()期间解绑,这是因为每一次生命周期转换都会发生这些回调,频率过高。

绑定服务的生命周期

绑定服务的生命周期
  • 绑定服务的回调方法解析
    1. IBinder onBind(Intent intent);
      1. 参数intent:来自客户端组件的bindService()
      2. 返回值:提供给客户端访问服务的公共方法。
      3. 何时被系统调用:服务被首次绑定,且onUnbind()返回false。
    2. boolean onUnbind(Intent intent);
      1. 参数intent:来自客户端组件的bindService()
      2. 返回值:返回true表明当之后被新客户端绑定时,不调用onBind(),而是调用onRebind()
      3. 何时被系统调用:当所有客户端都解绑时。
    3. void onRebind(Intent intent);
      1. 参数intent:来自客户端组件的bindService()
      2. 何时被系统调用:经历过所有客户端解绑,且onUnbind()返回true之后,有新的客户端绑定。

前台服务

  • 服务一般都是运行在后台,系统优先级比较低,当系统内存不足时,很有可能被回收掉。而前台服务被认为是用户主动意识到的一种服务,因此即使内存不足,系统也不会考虑将其终止。

  • 前台服务必须为状态栏提供通知,也就是说除非服务停止或从前台删除,否则不能清除通知。

  • Service类的void startForeground(int id, Notification notification)可让服务运行于前台。

    1. 参数id:通知的唯一标识,不可为0;
    2. 参数notification:在状态栏上显示,表明这是前台服务的通知。
  • Service类的void stopForeground(boolean removeNotification)可停止前台服务,布尔值参数指示是否删除状态栏的通知。注意此方法不会停止普通服务,只是针对前台服务。

服务的生命周期

服务的生命周期
  1. 启动服务
    1. 一个组件调用startService()启动服务,服务不存在则会创建且onCreate()被系统调用;
    2. onStartCommand()被系统调用;
    3. 这种服务可无限地运行下去,必须自己调用stopSelf()或调用stopService()来停止它,onDestroy()被系统调用。
  2. 绑定服务
    1. 一个组件调用bindService()绑定服务,服务不存在则会创建且onCreate()被系统调用;
    2. onBind()被系统调用,并返回IBinder,提供给组件与服务进行通信;
    3. 组件调用onbindService()来解绑。多个组件可绑定到同一服务,当所有解绑后,其onUnbind()被调用,之后系统会销毁它且调用其onDestroy()
  3. 混合服务
    • 以上两条路径并非完全独立。服务可被启动和被绑定,这种混合服务只有在所有组件解绑,且自身调用stopSelf()或调用stopService()才会停止运行。

我的博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 1,400评论 0 5
  • [文章内容来自Developers] Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。...
    岳小川阅读 853评论 0 7
  • 绑定服务: 绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity)绑定到服务、发送请求...
    pifoo阅读 1,220评论 0 4
  • 大吵了一架后,和他冷战了三个多月,这期间,谁也不搭理谁。他在干什么,过得怎样,有没有生病……我不知道。他也不知道我...
    f964145b03d3阅读 421评论 0 2
  • 1. 在床上呆了一个早上,不是没有事情做,而是懒洋洋的,只想放松自己,什么都不想做,什么都不想动。感觉自己特像一只...
    千里留荒阅读 220评论 0 1