编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 1.1App启动流程梳理
  • 1.2ActivityThread分析
  • 1.3Context设计思想
  • 2.1Activity基础介绍
  • 2.2Activity启动流程
  • 2.3Activity布局创建
  • 2.4Activity布局绘制
  • 2.5Service基础介绍
  • 2.6Service启动流程
  • 2.7Receiver广播基础
  • 2.8Receiver深入原理
  • 2.9ContentProvider分析
  • 2.10Fragment实践
  • 2.11Intent深入思考
  • 3.1Paint和Canvas
  • 3.2View的绘制基础
  • 3.3onMeasure流程设计
  • 3.4onLayout流程设计
  • 3.5onDraw流程设计
  • 3.6View工作原理
  • 3.7View刷新设计流程
  • 3.8自定义View控件
  • 3.9自定义ViewGroup控件
  • 4.1Handler基础使用
  • 4.2消息机制流程分析
  • 4.3Handler深度解析
  • 4.4Message深度理解
  • 4.5MessageQueue解析
  • 4.6Looper深度解析
  • 4.7理解Handler同步屏障
  • 4.8ThreadLocal分析
  • 4.9ThreadLocal场景
  • 5.1View事件设计思考
  • 5.2View滑动冲突处理
  • 5.3View事件源码分析
  • 5.4View事件总结案例
  • 6.1DecorView设计思想
  • 6.2视图的载体View
  • 6.3视图管理者Window
  • 6.4窗口管理服务WMS
  • 6.5布局解析者Inflater
  • 7.1AsyncTask深入介绍
  • 7.2HandlerThread设计
  • 7.3IntentService设计
  • 8.1IPC通信方式介绍
  • 8.2AIDL进程间通信
  • 8.7Binder通信机制设计
  • /zh/android/basic/9.1注解设计思想和原理.html
  • 9.2APT技术设计详解
  • 9.3APT多种案例实践

2.5Service基础介绍

目录介绍

  • 01.Service基础介绍
    • 1.1 Service基础介绍
    • 1.2 使用场景和作用
    • 1.3 Service应用场景
  • 02.Service分类说明
    • 2.1 Service种类说明
    • 2.2 两种启动方法
    • 2.3 声明Service配置属性
  • 03.Service生命周期
    • 3.1 对应生命周期
    • 3.2 对应生命周期
  • 04.Service案例演示
    • 4.1 不可交互的后台服务
    • 4.2 可交互的后台服务
    • 4.3 混合型后台服务
  • 05.详细说下绑定服务

01.Service基础介绍

1.1 Service基础介绍

  • Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类,只不过它没有UI界面,是在后台运行的组件。
  • Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作。

1.2 使用场景和作用

  • 注意:Service默认是运行在main线程的,因此Service中如果需要执行耗时操作(大文件的操作,数据库的拷贝,网络请求,文件下载等)的话应该在子线程中完成。
  • 使用场景:
    • 如果只是想开个服务在后台运行的话,直接startService即可,
    • 如果需要相互之间进行传值或者操作的话,就应该通过bindService。远程服务(不同应用程序[App]之间),通过bindService来绑定并且获取代理对象。

1.3 Service应用场景

  • Service是Android系统中的四大组件之一,主要有两个应用场景:
    • 后台运行和跨进程访问。Service可以在后台执行长时间运行操作而不提供用户界面,除非系统必须回收内存资源,否则系统不会停止或销毁服务。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。
  • 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。
    • 需要注意的是,Service是在主线程里执行操作的,可能会因为执行耗时操作而导致ANR

02.Service分类说明

2.1 Service种类说明

  • 按运行地点分类:
  • 按运行类型分类:
  • 按使用方式分类:

