nous avons reçu un crash sur Firebase pour une méthode kotlin :
Fatal Exception: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter code
at [redacted].DeliveryMethod.<init>(:2)
at [redacted].DeliveryMethodsUpdater$addSingleDMInAd$clientCall$1.invokeSuspend(DeliveryMethodsUpdater.kt:121)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
le modèle est celui-ci :
class DeliveryMethod() {
lateinit var code: String
lateinit var name: String
lateinit var description: String
var isAddressRequired: Boolean? = null
var image: JSONObject? = null
var isDefault: Boolean = false
constructor(code: String) : this() {
this.code = code
}
constructor(code: String, name: String, description: String, image: JSONObject? = null) : this() {
this.code = code
this.name = name
this.description = description
this.image = image
}
}
et la méthode :
private suspend fun addSingleDMInAd(
adId: Long,
deliveryMethodCode: String
): JoinAdDeliveryMethod? {
var addedDeliveryMethod: JoinAdDeliveryMethod? = null
val clientCall = GlobalScope.async(Dispatchers.IO) {
val cd = CountDownLatch(1)
Client.getInstance().addDeliveryMethodInAd(
adId,
DeliveryMethod(deliveryMethodCode),
object : NetworkCallback<JoinAdDeliveryMethod> {
override fun onSuccess(result: JoinAdDeliveryMethod) {
addedDeliveryMethod = result
cd.countDown()
}
override fun onFailure(err: NetworkError?) {
addedDeliveryMethod = null
cd.countDown()
}
}
)
cd.await()
}
clientCall.await()
return addedDeliveryMethod
}
maintenant, je comprends que le constructeur pour DeliveryMethod
est appelé avec une null
valeur pour code
, mais je ne comprends pas pourquoi l'exception n'apparaît qu'à ce stade. Comme vous pouvez le voir, le paramètre de méthode est également marqué comme non nul, tout comme les méthodes précédentes. L'exception ne devrait-elle pas être levée bien avant d'arriver à l'appel du constructeur pour DeliveryMethod
?
L'exception ne devrait-elle pas être levée bien avant d'arriver à l'appel du constructeur pour DeliveryMethod ?
Dans Kotlin, il n'est pas possible qu'un paramètre non nul reçoive une valeur nulle au moment de l'exécution (car le code n'aurait pas été compilé en premier lieu). Cependant, cela peut se produire si la valeur est transmise depuis Java.
C'est pourquoi le compilateur Kotlin génère des vérifications nulles (avec l'échec intrinsèque checkNotNullParameter
que vous voyez ici) uniquement dans les méthodes publiques/protégées/internes non suspendues , pour éviter toute utilisation abusive de Java. Il est inutile de le faire dans les méthodes privées ou suspendues, car elles ne peuvent être appelées que depuis Kotlin (généralement), et cela ajouterait une surcharge qui pourrait ne pas être acceptable dans un code sensible aux performances.
C'est pourquoi l'appel addSingleDMInAd
n'échoue pas avec cette erreur. Cela dit, il serait intéressant de voir comment vous obtenez la valeur nulle ici, car généralement les vérifications à la surface de l'API publique sont suffisantes. Y a-t-il une réflexion ou un casting dangereux impliqué ici ?
De plus, la façon dont votre modèle est configuré est assez étrange. Il semble que le lateinit
mente car selon le constructeur utilisé, les propriétés peuvent en fait ne pas être définies du tout. Il serait plus sûr de les marquer comme nullables pour tenir compte du moment où les utilisateurs de cette classe ne définissent pas la valeur de ces propriétés. En faisant cela, vous n'aurez même pas besoin de tous les constructeurs secondaires, et vous pouvez simplement utiliser les valeurs par défaut :
class DeliveryMethod() {
var code: String? = null,
var name: String? = null,
var description: String? = null,
var image: JSONObject? = null,
) {
var isAddressRequired: Boolean? = null
var isDefault: Boolean = false
}
Autres choses à noter sur addSingleDMInAd
:
ne pas utiliser GlobalScope
dans ce cas. Si vous devez exécuter des coroutines de courte durée, fournissez-leur une portée plus petite qui est annulée lorsque le travail n'est plus nécessaire - cela garantit qu'aucune coroutine ne fuit. Vous pouvez en savoir plus sur les pièges potentiels GlobalScope
et les remplacements possibles dans son propre doc . Cela dit, vous ne devriez probablement pas du tout démarrer une coroutine ici, voir le point suivant.
n'utilisez pas async {}
si vous utilisez await()
juste après - il est inutile de démarrer quelque chose d'asynchrone si vous l'attendez juste là. Si vous souhaitez basculer le contexte sur IO, utilisez à la withContext(Dispatchers.IO) { ... }
place. Cela dit, vous ne devriez même pas avoir besoin d'utiliser le répartiteur IO ici, voir le point suivant.
ne pas utiliser CountDownLatch
à cette fin. La bonne façon d'encapsuler une API asynchrone en tant que suspend
fonction pour les coroutines est d'utiliser suspendCancellableCoroutine (vérifiez sa doc, elle fournit un exemple sur la façon de l'utiliser). Une fois que vous l'utilisez, il n'y a plus besoin de Dispatchers.IO
car il ne bloquera plus le thread actuel.
Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.
En cas d'infraction, veuillez [email protected] Supprimer.
laisse moi dire quelques mots