OneSignal / OneSignal-DotNet-SDK

OneSignal is a free push notification service for mobile apps. This plugin makes it easy to integrate your .NET app with OneSignal. https://onesignal.com
MIT License
16 stars 5 forks source link

[Bug]: OneSignal .net-android error in Initialize call. Java.Lang.NoSuchMethodError #89

Open IainS1986 opened 10 months ago

IainS1986 commented 10 months ago

What happened?

Updgrading a Xamarin Native to .net-android and .net-ios project.

Added OneSignalSDK.DotnNet/5.0.2

OneSignal.Initialize("MY KEY");

This line then errors with...

Java.Lang.NoSuchMethodError
                                                                                                    no static method "Lcom/onesignal/OneSignal;.initWithContext(Landroid/content/Context;Ljava/lang/String;)V"
2024-01-18 09:56:40.876 17411-17411 DOTNET                  com.xxx.xxx               I     at Java.Interop.JniEnvironment.StaticMethods.GetStaticMethodID(JniObjectReference type, String name, String signature)
                                                                                                       at Java.Interop.JniType.GetStaticMethod(String name, String signature)
                                                                                                       at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String method, String signature)
2024-01-18 09:56:40.876 17411-17411 DOTNET                  com.xxx.xxx               I     at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String encodedMember)
                                                                                                       at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeVoidMethod(String encodedMember, JniArgumentValue* parameters)
                                                                                                       at Com.OneSignal.Android.OneSignal.InitWithContext(Context context, String appId)
2024-01-18 09:56:40.876 17411-17411 DOTNET                  com.xxx.xxx               I     at OneSignalSDK.DotNet.Android.AndroidOneSignal.Initialize(String appId)
                                                                                                       at OneSignalSDK.DotNet.OneSignal.Initialize(String appId)
                                                                                                       at xxx.Droid.Views.AppStartup.AppStartupContainerActivity.OnCreate(Bundle bundle)

Steps to reproduce?

1. Create new .net-android project
2. Add the OneSignalSDK.DotNet/5.0.2 nuget
3. Add the Initialize line to your first/main Activity
4. Run

What did you expect to happen?

It initialises.

Relevant log output

Java.Lang.NoSuchMethodError
                                                                                                    no static method "Lcom/onesignal/OneSignal;.initWithContext(Landroid/content/Context;Ljava/lang/String;)V"
2024-01-18 09:56:40.876 17411-17411 DOTNET                  com.xxx.xxx               I     at Java.Interop.JniEnvironment.StaticMethods.GetStaticMethodID(JniObjectReference type, String name, String signature)
                                                                                                       at Java.Interop.JniType.GetStaticMethod(String name, String signature)
                                                                                                       at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String method, String signature)
2024-01-18 09:56:40.876 17411-17411 DOTNET                  com.xxx.xxx               I     at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String encodedMember)
                                                                                                       at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeVoidMethod(String encodedMember, JniArgumentValue* parameters)
                                                                                                       at Com.OneSignal.Android.OneSignal.InitWithContext(Context context, String appId)
2024-01-18 09:56:40.876 17411-17411 DOTNET                  com.xxx.xxx               I     at OneSignalSDK.DotNet.Android.AndroidOneSignal.Initialize(String appId)
                                                                                                       at OneSignalSDK.DotNet.OneSignal.Initialize(String appId)
                                                                                                       at xxx.Droid.Views.AppStartup.AppStartupContainerActivity.OnCreate(Bundle bundle)

Code of Conduct

IainS1986 commented 10 months ago

Ok, this is an R8 shrinker issue.

I've tried adding some custom proguard rules but I'm struggling to get it to work.

-keep public class com.onesignal.* {;} -keep interface com.onesignal.* {;}

Works to get past the basic method missing errors but then I get the following Java.Lang.Exception...

Service interface com.onesignal.core.internal.operations.IOperationRepo could not be instantiated

I thought the keep interface line above would resolve this but it didn't. Any ideas what rules are needed?

IainS1986 commented 10 months ago

Does anyone have working, R8 shrinker rules for OneSignal on Android?

I've tried using older proguard rules from here https://github.com/OneSignal/OneSignal-Android-SDK/blob/main/OneSignalSDK/onesignal/consumer-proguard-rules.pro#L51 but this isn't working with R8 shrinking.

Turning off R8 works fine, I'm sure our older Xamarin native project using OneSignal was R8 shrinking so this seems a specific problem in the new .net-android SDK support.