2.2 两种启动方法

  • Service 的启动分两种情况:startService 和 bindService。
  • startService
    • 说明:如果服务已经开启,不会重复的执行 onCreate(), 而是会调用 onStart() 和onStartCommand()。服务停止的时候调用 onDestory()。服务只会被停止一次。
    • 当应用组件通过调用**startService()**启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
  • bindService
    • 绑定服务不会调用 onStart() 或者 onStartCommand() 方法。
    • 当应用组件通过调用**bindService()**绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。多个组件可以同时绑定服务,服务只会在组件与其绑定时运行,一旦该服务与所有组件之间的绑定全部取消,系统便会销毁它。
  • 还有一种特殊情况:启动并绑定
    • 服务既可以是启动服务,也允许绑定。此时需要同时实现以下回调方法:**onStartCommand()**和 onBind()。系统不会在所有客户端都取消绑定时销毁服务。为此,必须通过调用 stopSelf() 或 stopService() 显式停止服务。

2.3 声明Service配置属性

  • 如同其他组件一样,想要使用Service,必须在清单文件中对其进行声明。声明方式是添加 < service > 元素作为 < application > 元素的子元素。例如
    <application>
        <service android:name=".utils.push.PushService"
            android:enabled="true"
            android:exported="false"
            android:process=":pushcore">
            <intent-filter>
                <action android:name="cn.jiguang.user.service.action" />
            </intent-filter>
        </service>
    </application>
  • android:name
    • 属性是唯一必需的属性,用于指定服务的类名,还可将其他属性包括在 < service > 元素中以定义一些特性
    • 为了确保应用的安全性,最好始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。 启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样可以充分消除目标服务的不确定性
  • android:exported
    • 属性并将其设置为 "false",确保服务仅适用于本应用。这可以有效阻止其他应用启动本应用内的服务,即便在使用显式 Intent 时也是如此
  • android:process=":pushcore"
    • 用来运行服务的进程的名称。通常,应用程序的所有组件都运行在应用程序创建的默认进程中,它与应用程序包名具有相同的名称。 < application >元素的process属性可以为所有组件设置不同的默认值,但组件可以使用自己的进程属性覆盖默认值,从而允许跨多个进程扩展应用程序。
  • Service包含的属性有
    <service android:description="string resource"
             android:directBootAware=["true" | "false"]
             android:enabled=["true" | "false"]
             android:exported=["true" | "false"]
             android:icon="drawable resource"
             android:isolatedProcess=["true" | "false"]
             android:label="string resource"
             android:name="string"
             android:permission="string"
             android:process="string" >
    </service>
  • description
    • 对服务进行描述,属性值应为对字符串资源的引用,以便进行本地化
  • directBootAware
    • 设置是否可以在用户解锁设备之前运行,默认值为“false”
  • enabled
    • 设置是否可以由系统来实例化服务。< application >元素有自己的enabled属性,适用于包括服务在内的所有应用程序组件。要启用服务,< application >和< service >属性必须都为“true”(默认情况下都为true)。如果其中一个是“false”,则服务被禁用
  • exported
    • 设置其他应用程序的组件是否可以调用本服务或与其交互,如果可以,则为“true”。当值为“false”时,只有同一个应用程序或具有相同用户ID的应用程序的组件可以启动该服务或绑定到该服务。该属性的默认值取决于服务是否包含Intent filters。没有任何过滤器意味着它只能通过指定其确切的类名来调用,这意味着该服务仅用于应用程序内部使用(因为其他人不知道类名)。所以在这种情况下,默认值为“false”。另一方面,如果存在至少一个过滤器,意味着该服务打算供外部使用,因此默认值为“true”
  • icon
    • 服务的图标,属性值应是对drawable资源的引用。如果未设置,则将使用应用程序图标
  • isolatedProcess
    • 设置该服务是否作为一个单独的进程运行,如果设置为true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限,与它唯一的通信是通过服务API(绑定和启动)
  • label
    • 可以向用户显示的服务的名称,属性值应是对字符串资源的引用
  • permission
    • 设定组件必须具有的权限,得以启动服务或绑定服务。如果startService(),bindService()或stopService()的调用者没有被授予此权限,则该方法将不会工作,并且Intent对象不会传递到服务中

03.Service生命周期

