如何等待服务加载完成后启动Activity?监听服务状态最优方案
嘿,这个问题我刚好有不少实践经验,忙等待肯定是要避免的——既耗CPU又容易出问题。结合你的需求(Activity启动服务、显示启动页直到服务就绪后切Fragment),我给你推荐几个靠谱的实现方案,按推荐程度排序:
首选方案:用LiveData实现生命周期感知的状态监听
LiveData是Android Jetpack里的组件,天生支持生命周期感知,完美适配Activity和Service的状态同步,还能自动避免内存泄漏,代码也最简洁。
步骤1:在Service中定义静态LiveData
把服务的就绪状态封装成MutableLiveData,库加载完成后更新它的值:
class MyLibraryService : Service() { // 用静态MutableLiveData暴露就绪状态,初始值为false companion object { val isLibraryReady = MutableLiveData<Boolean>(false) } override fun onCreate() { super.onCreate() // 模拟库加载过程(4-5秒) Handler(Looper.getMainLooper()).postDelayed({ // 加载完成后,更新LiveData的值 isLibraryReady.postValue(true) }, 4500) } // 实现Service的其他必要方法(比如onBind) override fun onBind(intent: Intent): IBinder? { return null } }
步骤2:在MainActivity中监听LiveData
在Activity的onResume里显示启动页,然后观察LiveData的变化,一旦状态变为true就切换Fragment:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 启动服务 startService(Intent(this, MyLibraryService::class.java)) } override fun onResume() { super.onResume() // 先显示启动页Fragment supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, SplashFragment()) .commit() // 监听服务的就绪状态 MyLibraryService.isLibraryReady.observe(this) { ready -> if (ready) { // 切换到目标Fragment supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, MainContentFragment()) .commit() // 移除观察者,避免后续重复触发 MyLibraryService.isLibraryReady.removeObservers(this) } } } }
为什么选这个?
- LiveData会自动感知Activity的生命周期,当Activity销毁时自动取消观察,不会内存泄漏
- 不需要手动管理注册/注销逻辑,代码简洁
- 即使Activity在后台时状态更新,回到前台也能收到最新值
备选方案:用BroadcastReceiver跨组件通知
如果你的Service和Activity可能处于不同进程(不过看你用静态布尔值,应该是同进程),或者需要通知多个组件,广播接收器是个不错的选择。
步骤1:Service加载完成后发送广播
class MyLibraryService : Service() { // 定义广播action常量 companion object { const val ACTION_LIBRARY_READY = "com.yourpackage.ACTION_LIBRARY_READY" } override fun onCreate() { super.onCreate() Handler(Looper.getMainLooper()).postDelayed({ // 发送广播通知Activity sendBroadcast(Intent(ACTION_LIBRARY_READY)) }, 4500) } override fun onBind(intent: Intent): IBinder? { return null } }
步骤2:Activity中动态注册广播接收器
记得在onResume注册,onPause注销,避免内存泄漏:
class MainActivity : AppCompatActivity() { private lateinit var readyReceiver: BroadcastReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) startService(Intent(this, MyLibraryService::class.java)) // 初始化广播接收器 readyReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == MyLibraryService.ACTION_LIBRARY_READY) { // 切换到目标Fragment supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, MainContentFragment()) .commit() } } } } override fun onResume() { super.onResume() supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, SplashFragment()) .commit() // 注册广播接收器 registerReceiver(readyReceiver, IntentFilter(MyLibraryService.ACTION_LIBRARY_READY)) } override fun onPause() { super.onPause() // 注销广播接收器,避免内存泄漏 unregisterReceiver(readyReceiver) } }
注意点:动态注册的广播必须在合适的生命周期注销,否则会导致Context泄漏。
进阶方案:用Binder绑定Service实现回调
如果你的Activity需要和Service有更多交互(不止监听一个状态),可以用Binder绑定Service,直接获取Service实例并设置回调。
步骤1:Service实现Binder并提供回调接口
class MyLibraryService : Service() { private val binder = LocalBinder() var isLibraryReady = false private var onReadyCallback: (() -> Unit)? = null // 内部Binder类,让Activity获取Service实例 inner class LocalBinder : Binder() { fun getService(): MyLibraryService = this@MyLibraryService } override fun onCreate() { super.onCreate() Handler(Looper.getMainLooper()).postDelayed({ isLibraryReady = true // 触发回调通知Activity onReadyCallback?.invoke() }, 4500) } // 设置就绪回调 fun setOnReadyCallback(callback: () -> Unit) { onReadyCallback = callback } override fun onBind(intent: Intent): IBinder { return binder } }
步骤2:Activity绑定Service并设置回调
class MainActivity : AppCompatActivity() { private var myService: MyLibraryService? = null private var isServiceBound = false private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = service as MyLibraryService.LocalBinder myService = binder.getService() isServiceBound = true // 检查是否已经就绪,避免错过状态更新 if (myService?.isLibraryReady == true) { switchToMainFragment() } else { // 设置回调 myService?.setOnReadyCallback { switchToMainFragment() } } } override fun onServiceDisconnected(arg0: ComponentName) { isServiceBound = false myService = null } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) startService(Intent(this, MyLibraryService::class.java)) // 绑定Service bindService(Intent(this, MyLibraryService::class.java), serviceConnection, Context.BIND_AUTO_CREATE) } override fun onResume() { super.onResume() supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, SplashFragment()) .commit() } private fun switchToMainFragment() { supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, MainContentFragment()) .commit() // 如果不需要保持绑定,可以解绑 if (isServiceBound) { unbindService(serviceConnection) isServiceBound = false } } override fun onDestroy() { super.onDestroy() // 销毁时解绑Service,避免泄漏 if (isServiceBound) { unbindService(serviceConnection) isServiceBound = false } } }
适用场景:当Activity需要调用Service的其他方法,或者需要更紧密的交互时使用,缺点是代码量稍大,需要管理绑定和解绑逻辑。
内容的提问来源于stack exchange,提问作者Toan Le
相关产品推荐
相关产品推荐