Can't seem to get the R8 not to break whatever it is that implements IOperationRepo

[mono-rt] java.lang.Exception: Service interface com.onesignal.core.internal.operations.IOperationRepo could not be instantiated
[mono-rt]   at com.onesignal.common.services.ServiceProvider.getService(ServiceProvider.kt:70)
[mono-rt]   at com.onesignal.internal.OneSignalImp.initWithContext(OneSignalImp.kt:40)
[mono-rt]   at com.onesignal.OneSignal.initWithContext(OneSignal.kt:126)
IainS1986 commented 9 months ago

Is there anyone from OneSignal able to advise on how to use this SDK with R8 shrinking on Android?

IainS1986 commented 8 months ago

This is the Full Proguard rules i've tried for OneSignal (based off the old Xamarin one and the Android project) but not had any success yet...

# OneSignal
# https://github.com/OneSignal/OneSignal-Android-SDK/blob/main/OneSignalSDK/onesignal/consumer-proguard-rules.pro#L51
-dontwarn com.amazon.**
-dontwarn com.google.android.gms.location.LocationListener
-dontwarn com.onesignal.**
-dontwarn com.onesignal.notification.**

-keep class com.google.firebase.provider.** { *; }
-keep class com.onesignal.** { *; }
-keep interface com.onesignal.** { *; }
-keep class com.onesignal.ActivityLifecycleListenerCompat** {*;}

# These 2 methods are called with reflection.
-keep class com.google.android.gms.common.api.GoogleApiClient {
    void connect();
    void disconnect();
}

# Need to keep as these 2 methods are called with reflection from com.onesignal.PushRegistratorFCM
-keep class com.google.firebase.iid.FirebaseInstanceId {
    static com.google.firebase.iid.FirebaseInstanceId getInstance(com.google.firebase.FirebaseApp);
    java.lang.String getToken(java.lang.String, java.lang.String);
}

-keep class ** implements com.onesignal.notifications.IPermissionObserver{
    void onNotificationPermissionChange(java.lang.Boolean);
}

-keep class ** implements com.onesignal.user.subscriptions.IPushSubscriptionObserver {
    void onPushSubscriptionChange(com.onesignal.user.subscriptions.PushSubscriptionChangedState);
}

-keep class ** implements com.onesignal.notifications.INotificationServiceExtension{
    void onNotificationReceived(com.onesignal.notifications.INotificationReceivedEvent);
}

# Observer backcall methods are called with reflection
-keep class com.onesignal.OSSubscriptionState {
    void changed(com.onesignal.OSPermissionState);
}

-keep class com.onesignal.OSPermissionChangedInternalObserver {
    void changed(com.onesignal.OSPermissionState);
}

-keep class com.onesignal.OSSubscriptionChangedInternalObserver {
    void changed(com.onesignal.OSSubscriptionState);
}

-keep class com.onesignal.OSEmailSubscriptionChangedInternalObserver {
    void changed(com.onesignal.OSEmailSubscriptionState);
}

-keep class com.onesignal.OSSMSSubscriptionChangedInternalObserver {
    void changed(com.onesignal.OSSMSSubscriptionState);
}

-keep class ** implements com.onesignal.OSPermissionObserver {
    void onOSPermissionChanged(com.onesignal.OSPermissionStateChanges);
}

-keep class ** implements com.onesignal.OSSubscriptionObserver {
    void onOSSubscriptionChanged(com.onesignal.OSSubscriptionStateChanges);
}

-keep class ** implements com.onesignal.OSEmailSubscriptionObserver {
    void onOSEmailSubscriptionChanged(com.onesignal.OSEmailSubscriptionStateChanges);
}

-keep class ** implements com.onesignal.OSSMSSubscriptionObserver {
    void onOSEmailSubscriptionChanged(com.onesignal.OSSMSSubscriptionStateChanges);
}