3.1 Service生命周期

  • Service生命周期
  • OnCreate()
    • 系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作。如果service已经运行,这个方法不会被调用。
  • onStartCommand()
    • 每次客户端调用startService()方法启动该Service都会回调该方法(多次调用)。一旦这个方法执行,service就启动并且在后台长期运行。通过调用stopSelf()或stopService()来停止服务。
  • OnBind()
    • 当组件调用bindService()想要绑定到service时(比如想要执行进程间通讯)系统调用此方法(一次调用,一旦绑定后,下次再调用bindService()不会回调该方法)。在你的实现中,你必须提供一个返回一个IBinder来以使客户端能够使用它与service通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null。
  • OnUnbind()
    • 当前组件调用unbindService(),想要解除与service的绑定时系统调用此方法(一次调用,一旦解除绑定后,下次再调用unbindService()会抛出异常)。
  • OnDestory()
    • 系统在service不再被使用并要销毁时调用此方法(一次调用)。service应在此方法中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一个调用。

3.2 对应生命周期

  • 下面介绍三种不同情况下Service的生命周期情况。
  • 对应的生命周期如下:
  • context.startService()
    • ->onCreate()- >onStartCommand()->Service running--调用context.stopService() ->onDestroy()
  • context.bindService()
    • ->onCreate()->onBind()->Service running--调用>onUnbind() -> onDestroy()

3.3 startService / stopService

  • 生命周期顺序:onCreate->onStartCommand->onDestroy
    • 如果一个Service被某个Activity 调用 Context.startService方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务,还有一种方法可以关闭服务,在设置中,通过应用->找到自己应用->停止。
  • 注意点:
    • ①第一次 startService 会触发 onCreate 和 onStartCommand,以后在服务运行过程中,每次 startService 都只会触发 onStartCommand
    • ②不论 startService 多少次,stopService 一次就会停止服务

3.4 bindService / unbindService

  • 生命周期顺序:onCreate->onBind->onUnBind->onDestroy
    • 如果一个Service在某个Activity中被调用bindService方法启动,不论bindService被调用几次,Service的onCreate方法只会执行一次,同时onStartCommand方法始终不会调用。
    • 当建立连接后,Service会一直运行,除非调用unbindService来接触绑定、断开连接或调用该Service的Context不存在了(如Activity被Finish——即通过bindService启动的Service的生命周期依附于启动它的Context),系统在这时会自动停止该Service。
  • 注意点:
    • 第一次 bindService 会触发 onCreate 和 onBind,以后在服务运行过程中,每次 bindService 都不会触发任何回调

