Android Service的理论探讨与实践(一)service的启动方式以及区别的探索。

前言

       想转行android开发,本想年前投投简历,看能不能拿到offer呢,一番面试下来。感觉android知识面上还是太肤浅,很多知识往往知道怎么用,人家面试人员在问细一点就把自己打蒙了。比如经常被问到的一个点:
 1. service是什么?
 2. service的启动方式有哪些,有什么区别?
 这些在去面试之前做了些理论上的补课,真的是很多东西知道是这么回事,但是理论上说吧,又说不出来,不知道是不是自己嘴太笨的语言组织能力太差的缘故。比如第二个,我回答上来了service的启动方式和区别(下文展开讨论),人家听完又问了一个问题——onStartCommand()方法的返回值有哪些?分别代表什么意思?这下子把我问住了,我的天,我只知道service在主线程中在后台运行,对用户处于不可交互状态,用来执行某些需要长期运行的任务的时候用到service,但是使用过程中,压根没想到这个什么鬼的onStartCommand()的返回值问题,只晓得调用context.startService(intent)方法后,调用onCreate()然后调用onStartCommand()方法后service将处于运行状态。这个问题问的当场把我打蒙,乖乖说没注意过这个点,那叫一个尴尬。
 面试回来后,痛定思痛,各方面查资料。最后花了两三天时间把官方文档看了一遍,不懂的网上查漏补缺,很多以前没注意到的地方豁然有所得,特写此文记录service的过程已记录心得。
 本文将从以下几个点记录你自己探索service的过程,更多的是倾向于让自己站在求职者回答问题的角度上去求知:

  1. service是什么?
  2. service的启动方式以及区别的探索。
  3. onStartCommand()的返回值探索
  4. 前台服务探究
  5. service和线程的探索
  6. intentservice的探索。
  7. messenger的使用
  8. aidl的使用以及aidl和messenger的区别.
  9. BroactReceiver里为什么不能bind Service?

正文

# 1.什么是service?

  •  老生常谈,service是Android四大组件之一,在后台长期运行并切不提供和用户进行交互的接口。悄磨叽的就把事情给干掉了,不求露脸,但求低调。比如网络传输,播放音乐,文件读写,或者和内容提供者进行交互等这些不需要用户进行交互的,都可以让其在后台运行也就是放在service里
  •  service虽然在后台运行,但是它还是运行在主线程中。这就意味着不能在service里面进行CPU负载消耗严重的工作,这样将大大加大主线程的压力和运行效率。在这种情况下,可以在这service里面创建子线程让着些资源消型的任务放在子线程中运行

2.service的启动方式以及区别的探索。

 Service 有两种启动方式:
第一种:调用context.startService(Intent intent);
第二种:调用context.bindService(Intent service,ServiceConnection conn,int flags);
不同的开启服务方式,对应Serice不同的生命周期方法调用:

2.1 StartService

 当其他组件第一次调用startServeice()的时候,首先系统会调用onCreate()的方法,然后在调用onStartCommand()方法。然后此时Serice出去运行状态。此时通过此方法(调用startService)开启的Service和其开启者没有任何关系了,其生命周期比调用者更久。开启Service的组件没法调用Service的方法。比如在某个Activity里开启了一个Serice,如果此时销毁该Activity,则该Service仍然在后台默默的运行着。只有显式调用stopService(),或者Service自己调用StopSelf()才能停止该Service;
 对应这种方式下的Service生命周期如下:


简单使用案例:
service端代码:

public class LocalService extends Service {
    private static final String TAG = "LocalService";

    public LocalService() {
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate: 服务创建成功");
        super.onCreate();
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand: 服务开启咯");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: 服务器使命完成了");
        super.onDestroy();
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

client端代码:

简单的在布局文件中放了两个按钮,并直接绑定了两个点击事件处理方法

Intent serviceIntent;

    public void start(View v) {
        serviceIntent = new Intent(this, LocalService.class);
        startService(serviceIntent);
    }


    public void stop(View v) {
        stopService(serviceIntent);
    }

第一次点击start按钮和stop按钮打印输出:

02-07 06:48:00.974 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onCreate: 服务创建成功
02-07 06:48:00.975 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:48:09.617 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onDestroy: 服务器使命完成了

再次点击start按钮并多次点击start按钮的log如下

02-07 06:49:55.351 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onCreate: 服务创建成功
02-07 06:49:55.351 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:49:57.127 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:49:58.442 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:50:00.520 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onDestroy: 服务器使命完成了

实践出真知:

  •  可以看出,Service在开启前,首先调用而且仅调用一次onCreate()方法。onStartCommand()方法每次点击start按钮都会调用,但无论onStartCommand()方法无论调用多少次,调用一次stopService()方法,就可以关闭当前Service.
  •  通过调用startService(),调用者比如Activity,不能调用Service里自定义的方法。Service孤家寡人般的遗世独立。

2.2 BindService

