Search code examples
androidandroid-fragmentskotlinandroid-lifecycle

How to access a service from a fragment, via its activity?


I have an activity that, as far as I can tell, is happily binding to a service every time the activity is created. The first time the activity is created, it also starts the service with the startService command, as follows:

    private fun attachRecorderService() {
       val intent = Intent(this, AudioRecorderService::class.java)
       bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
       if (!mBooleanRecorderServiceStarted) {
           startService(intent)
           mBooleanRecorderServiceStarted = true
       }
    }

That activity's fragments all get a reference to that service in their onActivityCreated() function as follows:

    override fun onActivityCreated(savedInstanceState: Bundle?) {
       super.onActivityCreated(savedInstanceState)
       val parentActivity = activity as MainActivity
       mAudioRecorderService = parentActivity.mAudioRecorderService

That works fine the first time the fragment is created, but as soon as the screen is rotated I get an error telling me the service hasn't been initialised in the activity.

lateinit property mAudioRecorderService has not been initialized

As far as I can tell, onActivityCreated() in the fragment is racing with onCreate() in the activity, and trying to get the reference before onCreate() initialises it.

Which I don't understand. I thought onActivityCreated() waited until after onCreate() had completed.

What am I doing wrong? Should I use some sort of callback in the fragment, that only triggers when the activity has bound to the service? I've seen mention of that, but I have no idea how to do it.

The question Communication between Activity and Service deals with Activities and Services. I'm asking about the Fragments that are attached to the Activity, and how they can access a service that the Activity has already bound to.


Solution

  • The service is not available directly after calling bindService. Use a ServiceConnection. When onServiceConnected is called the service is ready to use

    private val connection = object : ServiceConnection {
        override fun onServiceDisconnected(p0: ComponentName?) {
    
        }
    
        override fun onServiceConnected(p0: ComponentName, binder: IBinder) {
    
        }
    }
    
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        application.bindService(Intent(application, MyService::class.java), connection, Context.BIND_AUTO_CREATE)
    }