java.lang.VerifyError: .../utils/KotlinViewExtKt$animateFadeOut$1
Getting that error while running app on emulator PRE Lolipop (<21 api)
Function causing the trouble:
fun View.animateFadeOut(animDuration: Long = 250) {
this.animate()
.alpha(0F)
.setDuration(animDuration)
.setListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(p0: Animator?) {}
override fun onAnimationEnd(animation: Animator?, isReverse: Boolean) {
super.onAnimationEnd(animation, isReverse)
show(false)
}
override fun onAnimationEnd(p0: Animator?) {
show(false)
}
override fun onAnimationCancel(p0: Animator?) {
}
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
}
override fun onAnimationStart(p0: Animator?) {
}
})
.start()
}
fun View.show(show: Boolean) {
val vis = if (show) View.VISIBLE else View.GONE
if (visibility != vis) {
visibility = vis
}
}
Pointing on .setListener
line.
Works perfectly on 21+ api.
AS versio: 3.0.1. Kotlin version: 1.2.21 (tried 1.1.51).
What could be the reason? My bad or kotlin? Multidex is enabled.
Solution
Based on this issue and this fix:
ViewExtension.kt
fun View.animateFadeOut(animDuration: Long = 250L) {
this.animate()
.alpha(0F)
.setDuration(animDuration)
.setListener(object : AbstractAnimatorListener() {
override fun onAnimationEnd(animation: Animator?, isReverse: Boolean) {
super.onAnimationEnd(animation, isReverse)
show(false)
}
override fun onAnimationEnd(p0: Animator?) {
show(false)
}
})
.start()
}
fun View.show(show: Boolean) {
val vis = if (show) View.VISIBLE else View.GONE
if (visibility != vis) {
visibility = vis
}
}
AbstractAnimatorListener.kt
abstract class AbstractAnimatorListener : Animator.AnimatorListener {
override fun onAnimationRepeat(p0: Animator?) {}
override fun onAnimationEnd(p0: Animator?) {}
override fun onAnimationCancel(p0: Animator?) {}
override fun onAnimationStart(p0: Animator?) {}
override fun onAnimationEnd(animation: Animator?, isReverse: Boolean) {
onAnimationEnd(animation)
}
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
onAnimationStart(animation)
}
}
Explanation
Try to remove these methods added in API 26 using Java 8 Defaults and use alternative animation:
onAnimationEnd added in API level 26
void onAnimationEnd (Animator animation, boolean isReverse)
onAnimationStart added in API level 26
void onAnimationStart (Animator animation, boolean isReverse)
These methods can be overridden, though not required, to get the additional play direction info when an animation starts.
Starting in Android 8.0 (API 26) the AnimatorSet API supports seeking and playing in reverse.
Note:
/**
* Skipping calling super when overriding this method results in
* {@link #onAnimationStart(Animator)} not getting called.
*/
default void onAnimationStart(Animator animation, boolean isReverse) {
onAnimationStart(animation);
}
I need to add this to my build.gradle
file to test it
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = "1.8"
}
}
or replace
super.onAnimationEnd(animation, isReverse)
by
onAnimationEnd(animation)
to avoid the error:
Super calls to Java default methods are prohibited in JVM target 1.6. Recompile with '-jvm-target 1.8'
And add the next line to my gradle.properties file thanks to this answer
android.enableD8=true
to avoid the exception:
com.android.dx.cf.code.SimException: default or static interface method used without --min-sdk-version >= 24
It compiles now, launching a kitKat emulator just now...
It works, and MultiDex is also enabled in my project.
Sorry, I cannot reproduce it.
Alternatively, remove the two methods using Java 8 defaults, it also works.
Note2:
Arpan suggestion about AnimatorListenerAdapter would work but it's not necessary.
You can remove those methods, change the animation and create:
object EmptyAnimatorListener : Animator.AnimatorListener {
override fun onAnimationRepeat(p0: Animator?) {}
override fun onAnimationEnd(p0: Animator?) {}
override fun onAnimationCancel(p0: Animator?) {}
override fun onAnimationStart(p0: Animator?) {}
}
And use delegation like this:
.setListener(object : Animator.AnimatorListener by EmptyAnimatorListener {
override fun onAnimationStart(animation: Animator) {
// Do something
}
override fun onAnimationEnd(animation: Animator) {
// Do something
}
})
Note3:
Reproduced adding it to an extension function:
E/AndroidRuntime: FATAL EXCEPTION: main Process: ...android, PID: 3409 java.lang.VerifyError: com/.../ui/common/extension/android/view/ViewExtensionKt$animateFadeOut$1
Removing the Api 26 methods using Java Defaults solves the issue.
Replacing the super call calling your onAnimationEnd also solves it:
override fun onAnimationEnd(animation: Animator?, isReverse: Boolean) {
onAnimationEnd(animation)
}
Calling super.onAnimationStart(animation: Animator?, isReverse: Boolean) requires API 26: