Kotlin :Wrapped into a reference object to be modified when captured in a closure

Viewed 6004

I am implementing Firebase on android using kotlin.

as the FirebaseInstanceId.getInstance().token is depriciated, I am using the following function inside the companion object of my MyFirebaseMessageService class to get the token:

companion object
{
  fun grabFcmToken():String?
  {
    var s:String?=null
    FirebaseInstanceId.getInstance().
    instanceId.addOnCompleteListener {task ->
    if (task.isSuccessful)
     {
        s=task.result!!.token
        Log.i("token used is ", s)
     }
     else
      {
        throw Exception("Can't get firebase token")
      } 
    }
    Log.i("To be returned token ", s)
    return s
  }
}

Android Studio highlights s as Wrapped into a reference object to be modified when captured in a closure

Upon the execution of grabFcmToken() the first log prints the token successfully, but the second log has a java.lang.NullPointerException: println needs a message indicating that s has the value null and hence it's value is unchanged.

how may i modify s such that the captured token is being reflected in it?

2 Answers

The thing here is that, the first log is inside a listener for an asynchronous. task. So it's probably going like this:

  1. Call grabFcmToken()
  2. FirebaseInstanceId.getInstance().instanceId is called. Here, the task to get the instanceId starts. So you also put in a listener. Since this is an asynchronous task, the code doesn't need to wait so it proceeds.
  3. Log.i("To be returned token ", s) runs where s is null. NPE here.

I would suggest altering the function to instead return the task or have a listener parameter (first one seems better). Like so:

companion object {
        fun requestPushToken(): Task<InstanceIdResult> {
            return FirebaseInstanceId.getInstance().instanceId
        }
}

Then call it from whichever class you need:

class SampleActivity: AppCompatActivity(){

    YourAppService.requestPushToken().addCompleteListener { task ->
        if (task.isSuccessful){
            // TODO: Handle
        } else {
            // TODO: Handle
        }
    }

}

So. I had some sort of epiphany after leaving this post. The current answer didn't actually help, but something clicked.

companion object {
  fun grabFcmToken():String? {
    var s:String? = null // !!!!! s is defined here

    FirebaseInstanceId.getInstance().
      instanceId.addOnCompleteListener {task ->
        if (task.isSuccessful) {
          s = task.result!!.token // !!!!!! s is redefined inside here

          Log.i("token used is ", s)
        } else {
          throw Exception("Can't get firebase token")
        } 
      }

    Log.i("To be returned token ", s)

    // !!!!!! s is never changed after the redefine
    return s
  }
}

The third set of super excited !!!! is the important set. Because we never update the state of s anywhere else we get this issue. I say issue because I hate when my IDE tells me something isn't right. So I want to know why it thinks that. I almost fully agree with this. BUT. In your case there isn't a real simple way to change this return. I'm not 1000% you went the way I would have with this, but at the same time it looks like it works.

Overall the point is that if you can instead do something along these lines::

instanceId.addOnCompleteListener { task ->
  val newS = "s"
  if(task.isSuccessful) {
    newS = task.result!!.token // this might give errors, but I think you get the point
  }

  return newS
}

the insignificant warning will disappear.

Related