Котлин: завернутый в ссылочный объект, который будет изменен при захвате в замыкании

Я реализую Firebase на android с помощью kotlin.

поскольку FirebaseInstanceId.getInstance().token изношен, я использую следующую функцию внутри сопутствующего объекта моего класса MyFirebaseMessageService, чтобы получить токен:

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 выделяет s как Wrapped into a reference object to be modified when captured in a closure

После выполнения grabFcmToken() первый журнал успешно распечатывает токен, но второй журнал имеет java.lang.NullPointerException: println needs a message, указывающий, что s имеет значение null и, следовательно, его значение не изменилось.

как я могу изменить s так, чтобы в нем отражался захваченный токен?

Вы также можете проверить это.

Alex Mamo 10.01.2019 08:41
2
1
5 038
2

Ответы 2

Дело в том, что первый журнал находится внутри слушателя для асинхронный. задача. Вероятно, это будет примерно так:

  1. Позвоните в grabFcmToken()
  2. FirebaseInstanceId.getInstance().instanceId называется. Здесь начинается задача получения instanceId. Итак, вы также добавляете слушателя. Поскольку это асинхронная задача, коду не нужно ждать, чтобы он продолжил работу.
  3. Log.i("To be returned token ", s) работает там, где s - это null. NPE здесь.

Я бы предложил изменить функцию, чтобы вместо этого возвращать задачу или иметь параметр слушателя (первый кажется лучше). Вот так:

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

Затем вызовите его из нужного вам класса:

class SampleActivity: AppCompatActivity(){

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

}

Так. После того, как я оставил этот пост, у меня было какое-то прозрение. Текущий ответ на самом деле не помог, но что-то щелкнуло.

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
  }
}

Третий набор супер-возбужденных !!!! - важный набор. Поскольку мы никогда не обновляем состояние s где-либо еще, мы сталкиваемся с этой проблемой. Я говорю «проблема», потому что ненавижу, когда моя IDE говорит мне, что что-то не так. Итак, я хочу знать, почему он так думает. Я почти полностью согласен с этим. НО. В вашем случае нет простого способа изменить этот доход. Я не на 1000% вы пошли так, как я бы поступил с этим, но в то же время похоже, что это работает.

В целом суть в том, что если вместо этого вы можете сделать что-то в этом роде:

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
}

незначительное предупреждение исчезнет.

Другие вопросы по теме