ryanheise / audio_service

Flutter plugin to play audio in the background while the screen is off.
787 stars 463 forks source link

Null pointer exception with audioHandlerInterface inside sendNotificationClicked() #1059

Closed musaffa closed 5 months ago

musaffa commented 5 months ago

Documented behaviour

We are using just_audio_background which depends on audio_service.

MainActivity Inherits from AudioServiceActivity like below:

import com.ryanheise.audioservice.AudioServiceActivity;

class MainActivity extends AudioServiceActivity {
    // ...
}

Actual behaviour

The app is crushing for 20% of Android users. The bug is not allowing MainActivity to run.

Runtime error

# Crashlytics - Stack trace
# Application: com.my_app
# Platform: android
# Version: 5.0.17 (112)

Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my_app/com.ryanheise.audioservice.AudioServiceActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.ryanheise.audioservice.a$c.R(java.lang.String, java.lang.Object)' on a null object reference
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3693)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3830)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2319)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:8010)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)

Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void com.ryanheise.audioservice.a$c.R(java.lang.String, java.lang.Object)' on a null object reference
       at com.ryanheise.audioservice.AudioServicePlugin.sendNotificationClicked(AudioServicePlugin.java:53)
       at com.ryanheise.audioservice.AudioServicePlugin.onAttachedToActivity(AudioServicePlugin.java:101)
       at io.flutter.embedding.engine.FlutterEngineConnectionRegistry.attachToActivityInternal(FlutterEngineConnectionRegistry.java:90)
       at io.flutter.embedding.engine.FlutterEngineConnectionRegistry.attachToActivity(FlutterEngineConnectionRegistry.java:23)
       at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach(FlutterActivityAndFragmentDelegate.java:37)
       at io.flutter.embedding.android.FlutterActivity.onCreate(FlutterActivity.java:13)
       at android.app.Activity.performCreate(Activity.java:8293)
       at android.app.Activity.performCreate(Activity.java:8272)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1384)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3674)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3830)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
.....

Minimal reproduction project

Official example: main.dart

Reproduction steps

It's a runtime issue for some users. We don't have the reproduction steps.

I guess that this line is causing the issue.

Output of flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.13.4, on Ubuntu 22.04.3 LTS 6.5.0-15-generic, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[✓] Chrome - develop for the web
[✗] Linux toolchain - develop for Linux desktop
    ✗ GTK 3.0 development libraries are required for Linux development.
      They are likely available from your distribution (e.g.: apt install libgtk-3-dev)
[✓] Android Studio (version 2023.1)
[!] Android Studio (version unknown)
    ✗ Unable to determine Android Studio version.
[✓] VS Code (version 1.85.2)
[✓] Connected device (3 available)
[✓] Network resources

Devices exhibiting the bug

Devices: Samsung, Oppo, Xiaomi, Vivo etc

OS: Android 14, 13, 12, 11, 10, 9 etc

ryanheise commented 5 months ago

Minimal reproduction project

Official example: main.dart < -----

Reproduction steps

It's a runtime issue for some users. We don't have the reproduction steps.

It's highly unlikely that you are actually running my example in production and it has users.

It is more likely that you just didn't bother to create the minimal reproduction project and provide the reproduction steps. Without that, it's impossible to determine exactly which line the issue happens on, and it is also impossible to tell whether this is a bug within the plugin or a bug within your app.

I'll close this as an invalid issue, but please open a new bug report and provide answers to the questions missing from your current report.

musaffa commented 5 months ago

@ryanheise Sorry for the confusion. We certainly are not running your example in Production. We have a production app with 200k Android users and 20% is facing this issue.

This issue doesn't happen in Android simulator and the Android devices we use for testing. That's why I didn't create any minimal reproduction project and I couldn't provide you the reproduction steps.

My hope was that since we can pin point the line where the exception occurs, you, the author of this plugin, might know why audioHandlerInterface is null for some users.

ryanheise commented 5 months ago

Is it possible for you to at least share your pubspec dependencies and mention anything non-standard that your app might be doing in configuration?

I have added a null check in branch fix/notification_npe. Is there any way you could test whether this solves your issue?

musaffa commented 5 months ago

audioHandlerInterface is initialized at onAttachedToEngine and removed at onDetachedFromEngine. Is there any chance that onAttachedToActivity might be called before onAttachedToEngine or after onDetachedFromEngine is called?

I have added a null check in branch fix/notification_npe. Is there any way you could test whether this solves your issue?

I'll try

ryanheise commented 5 months ago

audioHandlerInterface is initialized at onAttachedToEngine and removed at onDetachedFromEngine. Is there any chance that onAttachedToActivity might be called before onAttachedToEngine or after onDetachedFromEngine is called?