3.5 混合型(上面两种方式的交互)

  • 当一个Service在被启动(startService)的同时又被绑定(bindService),该Service将会一直在后台运行,并且不管调用几次,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数一致(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会停止Service,必须调用stopService或Service自身的stopSelf来停止服务。
  • 在什么情况下使用 startService 或 bindService 或 同时使用startService 和 bindService?
    • ①如果你只是想要启动一个后台服务长期进行某项任务那么使用 startService 便可以了。
    • ②如果你想要与正在运行的 Service 取得联系,那么有两种方法,一种是使用 broadcast ,另外是使用 bindService ,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且 BroadcastReceiver 本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用 bindService(这个时候你便同时在使用 startService 和 bindService 了,这在 Activity 中更新 Service 的某些运行状态是相当有用的)。
    • ③如果你的服务只是公开一个远程接口,供连接上的客服端(android 的 Service 是C/S架构)远程调用执行方法。这个时候你可以不让服务一开始就运行,而只用 bindService ,这样在第一次 bindService 的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是Remote Service,那么该效果会越明显(当然在 Service 创建的时候会花去一定时间,你应当注意到这点)。

04.Service案例演示

4.1 不可交互的后台服务

  • 不可交互的后台服务即是普通的Service
    • 通过startService()方式开启。Service的生命周期很简单,分别为onCreate、onStartCommand、onDestroy这三个。
  • 创建服务类:
    public class StartService extends Service {
    
        private static final String TAG = "StartService";
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG , "onBind");
            return null;
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.d(TAG , "onUnbind");
            return super.onUnbind(intent);
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d(TAG , "onCreate");
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d(TAG , "onDestroy");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG , "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    }
  • 配置服务:
    <service android:name=".StartService">
    </service>
    • 如果想配置成远程服务,加如下代码:
    android:process="remote"
    • 配置好Service类,只需要在前台,调用startService()方法。
  • 日志打印
    //startService
    2021-09-18 10:57:09.139 6763-6763/com.ycbjie.ychybrid D/StartService: onCreate
    2021-09-18 10:57:09.140 6763-6763/com.ycbjie.ychybrid D/StartService: onStartCommand
    //stopService
    2021-09-18 10:57:16.177 6763-6763/com.ycbjie.ychybrid D/StartService: onDestroy
    //startService
    2021-09-18 10:57:17.743 6763-6763/com.ycbjie.ychybrid D/StartService: onCreate
    2021-09-18 10:57:17.743 6763-6763/com.ycbjie.ychybrid D/StartService: onStartCommand
    //startService
    2021-09-18 10:57:18.663 6763-6763/com.ycbjie.ychybrid D/StartService: onStartCommand
    //startService
    2021-09-18 10:57:19.872 6763-6763/com.ycbjie.ychybrid D/StartService: onStartCommand
  • 注意:
    • ①不运行在一个独立的进程中,它同样执行在UI线程中,因此,在Service中创建了子线程来完成耗时操作。
    • ②当Service关闭后,如果在onDestory()方法中不关闭线程,你会发现我们的子线程进行的耗时操作是一直存在的,此时关闭该子线程的方法需要直接关闭该应用程序。因此,在onDestory()方法中要进行必要的清理工作。

4.2 可交互的后台服务

  • 可交互的后台服务是指前台页面可以调用后台服务的方法,通过bindService()方式开启。Service的生命周期很简单,分别为onCreate、onBind、onUnBind、onDestroy这四个。
  • 可交互的后台服务实现步骤是和不可交互的后台服务实现步骤是一样的,区别在于启动的方式和获得Service的代理对象。
  • 创建服务类
    • 和普通Service不同在于这里返回一个代理对象,返回给前台进行获取,即前台可以获取该代理对象执行后台服务的方法
    public class BindService extends Service {
    
        private static final String TAG = "BindService";
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG , "onBind");
            //返回MyBinder对象
            return new MyBinder();
        }
    
        //需要返回给前台的Binder类
        static class MyBinder extends Binder {
            public void showTip(){
                Log.d(TAG , "showTip , 我是来此服务的提示");
            }
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.d(TAG , "onUnbind");
            return super.onUnbind(intent);
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d(TAG , "onCreate");
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d(TAG , "onDestroy");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG , "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    }
  • 前台调用
    • 通过以下方式绑定服务:
    Intent intent = new Intent(this, BindService.class);
    bindService(intent,con,BIND_AUTO_CREATE);
    • 如何解绑操作呢:
    unbindService(con);
  • 其中第二个参数:
    private ServiceConnection con = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                BackService.MyBinder myBinder = (BackService.MyBinder) service;
                myBinder.showTip();
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
  • 打印如下所示
    //bindService
    2021-09-18 13:49:53.921 25941-25941/com.ycbjie.ychybrid D/BindService: onCreate
    2021-09-18 13:49:53.921 25941-25941/com.ycbjie.ychybrid D/BindService: onBind
    2021-09-18 13:49:53.927 25941-25941/com.ycbjie.ychybrid D/BindService: showTip , 我是来此服务的提示
    //unbindService
    2021-09-18 13:49:58.825 25941-25941/com.ycbjie.ychybrid D/BindService: onUnbind
    2021-09-18 13:49:58.825 25941-25941/com.ycbjie.ychybrid D/BindService: onDestroy
    //bindService
    2021-09-18 13:50:01.880 25941-25941/com.ycbjie.ychybrid D/BindService: onCreate
    2021-09-18 13:50:01.880 25941-25941/com.ycbjie.ychybrid D/BindService: onBind
    2021-09-18 13:50:01.881 25941-25941/com.ycbjie.ychybrid D/BindService: showTip , 我是来此服务的提示
    //再次点击bindService
    不打印
  • 当建立绑定后,onServiceConnected中的service便是Service类中onBind的返回值。如此便可以调用后台服务类的方法,实现交互。
  • 当调用unbindService()停止服务,同时要在onDestory()方法中做好清理工作。
  • 注意:通过bindService启动的Service的生命周期依附于启动它的Context。因此当前台调用bindService的Context销毁后,那么服务会自动停止。

4.3 混合型后台服务

  • 将上面两种启动方式结合起来就是混合性交互的后台服务了,即可以单独运行后台服务,也可以运行后台服务中提供的方法,其完整的生命周期是:onCreate->onStartCommand->onBind->onUnBind->onDestroy

4.4 前台服务

  • 所谓前台服务只不是通过一定的方式将服务所在的进程级别提升了。前台服务会一直有一个正在运行的图标在系统的状态栏显示,非常类似于通知的效果。
  • 由于后台服务优先级相对比较低,当系统出现内存不足的情况下,它就有可能会被回收掉,所以前台服务就是来弥补这个缺点的,它可以一直保持运行状态而不被系统回收。

05.详细说下绑定服务

  • 应用组件(客户端)通过调用 bindService() 绑定到服务,绑定是异步的,系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder。要接收 IBinder,客户端必须提供一个 ServiceConnection 实例用于监控与服务的连接,并将其传递给 bindService()。当 Android 系统创建了客户端与服务之间的连接时,会回调ServiceConnection 对象的**onServiceConnected()**方法,向客户端传递用来与服务通信的 IBinder
  • 多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至其他绑定的客户端。当所有客户端都取消了与服务的绑定后,系统会将服务销毁(除非 startService() 也启动了该服务)
  • 另外,只有 Activity、服务和内容提供者可以绑定到服务,无法从广播接收器绑定到服务
  • 可以通过以下三种方法定义IBinder接口:
    • 扩展 Binder 类
      • 如果服务是供本应用专用,并且运行在与客户端相同的进程中,则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Service 中可用的公共方法
    • 使用 Messenger
      • 如需让接口跨不同的进程工作,则可使用 Messenger 为服务创建接口。服务可以这种方式定义对应于不同类型 Message 对象的 Handler。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,从而让客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义自有 Messenger,以便服务回传消息。这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,这样就不必对服务进行线程安全设计
    • 使用 AIDL
      • AIDL(Android 接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。 之前采用 Messenger 的方法实际上是以 AIDL 作为其底层结构。 如上所述,Messenger 会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果想让服务同时处理多个请求,则可直接使用 AIDL。 在此情况下,服务必须具备多线程处理能力,并采用线程安全式设计。如需直接使用 AIDL,必须创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,随后可在服务内对其进行扩展

5.1 绑定服务的具体步骤

5.1.1 扩展 Binder 类
  • 如果服务仅供本地应用使用,不需要跨进程工作,则可以实现自有 Binder 类,让客户端通过该类直接访问服务中的公共方法。此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效 以下是具体的设置方法:
  • 在服务中创建一个可满足下列任一要求的 Binder 实例:
    • 包含客户端可调用的公共方法
    • 返回当前 Service 实例,其中包含客户端可调用的公共方法
    • 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法
  • 从 onBind() 回调方法返回此 Binder 实例
  • 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务
5.1.2、实现 ServiceConnection接口
  • 重写两个回调方法:
    • onServiceConnected()
      • 系统会调用该方法以传递服务的onBind() 方法返回的 IBinder
    • onServiceDisconnected()
      • Android 系统会在与服务的连接意外中断时,例如当服务崩溃或被终止时调用该方法。当客户端取消绑定时,系统不会调用该方法
  • 调用 bindService(),传递 ServiceConnection 对象
  • 当系统调用了 onServiceConnected()的回调方法时,就可以通过IBinder对象操作服务了
5.1.3、要断开与服务的连接需调用
  • unbindService()方法。如果应用在客户端仍处于绑定状态时销毁客户端,会导致客户端取消绑定,更好的做法是在客户端与服务交互完成后立即取消绑定客户端,这样可以关闭空闲服务**
  • 示例代码:
    public class MyBindService extends Service {
    
        private IBinder myBinder;
    
        private Random mGenerator;
    
        private final String TAG = "MyBindService";
    
        public class MyBinder extends Binder {
            MyBindService getService() {
                return MyBindService.this;
            }
        }
    
        @Override
        public void onCreate() {
            Log.e(TAG, "onCreate");
            myBinder = new MyBinder();
            mGenerator = new Random();
            super.onCreate();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            Log.e(TAG, "onBind");
            return myBinder;
        }
    
        @Override
        public void onDestroy() {
            Log.e(TAG, "onDestroy");
            super.onDestroy();
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.e(TAG, "onUnbind");
            return super.onUnbind(intent);
        }
    
        @Override
        public void onRebind(Intent intent) {
            Log.e(TAG, "onRebind");
            super.onRebind(intent);
        }
    
        public int getRandomNumber() {
            return mGenerator.nextInt(100);
        }
    
    }
  • 然后在看下面一部分代码
    public class BindServiceActivity extends AppCompatActivity {
    
        private MyBindService mService;
    
        private boolean mBound = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_bind_service);
        }
    
        private ServiceConnection mConnection = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                MyBindService.MyBinder binder = (MyBindService.MyBinder) service;
                mService = binder.getService();
                mBound = true;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mBound = false;
            }
        };
    
        public void bindService(View view) {
            Intent intent = new Intent(this, MyBindService.class);
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        }
    
        public void unBindService(View view) {
            if (mBound) {
                unbindService(mConnection);
                mBound = false;
            }
        }
    
        public void getData(View view) {
            if (mBound) {
                Toast.makeText(this, "获取到的随机数:" + mService.getRandomNumber(), Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "服务未绑定", Toast.LENGTH_SHORT).show();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mBound) {
                unbindService(mConnection);
                mBound = false;
            }
        }
    
    }

