Using default method raises AbstractMethorError in Android release build
Asked Answered
B

2

9

I have an interface that inherits from Android's TextWatcher to only implement afterTextChanged method. I have enabled Java 8 support in my project, and added source and target compatibility options in build.gradle file, but even though it works flawless in debug builds, it fails in release builds on every device I tested. I first noticed it in Play Console's pre-launch report, and tested again with the Firebase's Test Lab, still every device throw AbstractMethodError followed by a crash.

Here's my AfterTextChangedListener:

import android.text.Editable;
import android.text.TextWatcher;

public interface AfterTextChangedListener extends TextWatcher {
    @Override
    default void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Do nothing
    }

    @Override
    default void onTextChanged(CharSequence s, int start, int before, int count) {
        // Do nothing
    }

    @Override
    void afterTextChanged(Editable s);
}

Here's the portion of the code that uses this interface:

mSomeEditText.addTextChangedListener((AfterTextChangedListener) editable -> {
    // Logic using 'editable'.
});

Here's the Logcat output for the crash:

FATAL EXCEPTION: main
Process: my.package.name, PID: 4096
java.lang.AbstractMethodError: abstract method "void android.text.TextWatcher.beforeTextChanged(java.lang.CharSequence, int, int, int)"
    at android.widget.TextView.sendBeforeTextChanged(TextView.java:9704)
    at android.widget.TextView.setText(TextView.java:5615)
    at android.widget.TextView.setText(TextView.java:5571)
    at android.widget.EditText.setText(EditText.java:122)
    at android.widget.TextView.setText(TextView.java:5528)
    at i.a.a.f.c.d1.b(Unknown Source:25)
    at i.a.a.f.c.d1.b(Unknown Source:105)
    at androidx.fragment.app.Fragment.g(Unknown Source:11)
    at androidx.fragment.app.s.a(Unknown Source:35)
    at androidx.fragment.app.m.a(Unknown Source:240)
    at androidx.fragment.app.m.j(Unknown Source:2)
    at androidx.fragment.app.m.i(Unknown Source:58)
    at androidx.fragment.app.a.e(Unknown Source:171)
    at androidx.fragment.app.m.a(Unknown Source:38)
    at androidx.fragment.app.m.b(Unknown Source:120)
    at androidx.fragment.app.m.c(Unknown Source:84)
    at androidx.fragment.app.m.b(Unknown Source:31)
    at androidx.fragment.app.a.c(Unknown Source:6)
    at androidx.fragment.app.q.a(Unknown Source:4)
    at c.u.a.b.c(Unknown Source:385)
    at c.u.a.b.e(Unknown Source:2)
    at c.u.a.b.onMeasure(Unknown Source:191)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at androidx.coordinatorlayout.widget.CoordinatorLayout.a(Unknown Source:0)
    at com.google.android.material.appbar.b.a(Unknown Source:93)
    at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(Unknown Source:275)
    at android.view.View.measure(View.java:23181)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
    at android.view.View.measure(View.java:23181)
    at androidx.drawerlayout.widget.DrawerLayout.onMeasure(Unknown Source:264)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at androidx.appcompat.widget.ContentFrameLayout.onMeasure(Unknown Source:156)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1535)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:825)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:704)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1535)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:825)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:704)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at com.android.internal.policy.DecorView.onMeasure(DecorView.java:716)
    at android.view.View.measure(View.java:23181)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2727)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1580)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1864)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1468)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7208)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1004)
    at android.view.Choreographer.doCallbacks(Choreographer.java:816)
    at android.view.Choreographer.doFrame(Choreographer.java:751)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:990)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6694)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
   Force finishing activity my.package.name/.ui.activities.MainActivity

I don't have any idea why this happens especially considering Android supports default methods in interfaces.

Default and static interface methods: Any

Here's my module-level build.gradle:

apply plugin: 'com.android.application'
apply plugin: 'io.fabric'