That's unlikely, I doubt Flutter would be calling these lifecycle methods out of order. Without a minimal reproduction project, or even a list of dependencies or information on configuration, I won't be able to investigate what code path lead to this. We can only test whether the NPE guard solves it.

musaffa commented 5 months ago

pubspec.yaml

name: my_app
description: Native App

publish_to: 'none'

version: 5.0.18+113

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter

  # application
  flutter_dotenv: ^5.0.2

  # state management
  flutter_riverpod: ^2.3.10

  # routing
  qlevar_router: ^1.7.1

  # data
  flutter_data: ^1.5.13
  flutter_data_json_api_adapter: ^0.8.0
  isar: ^3.0.5
  isar_flutter_libs: ^3.0.5
  equatable: ^2.0.5

  # local db
  drift: ^2.10.0
  sqlite3_flutter_libs: ^0.5.13
  json_api: ^5.3.0

  # form
  date_field: ^3.0.2
  dropdown_button2: ^2.3.9
  idkit_inputformatters: ^0.0.1
  easy_debounce: ^2.0.3

  # widgets
  infinite_scroll_pagination: ^4.0.0
  scrollable_positioned_list: ^0.3.8
  carousel_slider: ^4.1.1
  animated_toggle_switch: ^0.8.0
  just_audio: ^0.9.36
  just_audio_background: ^0.0.1-beta.11
  pdfx: 2.4.0
  # pdfx:
  #   path: /home/musaffa/projects/libraries/packages.flutter/packages/pdfx

  # text
  intl: ^0.18.0
  flutter_localizations:
    sdk: flutter
  flutter_html: '^3.0.0-beta.2'
  inflection3: ^0.5.3+2
  recase: ^4.1.0
  html: ^0.15.2

  # platform
  permission_handler: ^10.2.0
  path_provider: ^2.0.12
  shared_preferences: ^2.0.16
  geolocator: ^10.0.0
  geocoding: ^2.1.0
  connectivity_plus: ^3.0.3
  open_filex: ^4.3.2
  share_plus: ^7.2.2
  wakelock_plus: ^1.1.4

  # http
  dio: ^5.3.2
  url_launcher: ^6.1.14

  # builder
  json_annotation: ^4.7.0

  # utility
  path: '^1.8.3'
  glob: ^2.1.2

  # extensions
  collection: ^1.16.0

  # islamic
  hijri: ^3.0.0
  hijri_picker:
    git:
      url: https://github.com/musaffa/hijri_picker.git
      ref: pass-weekday-number
  adhan: ^2.0.0-nullsafety.2
  smooth_compass: ^2.0.12

  # app widget
  home_widget: ^0.3.0
  workmanager: ^0.5.1

  # firebase
  firebase_core: ^2.15.0
  firebase_messaging: ^14.6.5
  flutter_local_notifications: ^15.1.0+1
  firebase_analytics: ^10.4.4
  firebase_crashlytics: ^3.3.4

  # assets
  flutter_svg: ^2.0.7
  cached_network_image: ^3.2.3
  transparent_image: ^2.0.0
  flutter_native_splash: ^2.3.2

dependency_overrides:
  audio_service:
     git:
       url: https://github.com/ryanheise/audio_service.git
       ref: fix/notification_npe
       path: audio_service

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^2.0.0

  build_runner: ^2.4.6
  isar_generator: ^3.0.5

  json_serializable: ^6.3.1
  faker: ^2.0.0
  drift_dev: ^2.10.0

  flutter_launcher_icons: "^0.13.1"

flutter:
  generate: true
  uses-material-design: true

  assets:
    - .env

  fonts:
    - family: Roboto
      fonts:
        - asset: assets/fonts/Roboto-Regular.ttf
        - asset: assets/fonts/Roboto-Bold.ttf
          weight: 700

flutter_launcher_icons:
  android: "launcher_icon"
  ios: true
  image_path: assets/images/logos/logo-icon.png

flutter_native_splash:
  color: "#0a676a"
  image: assets/images/logos/logo-transparent.png
  web: false

  android_12:
    image: assets/images/logos/logo-transparent-android-12.png
musaffa commented 5 months ago

@ryanheise Can you please open #1051 issue temporarily?

ryanheise commented 5 months ago

Regarding your dependencies, it may be possible that geolocator and/or some of the firebase plugins could be interacting in the background with the way that audio_service works, since those plugins also interact with the FlutterEngine lifecycle.

The ideal thing would be to have a minimal reproduction project to fully understand what is happening. My guess is that the lifecycle might be quickly started and stopped in such a short span of time that the callbacks end up running after things have detached. E.g. if one of the other plugins momentarily woke up the app in the background to receive a message and then quickly shut down again.

The null checks would help with that.

Regarding #1051 , the situation is probably the same. I have added a null check in case of any extremely short-lived FlutterEngine lifecycle (same branch).

github-actions[bot] commented 5 months ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.