google / ExoPlayer

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media
https://developer.android.com/media/media3/exoplayer
Apache License 2.0
21.74k stars 6.03k forks source link

java.lang.AbstractMethodError with gradle version 3.5.3 #6801

Closed TsmileAssassin closed 4 years ago

TsmileAssassin commented 4 years ago

I am using exoplayer version 2.11.1 with androidX ,java1.8 and gradle version 3.5.3. The crash appears after updating the gradle version to 3.5+. I am getting runtime crash below:

====================

E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.AbstractMethodError: abstract method "void com.google.android.exoplayer2.Player$EventListener.onTimelineChanged(com.google.android.exoplayer2.Timeline, int)"
at com.google.android.exoplayer2.ExoPlayerImpl$PlaybackInfoUpdate.lambda$run$0$ExoPlayerImpl$PlaybackInfoUpdate(ExoPlayerImpl.java:804)
at com.google.android.exoplayer2.-$$Lambda$ExoPlayerImpl$PlaybackInfoUpdate$N_S5kRfhaRTAkH28P5luFgKnFjQ.invokeListener(Unknown Source:2)
at com.google.android.exoplayer2.BasePlayer$ListenerHolder.invoke(BasePlayer.java:182)
at com.google.android.exoplayer2.ExoPlayerImpl.invokeAll(ExoPlayerImpl.java:845)
at com.google.android.exoplayer2.ExoPlayerImpl.access$000(ExoPlayerImpl.java:43)
at com.google.android.exoplayer2.ExoPlayerImpl$PlaybackInfoUpdate.run(ExoPlayerImpl.java:802)
at com.google.android.exoplayer2.ExoPlayerImpl.notifyListeners(ExoPlayerImpl.java:736)
at com.google.android.exoplayer2.ExoPlayerImpl.updatePlaybackInfo(ExoPlayerImpl.java:710)
at com.google.android.exoplayer2.ExoPlayerImpl.handlePlaybackInfo(ExoPlayerImpl.java:652)
at com.google.android.exoplayer2.ExoPlayerImpl.handleEvent(ExoPlayerImpl.java:595)
at com.google.android.exoplayer2.ExoPlayerImpl$1.handleMessage(ExoPlayerImpl.java:127)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:228)
at android.app.ActivityThread.main(ActivityThread.java:7726)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:993)
tonihei commented 4 years ago

