MaikuB / flutter_local_notifications

A Flutter plugin for displaying local notifications on Android, iOS, macOS and Linux
2.45k stars 1.4k forks source link

Exception NoSuchMethodError #1638

Closed zagalaga closed 2 years ago

zagalaga commented 2 years ago

With plugin version 9.6.1 I see the following crash for some users, I was not able to replicate it myself. Does anybody know if this is a bug or if I am doing something wrong?

Fatal Exception: java.lang.NoSuchMethodError: No static method e(Landroid/content/Context;)Landroidx/core/app/n; in class Landroidx/core/app/n; or its super classes (declaration of 'androidx.core.app.n' appears in /data/app/~~v8yhhqH2FHoMiGDXLA-pgQ==/androidx.test.tools.crawler-WljZg1GOGnOmmNq4rJSXqg==/base.apk) at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.getNotificationManager(FlutterLocalNotificationsPlugin.java) at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.cancelAllNotifications(FlutterLocalNotificationsPlugin.java:2) at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:323) at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:17) at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:18) at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0(DartMessenger.java:20) at io.flutter.embedding.engine.dart.DartMessenger.$r8$lambda$TsixYUB5E6FpKhMtCSQVHKE89gQ(DartMessenger.java) at io.flutter.embedding.engine.dart.DartMessenger$$InternalSyntheticLambda$0$ceffc6bae7d364cb48afaf1aaebd60bf9050360d0efb9035ebc54f0851df0a05$0.run(DartMessenger.java:12) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7839) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

tnaseem commented 2 years ago

I'm having a similar issue too (Flutter 2.10.5 / Dart 2.16.2).

I uploaded my app to the Google Play Store and the Pre-launch report brought up this crash report:

java.lang.NoSuchMethodError: No static method e(Landroid/content/Context;)Landroidx/core/app/i; in class Landroidx/core/app/i; or its super classes (declaration of 'androidx.core.app.i' appears in base.apk)
    at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.getNotificationManager(Unknown Source:0)
    at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.cancelAllNotifications(Unknown Source:2)
    at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(Unknown Source:323)
    at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(Unknown Source:17)
    at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(Unknown Source:18)
    at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0(Unknown Source:20)
    at io.flutter.embedding.engine.dart.DartMessenger.a(Unknown Source:0)
    at io.flutter.embedding.engine.dart.a.run(Unknown Source:12)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at androidx.test.espresso.base.Interrogator.loopAndInterrogate(Interrogator.java:10)
    at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:7)
    at androidx.test.espresso.base.UiControllerImpl.loopMainThreadUntilIdle(UiControllerImpl.java:16)
    at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:3)
    at androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform(Unknown Source:0)
    at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:6)
    at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    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:6669)
    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)
MaikuB commented 2 years ago

Difficult for me to say what is going with a minimal app and steps to reproduce this. Things that come mind are if Proguard rules were configured correctly or perhaps that are other settings your apps needs based on your situation that may not be applicable to all apps e.g. enabling multidex

tnaseem commented 2 years ago

Thanks for your reply. I did check the Proguard rules and, as far as I can see, it's set up correctly I think. It's below, if want want to take a look - See if there's anything I've missed!

## Flutter wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }
-dontwarn io.flutter.embedding.**

## Gson rules
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
tnaseem commented 2 years ago

My Notifications class helper, in case it's useful. You'll see a few Crashlytics logs in there, as I was trying to determine where it was crashing. In each case, it was crashing in the init() function with the call to await flutterLocalNotificationsPlugin.initialize()

import 'dart:async';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

import 'package:whitu/helpers/native_timezone.dart';

FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
NotificationAppLaunchDetails notificationAppLaunchDetails;

// Notification channels
class NotificationChannel {
  final String id;
  final String name;
  final String description;

  const NotificationChannel({this.id, this.name, this.description});
}

// Singleton notifications helper.
class Notifications {

  // Singleton.
  static final Notifications _instance = new Notifications.internal();
  factory Notifications() => _instance;

  Notifications.internal();

  // Notification channels.
  static const NotificationChannel appChannel = NotificationChannel(
    id: 'Main', name: 'Main App Notifications', description: 'App notifications');

  static const NotificationChannel dailyChannel = NotificationChannel(
    id: 'Daily', name: 'Daily Reminders', description: 'Receive daily reminders');

  // Cancel notifications.
  Future<void> cancel({@required int id}) async => await flutterLocalNotificationsPlugin.cancel(id);
  Future<void> cancelAll() async => await flutterLocalNotificationsPlugin.cancelAll();

  // Initialise on create. We should only do this once.
  Future<void> init() async {

    FirebaseCrashlytics.instance.log("notifications: Get local time zone.");
    await _getLocalTimeZone();

    notificationAppLaunchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();

    // Initialise the plugin. app_icon.png needs to be a added as a drawable resource in
    // android/app/src/main/res/drawable.
    FirebaseCrashlytics.instance.log("notifications: Add Android initialisation settings.");
    var initializationSettingsAndroid = AndroidInitializationSettings('notification_icon');

    // Ask for permissions on iOS, on app start.
    FirebaseCrashlytics.instance.log("notifications: Add iOS initialisation settings.");
    var initializationSettingsIOS = IOSInitializationSettings(
      requestSoundPermission: true,
      requestBadgePermission: true,
      requestAlertPermission: true,
      onDidReceiveLocalNotification: _onDidReceiveLocalNotification,
    );

    FirebaseCrashlytics.instance.log("notifications: Apply Android/iOS initialisation settings.");

    var initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS,
    );

    FirebaseCrashlytics.instance.log("notifications: Initialise the notifications.");