 服务开启的第二种方式是,其他组件比如Activity调用bindService()。后面简称bindservice.以这种方式开启服务,开启Service的组件能调用Service的方法,bindservice的生命周期和开启它的组件一样长。并且一个bindservice可以被多个组件绑定,但是要停止该bindservice。需要绑定它的组件都取消绑定(调用unbind)的时候,该Service才会停止。一切理论总要经得起实践的推敲,在学习过程中还是要多思考,多敲代码,下面是简单的使用验证代码
先贴上bindservice对应的生命周期图


bindservice 代码

package com.example.yanchunguo.bindservice;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class BindService extends Service {
    private static final String TAG = "BindService";
    public BindService() {
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate: 绑定服务创建");
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: 开始绑定");
        return new MyBinder();
    }

    public class MyBinder extends Binder{
        /**
         * 客户端用过返回的binder ,来调用此方法,从而获取当前服务实例,
         * 用来调用Service的方法,比如下面的sayHello()
         * @return返回该服务的的当前对象
         */
        public BindService getService(){
            return BindService.this;
        }
    }

    public String sayHello(){
        return "你好,这美好的世界";
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: 服务器销毁咯");
        super.onDestroy();
    }
}

客户端代码:

/**
     * 点击按钮绑定服务
     * @param v
     */
    public void bind(View v) {
        bindLocalService();
    }

    private void bindLocalService() {

        Intent intent = new Intent(this, BindService.class);

        bindService(intent, mSerConection, Context.BIND_AUTO_CREATE);

    }

    private ServiceConnection mSerConection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "bind成功: ");
            BindService.MyBinder binder = (BindService.MyBinder) service;
            BindService bs = binder.getService();
             Log.i(TAG, "onServiceConnected: " +bs.sayHello());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "取消bind");
        }
    };

    public void unbind(View v) {
        unbindService(mSerConection);
    }

先看正常点击绑定按钮和取消绑定按钮,后台打印的log信息

02-07 07:49:03.603 28483-28483/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 07:49:03.603 28483-28483/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 07:49:03.608 28483-28483/com.example.yanchunguo.bindservice I/MainActivity: bind成功: 
02-07 07:49:03.608 28483-28483/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
02-07 07:49:10.387 28483-28483/com.example.yanchunguo.bindservice I/BindService: onDestroy: 服务器销毁咯

实践出真知——log信息中得知:

  1. 和startService启动模式一样,首先调用的是onCreate方法。
  2. 同一个组件比如Acitivty,多次调用bindservice。Service的onBind方法就调用一次。当然其他组件,比如另外一个Activity调用bindService()方法的时候,会再次调用onBind方法。

现在再次点击绑定按钮,

02-07 08:17:08.410 9839-9839/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 08:17:08.411 9839-9839/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 08:17:08.435 9839-9839/com.example.yanchunguo.bindservice I/MainActivity: bind成功: 
02-07 08:17:08.435 9839-9839/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界

上面的是服务正常创建和启动,和上面一样,没什么问题 ,然后点击手机虚拟返回键,打印的log信息如下:

2-07 08:17:24.260 9839-9839/com.example.yanchunguo.bindservice E/ActivityThread: Activity com.example.yanchunguo.bindservice.MainActivity has leaked ServiceConnection com.example.yanchunguo.bindservice.MainActivity$1@e376196 that was originally bound here
02-07 08:17:24.279 9839-9839/com.example.yanchunguo.bindservice I/BindService: onDestroy: 服务器销毁咯

log分析:
 首先,直接点击手机虚拟返回键(注意是直接点击返回,没有显式调用unbind进行解绑),当前开启Service的活动被销毁了,会打印一个错误log,提示有一个ServiceConnection泄露了(leaked),也就是Serivce没有解除绑定。
 然后紧接着打印出了在BindService中的重写(override)的onDestroy()方法。
 结论:验证了bindservice的生命周期依赖于其绑定的组件,组件销毁了,服务也销毁了

本篇总结

  • startservice和bindservice的共同点

  1. 两者的实现类都需要是Service的子类。
  2. 都在后台运行。
  • startservice和bindservice的区别

  1. 生命周期不同
     startservice的生命周期比较长,和启动它的组件如activity无关,启动组件销毁了,startservice也在后台运行,除了系统因为内存不够而销毁service,需要启动组显示调用stopService(),或者Service自己调用stopSelf()来停止Serivce
     bindservice的生命周期比较短,依赖于启动它的组件。绑定它的组件销毁了,service随之销毁,也可以显式调用unbindService()停止服务

  2. 和service交互上面
     startService的启动者组件不能调用定义在Service的方法。
     bindService的绑定组件能够调用定义在Service的方法

引申

既使用startService又实用bindService的情况。

 写着写着突然想起来这个情况,于是乎还是先从实践开始,然后通过log信息得出相应的结论
Service代码如下:在上班bindservice的基础伤重写了onStartCommand方法;这样此时的服务既可以用startService方法开启,又可以用bindService绑定了,同时也重写了onRebind()方法和onUnbind()方法。

public class BindService extends Service {
    private static final String TAG = "BindService";
    public BindService() {
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate: 绑定服务创建");
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: 开始绑定");
        return new MyBinder();
    }