Previous reports of similar issues mostly forgot to enable Java 8 support (for example: #5600). Can you check that your gradle config includes the following lines?

compileOptions {
  targetCompatibility JavaVersion.VERSION_1_8
}

You could also try to clean all Android Studio caches ("File -> Invalidate Caches / Restart") and then also clean and rebuild your project to exclude unwanted side effects from previous versions of ExoPlayer.

zvonkokemperle commented 4 years ago

I experience exactly the same issue. I have targetCompatibility JavaVersion.VERSION_1_8 included. This issue is only visible if generating signed builds, it doesn't matter if it's debug or release.

The only solution I have found is to disable D8 desugaring by adding the following line to gradle.properties: android.enableD8.desugaring=false

This solution however is not ok. I am developing a library and I can't force the users to use this workaround.

TsmileAssassin commented 4 years ago

I have targetCompatibility JavaVersion.VERSION_1_8 included

tonihei commented 4 years ago

I can't reproduce this in my setup. Have you tried the suggested cleaning and rebuilding steps?

If you did and the problem persists, could you also tell us the Android Studio version and gradle version you are using? And also how you depend on ExoPlayer and other noteworthy build setting that may help us to reproduce the issue.

zvonkokemperle commented 4 years ago

@tonihei Please note that this is only reproducible when a signed builds (it doesn't matter if you use debug or release).

I am using:

Android Studio 3.5.3 Gradle 5.4.1

Nothing helps other than android.enableD8.desugaring=false This could maybe also be a bug in D8.

tonihei commented 4 years ago

There must be something else.

I just did the following:

Running this application on a Nexus 6P works without any crashes. And I can see both the signing and the R8 step happening during the build.

Could try to some up with a similar basic setup that reproduces the problem? This would help to find the offending part and whether there is something we can do about it.

zvonkokemperle commented 4 years ago

@tonihei

It really looks like that this is some kind of Android Studio or Gradle caching bug. Invalidate caches and restart did the trick, It's strange as I did this before and it didn't help. Currently I can't reproduce this anymore. I really hope that this is the solution.

Thank you for your help.

tonihei commented 4 years ago

Good to know. We had this before that ExoPlayer updates with subtle API changes caused Android Studio to build something wrong, possible based on cached data. Invalidating the caches helped in almost all cases so far :)

tonihei commented 4 years ago

@TsmileAssassin Can you also try to do invalidate caches and restart again to see if it works?

zvonkokemperle commented 4 years ago

@tonihei Actually the issue persists. I finally found the cause. It's a bug in Gradle Plugin and it has something to do with Dexing Artifact Transformations. Steps to reproduce:

  1. Create a library that is using Exo Player, e.g. init the player add EventListener and return it
  2. Create App that uses the generated aar from step 1.
  3. In the App call the library method that returns the player

You will see the crash:

java.lang.AbstractMethodError: abstract method "void com.google.android.exoplayer2.Player$EventListener.onTimelineChanged(com.google.android.exoplayer2.Timeline, int)"
        at com.google.android.exoplayer2.ExoPlayerImpl$PlaybackInfoUpdate.lambda$run$0$ExoPlayerImpl$PlaybackInfoUpdate(ExoPlayerImpl.java:804)
        at com.google.android.exoplayer2.-$$Lambda$ExoPlayerImpl$PlaybackInfoUpdate$N_S5kRfhaRTAkH28P5luFgKnFjQ.invokeListener(Unknown Source:2)
        at com.google.android.exoplayer2.BasePlayer$ListenerHolder.invoke(BasePlayer.java:182)
        at com.google.android.exoplayer2.ExoPlayerImpl.invokeAll(ExoPlayerImpl.java:845)
        at com.google.android.exoplayer2.ExoPlayerImpl.access$000(ExoPlayerImpl.java:43)
        at com.google.android.exoplayer2.ExoPlayerImpl$PlaybackInfoUpdate.run(ExoPlayerImpl.java:802)
        at com.google.android.exoplayer2.ExoPlayerImpl.notifyListeners(ExoPlayerImpl.java:736)
        at com.google.android.exoplayer2.ExoPlayerImpl.updatePlaybackInfo(ExoPlayerImpl.java:710)
        at com.google.android.exoplayer2.ExoPlayerImpl.handlePlaybackInfo(ExoPlayerImpl.java:652)
        at com.google.android.exoplayer2.ExoPlayerImpl.handleEvent(ExoPlayerImpl.java:595)
        at com.google.android.exoplayer2.ExoPlayerImpl$1.handleMessage(ExoPlayerImpl.java:127)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)

The crash doesn't happen if I create a maven repo that includes the Exo Player dependencies automatically by using transitive true. If not using transitive true and providing the Exo Player dependencies inside the app's build.gradle file it crashes. There is a workaround that fixes this issue by using: android.enableDexingArtifactTransform=false This however is not a solution for me as I can't force users to do this in their App.

Please refer to https://issuetracker.google.com/issues/139821726 and https://www.reddit.com/r/androiddev/comments/d0e1mi/gradle_plugin_350_causes_abstractmethoderror_for/

Edit: The min API level needs to be below 21.

Koster35 commented 4 years ago

I am also having a similar AbstractMethodError issue using ExoPlayer 2.10.6. This occurs when we call SimpleExoPlayer#setVideoSurfaceHolder() in our ExoPlayer implementation:

java.lang.AbstractMethodError: abstract method "void com.google.android.exoplayer2.analytics.AnalyticsListener.onSurfaceSizeChanged(com.google.android.exoplayer2.analytics.AnalyticsListener$EventTime, int, int)"
at com.google.android.exoplayer2.analytics.AnalyticsCollector.onSurfaceSizeChanged(AnalyticsCollector.java:331)
at com.google.android.exoplayer2.SimpleExoPlayer.maybeNotifySurfaceSizeChanged(SimpleExoPlayer.java:1208)
at com.google.android.exoplayer2.SimpleExoPlayer.setVideoSurfaceHolder(SimpleExoPlayer.java:342)