    // What we should call when the user taps on the notification.
    await flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onSelectNotification: _onSelectNotification,
    );
  }

  // Display a simple notification.
  Future<void> show({
    int id = 0,
    @required String title,
    String body,
    String payload,
  }) async {
    BigTextStyleInformation style = _styleText(title: title, body: body);
    await flutterLocalNotificationsPlugin.show(
      id, title, body, _notificationDetails(appChannel, style), payload: payload);
  }

  Future<void> showDaily({
    int id = 0,
    @required Time time,
    @required String title,
    String body,
    String payload,
  }) async {
    BigTextStyleInformation style = _styleText(title: title, body: body);
    await flutterLocalNotificationsPlugin.zonedSchedule(
      id,
      title,
      body,
      _nextInstanceOfTime(time),
      _notificationDetails(dailyChannel, style),
      payload: payload,
      matchDateTimeComponents: DateTimeComponents.time,
      androidAllowWhileIdle: true,
      uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
    );
  }

  // Get the next occurrence of the specified notification time.
  tz.TZDateTime _nextInstanceOfTime(Time time) {
    final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
    tz.TZDateTime scheduledDate =
    tz.TZDateTime(tz.local, now.year, now.month, now.day, time.hour, time.minute);

    // Add a day, if the time has already passed.
    if (scheduledDate.isBefore(now)) {
      scheduledDate = scheduledDate.add(const Duration(days: 1));
    }

    return scheduledDate;
  }

  // Retrieve the phone's current time zone.
  Future<void> _getLocalTimeZone() async {
    tz.initializeTimeZones();
    final String timezoneName = await NativeTimezone.getLocalTimezone();
    tz.setLocalLocation(tz.getLocation(timezoneName));
  }

  // Callback for handling when a notification is triggered while the app is in the foreground.
  Future<void> _onDidReceiveLocalNotification(int id, String title, String body, String payload) async {
    if (payload != null) {
      debugPrint('iOS notification payload: ${payload ?? 'None'}');
    }
  }

  // Calback for when the notification has been tapped on.
  Future<void> _onSelectNotification(String payload) async {
    if (payload != null) {
      debugPrint('nNotification payload: ${payload ?? 'None'}');
    }
  }

  BigTextStyleInformation _styleText({String title, String body}) {
    return BigTextStyleInformation(
      body,
      htmlFormatBigText: true,
      contentTitle: title,
      htmlFormatContentTitle: true,
    );
  }
  // Platform channel specifics.
  NotificationDetails _notificationDetails(
    NotificationChannel channel,
    BigTextStyleInformation styleInformation,
  ) {
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
      channel.id, channel.name,
      channelDescription: channel.description,
      styleInformation: styleInformation,
      importance: Importance.high,
      priority: Priority.high,
      ticker: 'ticker',
    );

    var iOSPlatformChannelSpecifics = IOSNotificationDetails();
    return NotificationDetails(
      android: androidPlatformChannelSpecifics,
      iOS: iOSPlatformChannelSpecifics,
    );
  }
}
MaikuB commented 2 years ago

The rules look fine to me for the gson stuff. Not sure about the Flutter ones. One thing to note is logs point to an error where it looks like it can't resolve a method within the Android APIs themselves as opposed to something from this plugin so not something I can help with

MaikuB commented 2 years ago

@tnaseem providing Dart code won't help with these issues, which is why I mentioned a minimal app and that could be a link to a GitHub repo

tnaseem commented 2 years ago

No worries. I'll get it done after the weekend and see if it still happens. I'll need to run the test on Firebase TestLab to see if it shows up the crashes then. Only seems to happen on a few of their test devices.

zagalaga commented 2 years ago

I am using the latest GSON rules and it is still happening

armandojimenez commented 2 years ago

This also started showing for me in crashlytics:

Fatal Exception: java.lang.NoSuchMethodError: No static method e(Landroid/content/Context;)Landroidx/core/app/l; in class Landroidx/core/app/l; or its super classes (declaration of 'androidx.core.app.l' appears in base.apk)
       at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.getNotificationManager(FlutterLocalNotificationsPlugin.java)
       at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.cancelAllNotifications(FlutterLocalNotificationsPlugin.java:2)
       at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:323)
       at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:17)
       at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:18)
       at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0(DartMessenger.java:20)
       at io.flutter.embedding.engine.dart.DartMessenger.$r8$lambda$TsixYUB5E6FpKhMtCSQVHKE89gQ(DartMessenger.java)
       at io.flutter.embedding.engine.dart.DartMessenger$$InternalSyntheticLambda$0$ceffc6bae7d364cb48afaf1aaebd60bf9050360d0efb9035ebc54f0851df0a05$0.run(DartMessenger.java:12)
       at android.os.Handler.handleCallback(Handler.java:873)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:214)
       at android.app.ActivityThread.main(ActivityThread.java:7050)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)

No code changes or new release, started appearing 6 days ago

armandojimenez commented 2 years ago

Found this, looks related: https://issuetracker.google.com/issues/238234609

tnaseem commented 2 years ago

That's interesting, in Crashlytics mine appears on those same devices mentioned in the last comment on that thread. ie.:

(https://issuetracker.google.com/issues/238234609#comment6)

ArmandoJimenezOS commented 2 years ago

That's interesting, in Crashlytics mine appears on those same devices mentioned in the last comment on that thread. ie.:

  • Galaxy S20 5G
  • Galaxy 8
  • AQUOS sense2 SH-01L
  • Pixel 5

(https://issuetracker.google.com/issues/238234609#comment6)

That's my comment 😅, I confirmed later that this only happens when the pre release report is running

MaikuB commented 2 years ago

Looks like this is to do with an issue outside of the plugin. Will keep this open for now so others know about this.

MaikuB commented 2 years ago

According to the comment at https://issuetracker.google.com/issues/237785592#comment96, this has been fixed by the Google team so will close this