    public class MyBinder extends Binder{
        /**
         * 客户端用过返回的binder ,来调用此方法,从而获取当前服务实例,
         * 用来调用Service的方法,比如下面的sayHello()
         * @return返回该服务的的当前对象
         */
        public BindService getService(){
            return BindService.this;
        }
    }
      @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, "onRebind: 重新绑定");
        super.onRebind(intent);
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "onUnbind: 解除绑定");
        return true;
    }

    @Override
    public int onStartCommand(Intent intent,int flags, int startId) {
        Log.i(TAG, "onStartCommand: 客户端调用onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    public String sayHello(){
        return "你好,这美好的世界";
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: 服务器销毁咯");
        super.onDestroy();
    }
}

对应的client代码:代码没什么改动,主要是把start按钮点击处理事件里面的启动服务由LocalService.class改成了BindService.class

/**
     * 点击按钮绑定服务
     * @param v
     */
    public void bind(View v) {
        bindLocalService();
    }

    private void bindLocalService() {

        Intent intent = new Intent(this, BindService.class);

        bindService(intent, mSerConection, Context.BIND_AUTO_CREATE);

    }

    private ServiceConnection mSerConection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "bind成功: ");
            BindService.MyBinder binder = (BindService.MyBinder) service;
            BindService bs = binder.getService();
            Log.i(TAG, "onServiceConnected: " +bs.sayHello());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "取消bind");
        }
    };

    public void unbind(View v) {
        unbindService(mSerConection);
    }

    public void open(View v) {
        Intent in = new Intent(this, OtherActivity.class);
        startActivity(in);
    }

    Intent serviceIntent;

    public void start(View v) {
        serviceIntent = new Intent(this, BindService.class);
        startService(serviceIntent);
    }


    public void stop(View v) {
        stopService(serviceIntent);
    }

    @Override
    protected void onDestroy() {
        Log.i(TAG, "onDestroy: 主活动销毁了");
        super.onDestroy();
    }

首先,先调用startSevice()。在调用bindservice()log信息如下

02-07 10:20:23.221 2458-2458/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 10:20:23.222 2458-2458/com.example.yanchunguo.bindservice I/BindService: onStartCommand: 客户端调用onStartCommand.
02-07 10:20:26.991 2458-2458/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 10:20:27.011 2458-2458/com.example.yanchunguo.bindservice I/MainActivity: bind成功: 
02-07 10:20:27.011 2458-2458/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界

根据log信息得知,先由startService开启服务后onCreate()先执行,然后onStartCommand()方法顺利执行,然后在调用bindServicede,发现直接调用的是onBind()方法。onCreate()方法不再执行。 反过来,刚开始先调用bindService()然后在调用startService()一样,onCreate()也只是执行一次,其它log信息和上面相反,先调用onBind(),在调用onStartCommand()。在此不再赘述。

重点来了:现在我们先调用unbindservice()看log

02-07 10:32:25.670 12704-12704/com.example.yanchunguo.bindservice I/BindService: onUnbind: 解除绑定

此时由log信息可以看出服务的onUnbind()方法调用了,但是和上面单独讲bindservice的log不同,此时并没有打印出“服务销毁”这句log.然后我们此时在调用stopService();看log

02-07 10:36:54.198 12704-12704/com.example.yanchunguo.bindservice I/BindService: onDestroy: 服务器销毁咯

此时服务才被销毁。 返回来实践也一样,先调用stopService(),服务并没有被销毁,需要在调用unbindService()之后服务器才销毁。具体可以操作实践,这个log就不贴了

回到最初的上面来,也就是调用了startService()和bindService()的基础上,在调用unbindservice(),此时不调用stopService()而是直接在调用bindService()。看你卡这次会打印什么

02-07 10:58:24.600 4320-4320/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 10:58:24.600 4320-4320/com.example.yanchunguo.bindservice I/BindService: onStartCommand: 客户端调用onStartCommand
02-07 10:58:25.607 4320-4320/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 10:58:25.627 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: bind成功: 
02-07 10:58:25.627 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
02-07 10:58:27.852 4320-4320/com.example.yanchunguo.bindservice I/BindService: onUnbind: 解除绑定
02-07 10:58:29.420 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: bind成功: 
02-07 10:58:29.420 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
02-07 10:58:29.421 4320-4320/com.example.yanchunguo.bindservice I/BindService: onRebind: 重新绑定

最后一个发现调用了onRebind()进行了重新绑定;

对于此种去看了下官方文档,查到了这种解释:

如果你的Service即通过调用startService()又通过bindService()进行绑定。那么在调用onbindService()进行解绑的时候,如果在重写的onUnbind()方法里返回true,在服务没有被停止的情况下,再次调用bindService()方法,会调用onRebind()方法。 上面的log也印证了这一点,同时官方也给出了相应的流程图,也印证了这一点,如下图 


引申总结

 如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStartCommand方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行,该服务被不同组件绑定了多少次就要unbindService多少次,也及时解除所有的额绑定。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值