一句话总结:
服务(Service)启动就像雇人干活------要么长期包养(startService),要么临时雇佣随叫随到(bindService),通信全靠"传纸条"(Binder、广播等)!
一、服务的启动方式(雇人干活的两副面孔)
1. startService()
------ 长期包养型
-
作用: 让服务在后台长期运行(即使Activity销毁了,服务还在!)
-
场景: 音乐播放、下载文件等需要持续干活的任务。
-
代码:
scss// Activity中启动 val intent = Intent(this, MyService::class.java) startService(intent) // 停止服务(在合适的时候调用) stopService(intent)
-
生命周期:
onCreate()
→onStartCommand()
→ 运行 →onDestroy()
-
比喻:
雇个工人(Service)去搬砖,搬完别停,随时待命!
2. bindService()
------ 临时雇佣型
-
作用: 绑定服务后可以和它"直接对话",Activity销毁时服务可能被回收。
-
场景: 需要和服务频繁交互,比如控制音乐播放/暂停。
-
代码:
kotlin// Activity中绑定服务 val connection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { // 获取服务的Binder对象,用来通信 val myBinder = binder as MyService.LocalBinder myService = myBinder.getService() } override fun onServiceDisconnected(name: ComponentName?) {} } val intent = Intent(this, MyService::class.java) bindService(intent, connection, Context.BIND_AUTO_CREATE) // 解绑服务(通常在onDestroy中调用) unbindService(connection)
-
生命周期:
onCreate()
→onBind()
→ 运行 →onUnbind()
→onDestroy()
-
比喻:
临时雇个秘书(Service),需要时喊TA泡咖啡,用完就拜拜!
二、Service和Activity通信(如何传纸条?)
方式1:通过Binder(直接对话,绑定服务时用)
-
步骤:
-
Service中定义Binder:
kotlinclass MyService : Service() { private val binder = LocalBinder() inner class LocalBinder : Binder() { fun getService() = this@MyService // 返回Service实例 } override fun onBind(intent: Intent): IBinder = binder }
-
Activity中调用Service方法:
scss// 绑定成功后,通过myService直接调用方法 myService.playMusic() myService.pauseMusic()
-
-
优点: 高效直接,适合频繁交互。
-
缺点: 必须绑定服务,且需手动解绑防内存泄漏!
方式2:通过广播(跨组件通信)
-
场景: Activity和服务不需要绑定,偶尔发个通知。
-
代码:
kotlin// Service中发送广播 val intent = Intent("ACTION_UPDATE_UI") intent.putExtra("data", "下载完成!") sendBroadcast(intent) // Activity中注册接收 private val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { textView.text = intent?.getStringExtra("data") } } override fun onResume() { super.onResume() registerReceiver(receiver, IntentFilter("ACTION_UPDATE_UI")) } override fun onPause() { super.onPause() unregisterReceiver(receiver) // 必须解注册! }
-
优点: 松耦合,任意组件都能接收。
-
缺点: 效率低,容易滥用导致代码混乱。
方式3:通过LiveData(MVVM推荐)
-
场景: 结合ViewModel,数据驱动UI更新。
-
代码:
kotlin// ViewModel中 class MyViewModel : ViewModel() { val progress = MutableLiveData<Int>() } // Service中(需能访问ViewModel) viewModel.progress.postValue(50) // 更新进度 // Activity中观察 viewModel.progress.observe(this) { progress -> progressBar.progress = progress }
-
优点: 生命周期安全,自动更新UI。
-
注意: 需要确保Service能获取到ViewModel实例(可通过依赖注入)。
三、Service和Service通信(同事之间怎么聊?)
方式1:Binder(同一进程内)
- 步骤: 和Activity通信类似,一个Service绑定另一个Service的Binder。
- 代码: 参考Activity的Binder方式,但需注意服务绑定服务的生命周期。
方式2:Messenger(跨进程安全)
-
场景: 服务在不同进程(如远程服务)。
-
代码:
ini// ServiceA 发送消息给ServiceB val intent = Intent(this, ServiceB::class.java) bindService(intent, connection, Context.BIND_AUTO_CREATE) // 绑定成功后,通过Messenger发送消息 val message = Message.obtain() message.what = 1 message.obj = "Hello from ServiceA" serviceBMessenger.send(message)
方式3:AIDL(高级跨进程通信)
-
场景: 需要复杂接口调用的跨进程服务(如系统服务)。
-
步骤:
- 定义AIDL接口文件。
- 实现接口并在Service的onBind()中返回。
- 客户端绑定服务后通过Stub调用方法。
-
比喻: 两个服务用"加密电报"交流,需要提前约定暗号(接口)!
四、终极总结表
场景 | 推荐方式 | 优点 | 缺点 |
---|---|---|---|
长期后台任务 | startService() | 独立运行,不依赖Activity | 不能直接通信 |
频繁交互控制 | bindService() + Binder | 高效直接 | 需手动解绑 |
跨组件通知 | 广播 | 松耦合,灵活 | 效率低,易乱 |
数据驱动UI更新 | LiveData + ViewModel | 生命周期安全,自动更新 | 需要架构支持 |
跨进程服务通信 | AIDL/Messenger | 支持复杂操作,跨进程安全 | 代码复杂,需要定义接口 |
避坑指南:
- 内存泄漏: 绑定服务后一定要在
onDestroy()
解绑! - ANR: Service主线程不能干重活,耗时操作开子线程!
- 跨进程: 优先用Messenger,AIDL是最后的选择!
口诀:
"启动服务分两种,长期临时看需求;
Binder直接效率高,广播LiveData解烦忧;
跨进程用Messenger,AIDL复杂在后头!