My project is also using Mux 1.0.0-r2.10.6 (https://github.com/muxinc/mux-stats-sdk-exoplayer). Mux listens to Exoplayer's AnalyticsListener interface. When we disable Mux, the issue does not seem to occur.

Also, this issue only consistently occurs on devices running Android API 22 and below.

My solution was to downgrade our project's Gradle plugin 3.5.2 -> 3.4.2, as described in the previous posts about this issue. After downgrading the Gradle plugin, the issue no longer reproduces on any API level, even with Mux enabled.

tonihei commented 4 years ago

@zvonkokemperle @Koster35 Thanks for updating the reproduction steps.

It seems this is may actually be working as intended according to the issue you've linked, in particular this post: https://issuetracker.google.com/139821726#comment5 "The dexing and desugaring pipeline has changed in 3.5, and by default, we assume all Maven dependencies are fully declared. In your case, "[library1, e.g. your library]" depends on "[library2, e.g. ExoPlayer]" which you are explicitly excluding."

Can you provide more details on how the app, your test lib and ExoPlayer depend upon each other in gradle? I think directly depending on an aar without context means you may not automatically get all dependencies needed for correct dexing. Mux is also doing some aar rewriting that may be related.

To further debug this, it may be helpful to upload your test setup somewhere so we can verify ourselves.

zvonkokemperle commented 4 years ago

@tonihei

Here is my implementation.

Library:

build.gradle

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    api 'com.google.android.exoplayer:exoplayer-core:2.11.1'
    api 'com.google.android.exoplayer:exoplayer-ui:2.11.1'

    api 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation "androidx.core:core-ktx:1.1.0"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

MyPlayer.kt

class MyPlayer {
    private val TAG = "MyPlayer"

    private var exoPlayer: SimpleExoPlayer? = null

    fun initPlayer(context: Context?): SimpleExoPlayer {
        exoPlayer = ExoPlayerFactory.newSimpleInstance(context!!, DefaultTrackSelector())
        val listener: Player.EventListener = object : Player.EventListener {
            override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
                Log.w(TAG, "playewhenready: $playWhenReady, state: $playbackState")
                exoPlayer!!.playWhenReady = true
            }

            override fun onRepeatModeChanged(repeatMode: Int) {}

            override fun onIsPlayingChanged(isPlaying: Boolean) {}

            override fun onPlayerError(error: ExoPlaybackException) {}

            override fun onSeekProcessed() {}

            override fun onTimelineChanged(timeline: Timeline, manifest: Any?, reason: Int) {}

            override fun onTracksChanged(trackGroups: TrackGroupArray, trackSelections: TrackSelectionArray) {}

            override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}

            override fun onPositionDiscontinuity(reason: Int) {}

            override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters) {}
        }
        exoPlayer!!.addListener(listener)
        val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(context, "My User Agent")
        val videoSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse("https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4"))
        exoPlayer!!.prepare(videoSource)
        return exoPlayer!!
    }
}

App:

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.test.exocrashtest"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

repositories {
    google()
    mavenCentral()
    maven {
        url 'https://maven.google.com'
    }

    jcenter()

    flatDir {
        dirs 'libs'
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation "androidx.core:core-ktx:1.1.0"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    // my library including ExoPlayer
    implementation (name: 'exolibrary-debug', ext: 'aar')

    implementation 'com.google.android.exoplayer:exoplayer-core:2.11.1'
    implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.1'
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/playerView"
        android:layout_width="300dp"
        android:layout_height="300dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onResume() {
        super.onResume()
        val simpleExoPlayer = MyPlayer().initPlayer(this)
        val view = findViewById<PlayerView>(R.id.playerView)
        view.useController = true
        view.player = simpleExoPlayer
    }
}

For dependencies I have tried both api and implementation and it crashes in both cases.

tonihei commented 4 years ago

I can reliably reproduce the problem with your setup. I think it still works as intended because you directly depend on the aar file and the problem goes away once you change the dependency to implementation project(path: ':exolibrary-debug')

The underlying issue is that the aar file doesn't contain any dependency declaration and that's why the dexing step can't resolve the methods successfully. According to the post linked above, full dependency declarations are required from 3.5.3 onwards.

To tell gradle about the dependencies, you need a pom file as published by Maven for example. If you were publishing your library to Maven and then include it directly from there (i.e. using the aar + the pom file), it probably works again. You could also try to use a local Maven repository including pom files, following the instructions here. I haven't tried it because it's no longer ExoPlayer related and we can't provide support for generic gradle issues. Might be worth a try though.

Based on the above, I'll close this issue as working as intended. It only occurs if you depend on an aar directly without full dependency declaration, so anyone depending on ExoPlayer through the normal way will not see any issues.

calmmycode commented 4 years ago

For me the fix was to override exact every abstract default method in all exoplayer interfaces with blank code