Android随笔-startServicebindService

发布时间:2026/7/17 0:29:27
Android随笔-startServicebindService 一、核心区别对比特性startService()bindService()启动方式直接启动无需组件绑定需要与组件Activity/Fragment绑定生命周期onCreate()→onStartCommand()onCreate()→onBind()与调用者关系调用者与 Service无直接关联调用者与 Service强绑定Service 存活调用者销毁后Service 仍可独立运行所有绑定者解绑后Service 自动销毁通信方式单向Intent 传入无直接回调双向通过IBinder接口直接通信典型用途后台长期任务音乐播放、文件下载需要与 UI 交互的后台服务音乐控制、定位多次调用每次调用都触发onStartCommand()首次绑定触发onBind()后续复用同一连接二、生命周期对比startService 生命周期调用 startService() ↓ onCreate() [仅首次] ↓ onStartCommand() [每次 startService() 都会触发] ↓ ... 服务运行中 ... ↓ 调用 stopService() 或 stopSelf() ↓ onDestroy()bindService 生命周期调用 bindService() ↓ onCreate() [仅首次] ↓ onBind() [返回 IBinder仅首次绑定触发] ↓ ServiceConnection.onServiceConnected() [回调给调用者] ↓ ... 服务运行中可双向通信 ... ↓ 所有绑定者调用 unbindService() ↓ onUnbind() [最后一个解绑时触发] ↓ onDestroy()混合模式同时使用两者startService() bindService() ↓ onCreate() → onStartCommand() → onBind() ↓ ... 运行中 ... ↓ 所有 bind 解绑 stopService()/stopSelf() ↓ onUnbind() → onDestroy()关键规则混合模式下Service 必须同时满足两个条件才会销毁没有startService()启动或已调用stopSelf()/stopService()没有活跃的绑定连接三、代码示例1. startService 示例// Service 端publicclassMusicServiceextendsService{OverridepublicvoidonCreate(){super.onCreate();Log.d(MusicService,onCreate);}OverridepublicintonStartCommand(Intentintent,intflags,intstartId){Stringactionintent.getStringExtra(action);if(play.equals(action)){startMusic();}elseif(stop.equals(action)){stopMusic();}// START_STICKY: 被杀死后系统会自动重启returnSTART_STICKY;}OverridepublicvoidonDestroy(){super.onDestroy();stopMusic();Log.d(MusicService,onDestroy);}OverridepublicIBinderonBind(Intentintent){returnnull;// startService 不需要 Binder}privatevoidstartMusic(){/* ... */}privatevoidstopMusic(){/* ... */}}// Activity 端启动publicclassMainActivityextendsAppCompatActivity{publicvoidstartMusic(){IntentintentnewIntent(this,MusicService.class);intent.putExtra(action,play);startService(intent);}publicvoidstopMusic(){IntentintentnewIntent(this,MusicService.class);stopService(intent);}}2. bindService 示例// Service 端提供 Binder 接口publicclassMusicServiceextendsService{privatefinalIBinderbindernewMusicBinder();privatebooleanisPlayingfalse;// 定义通信接口publicclassMusicBinderextendsBinder{publicMusicServicegetService(){returnMusicService.this;}}// 暴露给外部的方法publicvoidplay(){isPlayingtrue;// 播放逻辑}publicvoidpause(){isPlayingfalse;// 暂停逻辑}publicbooleanisPlaying(){returnisPlaying;}OverridepublicIBinderonBind(Intentintent){returnbinder;}}// Activity 端绑定publicclassMainActivityextendsAppCompatActivity{privateMusicServicemusicService;privatebooleanisBoundfalse;privateServiceConnectionconnectionnewServiceConnection(){OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){MusicService.MusicBinderbinder(MusicService.MusicBinder)service;musicServicebinder.getService();isBoundtrue;// 绑定成功可以调用 Service 方法updateUI();}OverridepublicvoidonServiceDisconnected(ComponentNamename){isBoundfalse;musicServicenull;}};OverrideprotectedvoidonStart(){super.onStart();IntentintentnewIntent(this,MusicService.class);bindService(intent,connection,Context.BIND_AUTO_CREATE);}OverrideprotectedvoidonStop(){super.onStop();if(isBound){unbindService(connection);isBoundfalse;}}// 通过 Service 控制音乐publicvoidonPlayClick(Viewv){if(isBoundmusicService!null){musicService.play();updateUI();}}privatevoidupdateUI(){if(musicService!null){booleanplayingmusicService.isPlaying();// 更新按钮状态}}}3. 混合模式示例最常用// 先 startService 保证长期存活再 bindService 实现交互publicclassMainActivityextendsAppCompatActivity{OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);// 第一步startService 保证 Service 不被系统轻易杀死IntentintentnewIntent(this,MusicService.class);startService(intent);// 第二步bindService 实现双向通信bindService(intent,connection,Context.BIND_AUTO_CREATE);}OverrideprotectedvoidonDestroy(){super.onDestroy();// 只解绑不 stopService让音乐继续后台播放if(isBound){unbindService(connection);}}// 真正退出应用时才 stopServicepublicvoidexitApp(){IntentintentnewIntent(this,MusicService.class);stopService(intent);}}四、日常使用场景场景推荐方式原因后台音乐播放startServicebindService需要长期运行 需要 UI 控制文件下载startService不需要 UI 交互完成后自动停止定位服务bindService需要实时获取位置数据更新 UI计步器startService长期后台运行不需要频繁交互视频播放器后台播放混合模式切后台继续播放返回前台可控制AIDL 跨进程通信bindService必须通过 Binder 实现 IPC五、注意事项1. 内存泄漏最常见// ❌ 错误匿名内部类持有 Activity 引用publicclassMainActivityextendsAppCompatActivity{privateServiceConnectionconnectionnewServiceConnection(){OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){// 这里隐式持有 MainActivity 引用// 如果 Service 生命周期比 Activity 长会造成内存泄漏}};}// ✅ 正确使用静态内部类 WeakReferencepublicclassMainActivityextendsAppCompatActivity{privatestaticclassMyServiceConnectionimplementsServiceConnection{privateWeakReferenceMainActivityactivityRef;MyServiceConnection(MainActivityactivity){this.activityRefnewWeakReference(activity);}OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){MainActivityactivityactivityRef.get();if(activity!null){// 安全使用}}OverridepublicvoidonServiceDisconnected(ComponentNamename){}}}2. 重复解绑崩溃// ❌ 错误多次调用 unbindService 会抛 IllegalArgumentExceptionOverrideprotectedvoidonDestroy(){super.onDestroy();unbindService(connection);// 如果已经解绑过这里崩溃}// ✅ 正确用标志位控制OverrideprotectedvoidonDestroy(){super.onDestroy();if(isBound){unbindService(connection);isBoundfalse;}}3. bindService 后忘记解绑// ❌ 错误在 onDestroy 中不解绑OverrideprotectedvoidonDestroy(){super.onDestroy();// 漏了 unbindService}绑定后不解绑会导致Service 无法销毁占用内存Activity 泄漏ServiceConnection 持有 Activity 引用应用退出时可能报ServiceConnectionLeaked警告4. 混合模式下的生命周期陷阱// 陷阱先 bind 后 start解绑后 Service 不会自动销毁// 因为 startService 启动了它需要显式 stopService// 正确顺序// 1. startService() → 启动// 2. bindService() → 绑定// 3. unbindService() → 解绑Service 继续运行// 4. stopService() → 真正停止5. 主线程耗时操作// ❌ 错误在 Service 主线程做耗时操作OverridepublicintonStartCommand(Intentintent,intflags,intstartId){downloadLargeFile();// ANRreturnSTART_STICKY;}// ✅ 正确使用子线程或 IntentService/WorkManagerOverridepublicintonStartCommand(Intentintent,intflags,intstartId){newThread(()-downloadLargeFile()).start();returnSTART_STICKY;}6. Android 8.0 后台限制从 Android 8.0API 26开始后台应用调用startService()会受到限制// Android 8.0 后台启动 Service 会抛 IllegalStateException// 解决方案使用 startForegroundService()if(Build.VERSION.SDK_INTBuild.VERSION_CODES.O){startForegroundService(intent);}else{startService(intent);}// 然后在 Service 的 onCreate() 中必须在 5 秒内调用 startForeground()OverridepublicvoidonCreate(){super.onCreate();NotificationnotificationbuildNotification();startForeground(NOTIFICATION_ID,notification);}7. onStartCommand 返回值选择OverridepublicintonStartCommand(Intentintent,intflags,intstartId){// 根据场景选择返回值// START_STICKY服务被杀后自动重启intent 为 null音乐播放// START_NOT_STICKY服务被杀后不自动重启一次性任务// START_REDELIVER_INTENT服务被杀后自动重启且重新传入原 intent下载任务returnSTART_STICKY;}六、总结问题答案需要后台长期运行且不与 UI 交互用startService()需要与 Activity 实时交互用bindService()既要后台运行又要 UI 控制混合模式先startService()再bindService()混合模式下如何正确销毁先unbindService()再stopService()Android 8.0 后台启动限制用startForegroundService()startForeground()核心原则startService管活多久bindService管怎么交互。两者不是互斥的而是互补的混合使用是最常见的实战模式。