5.2 绑定服务的生命周期

  • 绑定服务的生命周期在同时启动服务的情况下比较特殊,想要终止服务,除了需要取消绑定服务外,还需要服务通过 stopSelf() 自行停止或其他组件调用 stopService()
  • 其中,如果服务已启动并接受绑定,则当系统调用了onUnbind() 方法,想要在客户端下一次绑定到服务时调用 onRebind() 方法的话,则onUnbind() 方法需返回 true。onRebind() 返回空值,但客户端仍可以在其 onServiceConnected() 回调中接收到 IBinder对象
    • 这里写图片描述
      这里写图片描述

5.3 绑定时机

  • 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定
  • 如果希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。这意味着 Activity 在其整个运行过程中(包括后台运行期间)都需要使用此服务
  • 通常情况下,切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,应该使发生在这些转换期间的处理保持在最低水平。假设有两个Activity需要绑定到同一服务,从Activity A跳转到Activity B,这个过程中会依次执行A-onPause,B-onCreate,B-onStart,B-onResume,A-onStop。这样系统会在A-onPause的时候销毁服务,又在B-onResume的时候重建服务。当Activity B回退到Activity A时,会依次执行B-onPause,A-onRestart,A-onStart,A-onResume,B-onStop,B-onDestroy。此时,系统会在B-onPause时销毁服务,又在A-onResume时重建服务。这样就造成了多次的销毁与重建,因此需要选定好绑定服务与取消绑定服务的时机
贡献者: yangchong211
上一篇
2.4Activity布局绘制
下一篇
2.6Service启动流程