-keep class com.onesignal.shortcutbadger.impl.AdwHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.ApexHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.AsusHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.DefaultBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.EverythingMeHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.HuaweiHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.LGHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.NewHtcHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.NovaHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.OPPOHomeBader { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.SamsungHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.SonyHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.VivoHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.XiaomiHomeBadger { <init>(...); }
-keep class com.onesignal.shortcutbadger.impl.ZukHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.AdwHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.ApexHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.AsusHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.DefaultBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.EverythingMeHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.HuaweiHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.LGHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.NewHtcHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.NovaHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.OPPOHomeBader { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.SamsungHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.SonyHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.VivoHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.XiaomiHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.ZukHomeBadger { <init>(...); }

-dontwarn com.amazon.**
-dontwarn com.huawei.**

# Proguard ends up removing this class even if it is used in AndroidManifest.xml so force keeping it.
-keep public class com.onesignal.ADMMessageHandler {*;}
-keep public class com.onesignal.ADMMessageHandlerJob {*;}

# OSRemoteNotificationReceivedHandler is an interface designed to be extend then referenced in the
#    app's AndroidManifest.xml as a meta-data tag.
# This doesn't count as a hard reference so this entry is required.
-keep class ** implements com.onesignal.OneSignal$OSRemoteNotificationReceivedHandler {
   void remoteNotificationReceived(android.content.Context, com.onesignal.OSNotificationReceivedEvent);
}

-keep class com.onesignal.JobIntentService$* {*;}
-keep class com.onesignal.OneSignalUnityProxy {*;}

-keepclassmembers class com.onesignal.notifications.** { *; }
IainS1986 commented 8 months ago

Example Repo of the issue (using MAUI)

https://github.com/IainS1986/OneSignal-R8-Test

This is a basic MAUI template (from Rider).

When I run a release configuration build to my phone I get the same exception…

FATAL EXCEPTION: main Process: com.companyname.mauiapp1, PID: 1221 java.lang.RuntimeException: Unable to create application crc64e632a077a20c694c.MainApplication: java.lang.Exception: Service interface com.onesignal.core.internal.operations.IOperationRepo could not be instantiated

rmayrink commented 8 months ago

Same problema here...

ghost commented 7 months ago

I had to add this instruction to my Proguard file to fix it: -keepattributes Signature

IainS1986 commented 7 months ago

When I try adding that I'm getting a whole bunch of R8 errors "A type or variable is not in scope."

Can you share your whole proguard file?

ghost commented 7 months ago

OneSignal - Proguard ends up removing this class even if it is used in AndroidManifest.xml so force keeping it.

-dontwarn com.onesignal.**

OneSignal - These 2 methods are called with reflection.

-keep class com.google.android.gms.common.api.GoogleApiClient { void connect(); void disconnect(); }

-keep class com.onesignal.* {;} -keepattributes Signature

Keep methods annotated with @JavascriptInterface for webview bridge (InApp feature)

-keepclassmembers class { @android.webkit.JavascriptInterface ; }

End OneSignal

ghost commented 7 months ago

It seems only this was enough for my project. At least for initializing the onesignal.

IainS1986 commented 7 months ago

Cheers, mayhbe theres something else in my proguard colliding with this, i'll try removing bits and adding bits and see how I go, cheers for pointing this line out though 👍

ghost commented 7 months ago

My project is Xamarin and it's using the version 5.0.2.

IainS1986 commented 7 months ago

Might be the difference too, this is specifically about .net-android 8+ (which you'll have to port over too soon). Hard to tell thought 🤷

ghost commented 7 months ago

I don't think it doesn't change much because it's using the onesignal android 5.0.3. It should be the same.

IainS1986 commented 7 months ago

Proguard effects your entire app. Not just onesignal. So it might fix it, but adding that line breaks some other sections in my proguard that (may) be needed for .net-android, I can't say for sure so will need some time to go through things bit by bit. I think it broke an AndroidX section in my proguard but will see.

ghost commented 7 months ago
# OneSignal
# https://github.com/OneSignal/OneSignal-Android-SDK/blob/main/OneSignalSDK/onesignal/consumer-proguard-rules.pro#L51
-dontwarn com.onesignal.**
-keep class com.microsoft.maui.** {*;}
-keep class androidx.startup.** {*;}
-dontwarn com.amazon.** 
-dontwarn com.google.firebase.**
-keep class com.google.firebase.**

# OneSignal - Proguard ends up removing this class even if it is used in AndroidManifest.xml so force keeping it.

-keep class com.onesignal.** {*;}
-keepattributes Signature

# Keep methods annotated with @JavascriptInterface for webview bridge (InApp feature)
-keepclassmembers class * {
@android.webkit.JavascriptInterface *;
}
# End OneSignal
ghost commented 7 months ago

I tried to change your maui test app using this configuration.

IainS1986 commented 7 months ago

When I'm refering the my proguard file thats breaking I'm not referring to that test project, that was for the OneSignal team to investigate.

The proguard file I have in another app is much larger. But I'll dig more into it but won't be happening for a week or two

IainS1986 commented 7 months ago

I narrowed it down to the following lines that break with the keepattributes Signature line,

-dontwarn androidx.**
-keep class androidx.** { *; }
-keep interface androidx.** { *; }

So I need to figure out the exact bits of AndroidX to keep as opposed to an (ugly) blanket keep on the whole thing

IainS1986 commented 7 months ago

Ok. After a lot of head banging I think I've resolved the issue.

So as @arctouch-rafaelpontes above pointed out, you do need to have this in your proguard,

-keepattributes Signature

But, you might (like me) have the following your proguard

-keep class androidx.** { *; }

These two lines do not work together. Ideally, you shouldn't be just having keep on the whole androidx, but its a faff not doing that if you need it. However you'll need to work out all references you are using in androidx and only include those. This is not fun.

Thankfully, someone did a lot of leg work already around this issue they were having with a different library here https://github.com/xamarin/xamarin-android/issues/7008

So from this, I managed to make the following proguard that works, you might have some things still fail that you need to add, but this should work.

# OneSignal
-keep class com.onesignal.** { *; }
-keep class com.onesignal.core.** { *; }
-keep class com.onesignal.session.** { *; }
-keep class com.onesignal.user.** { *; }
-keep class com.onesignal.internal.** { *; }
-keep class com.onesignal.debug.** { *; }
-keep class com.onesignal.common.** { *; }
-keep class ** implements com.onesignal.common.modules.IModule { *; }

# https://github.com/xamarin/xamarin-android/issues/7008#issuecomment-1789672213
-keepattributes AnnotationDefault,
                EnclosingMethod,
                InnerClasses,
                RuntimeVisibleAnnotations, # <-- Kotlin metadata is a runtime-visible annotation
                RuntimeVisibleParameterAnnotations,
                RuntimeVisibleTypeAnnotations,
                Signature

# Glide and MAUI Essentials                
-keep class com.microsoft.maui.** { *; }
-keep class com.bumptech.glide.** { *; }

# Google Android Material
-keep class com.google.android.material.** { *; }

# AndroidX
-keep class androidx.activity.result.** { *; }
-keep class androidx.appcompat.** { *; }
-keep class androidx.autofill.** { *; }
-keep class androidx.biometric.** { *; }
-keep class androidx.core.app.** { *; }
-keep class androidx.core.content.pm.PackageInfoCompat { *; }
-keep class androidx.core.net.** { *; }
-keep class androidx.core.splashscreen.SplashScreen { *; }
-keep class androidx.core.text.** { *; }
-keep class androidx.core.transition.** { *; }
-keep class androidx.core.view.** { *; }
-keep class androidx.core.widget.** { *; }
-keep class androidx.constraintlayout.** { *; }
-keep class androidx.startup.** { *; }
-keep class androidx.security.crypto.** { *; }
-keep class androidx.window.** { *; }
-keep class com.android.internal.policy.** { *; }
-keep class androidx.fragment.** { *; }
-keep class androidx.work.impl.background.** { *; }
-keep class androidx.viewpager2.** { *; }
-keep class androidx.recyclerview.** { *; }
-keep class com.android.internal.policy.** { *; }
-keep class androidx.percentlayout.** { *; }
-keep class androidx.browser.** { *; }
-keep class android.app.** { *; }
-keep class android.view.** { *; }
-keep class android.util.** { *; }
-keep class android.net.** { *; }
-keep class androidx.navigation.** { *; }

# Java
-keep class java.util.** { *; }

If all you want is the specific OneSignal rules

# OneSignal
-keepattributes Signature
-keep class com.onesignal.** { *; }
-keep class com.onesignal.core.** { *; }
-keep class com.onesignal.session.** { *; }
-keep class com.onesignal.user.** { *; }
-keep class com.onesignal.internal.** { *; }
-keep class com.onesignal.debug.** { *; }
-keep class com.onesignal.common.** { *; }
-keep class ** implements com.onesignal.common.modules.IModule { *; }

tbh, this might be enough

# OneSignal
-keepattributes Signature
-keep class com.onesignal.** { *; }
bnoffer commented 1 month ago

Thanks @IainS1986 for this tip. That got my app back in working order.