android {
    signingConfigs {
        release {
            // keystore credentials
        }
    }
    compileSdkVersion 29
    buildToolsVersion '28.0.3'
    defaultConfig {
        applicationId "my.package.name"
        minSdkVersion 16
        targetSdkVersion 29
        multiDexEnabled true
        versionCode 66
        versionName "2020.2b1"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')

    implementation 'com.google.code.gson:gson:2.8.5'

    implementation 'androidx.multidex:multidex:2.0.1'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    // Material
    implementation 'com.google.android.material:material:1.2.0-alpha04'
    implementation 'androidx.exifinterface:exifinterface:1.1.0'
    implementation 'androidx.browser:browser:1.2.0'

    // Room components
    implementation 'androidx.room:room-runtime:2.2.3'
    implementation 'androidx.preference:preference:1.1.0'
    annotationProcessor 'androidx.room:room-compiler:2.2.3'
    androidTestImplementation 'androidx.room:room-testing:2.2.3'

    // SafeRoom
    implementation "com.commonsware.cwac:saferoom.x:1.1.3"

    // Lifecycle components
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"

    // Firebase
    implementation 'com.google.firebase:firebase-core:17.2.2'
    implementation 'com.google.firebase:firebase-ads:18.3.0'
    implementation 'com.google.firebase:firebase-firestore:21.4.0'
    implementation 'com.google.firebase:firebase-inappmessaging-display:19.0.3'
    implementation 'com.google.firebase:firebase-messaging:20.1.0'
    implementation 'com.google.firebase:firebase-perf:19.0.5'
    implementation 'com.google.firebase:firebase-auth:19.2.0'
    implementation 'com.google.firebase:firebase-storage:19.1.1'
    implementation 'com.google.firebase:firebase-config:19.1.1'
    implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'

    //implementation('com.crashlytics.sdk.android:crashlytics:2.7.1@aar') {
    //    transitive = true
    //}
    implementation 'com.google.android.gms:play-services-auth:17.0.0'

    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.2'
    implementation 'com.squareup.picasso:picasso:2.71828'
    // implementation 'com.squareup.picasso:picasso:2.5.2'
    implementation 'com.hootsuite.android:nachos:1.1.1'
    implementation 'com.robertlevonyan.view:MaterialChipView:1.2.4'
    implementation 'net.lingala.zip4j:zip4j:1.3.2'
    implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2'

    // RoundedImageView
    implementation 'com.makeramen:roundedimageview:2.3.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    testImplementation 'org.json:json:20190722'

    implementation 'com.wdullaer:materialdatetimepicker:4.2.3'

    // LeakCanary
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-beta-3'
}


apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.firebase-perf'
apply plugin: 'org.sonarqube'

sonarqube {
    sonarqube {
        properties {
            // Some properties for sonarqube
        }
    }

}

Here's my project-level build.gradle:

buildscript {

    repositories {
        google()
        jcenter()
        maven {
            url 'https://maven.fabric.io/public'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.google.gms:google-services:4.3.3'
        classpath 'io.fabric.tools:gradle:1.26.1'
        classpath 'com.google.firebase:perf-plugin:1.3.1' 
        classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1"
    }
}


allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
        maven {
            url 'https://maven.google.com/'
        }
        maven {
            url "https://s3.amazonaws.com/repo.commonsware.com"
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Any idea why this happens, and how it can be solved?

Bushido answered 9/2, 2020 at 23:20 Comment(5)
Did you try changing default to public? default void beforeTextChanged -> public void beforeTextChangedRoobbie
Like public default void? No, I haven't, Android Studio says it's redundant, but will try. What does it do?Bushido
Yeah, I tried, and as expected, no luck.Bushido
Also, I am wondering why you creating a brand new interface when you don't have any new functions. You can use TextWatcher directly in your code, without the need of creating a brand new interface. New interface require when you are planning to extend it by adding new methods etc.Roobbie
I created a new interface because I don't want to write unused methods beforeTextChanged and onTextChanged in TextWatcher, if I use it, code becomes bloated with empty implementations.Bushido
R
0

can you try this -

public interface AfterTextChangedListener extends TextWatcher {
    @Override
    void afterTextChanged(Editable s);

    @Override
    void beforeTextChanged(CharSequence s, int start, int count, int after);

    @Override
    void onTextChanged(CharSequence s, int start, int before, int count);
}

It worked fine for me.

Roobbie answered 12/2, 2020 at 3:23 Comment(1)
It is exactly same as TextWatcher. But I want to discard unused methods beforeTextChanged and onTextChanged, so I created a new interface.Bushido
B
0

Is it important that it be an interface? I would just use an abstract class for this.

import android.text.Editable;
import android.text.TextWatcher;

public abstract class AfterTextChangedListener implements TextWatcher {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) { }

    @Override
    public void afterTextChanged(Editable s);
}
Breadthways answered 17/12, 2021 at 7:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.