dotnet / android

.NET for Android provides open-source bindings of the Android SDK for use with .NET managed languages such as C#
MIT License
1.93k stars 529 forks source link

App crashes in runtime when trying to create class instance defined in custom Android Binding Library #7952

Closed anasonov1992 closed 1 year ago

anasonov1992 commented 1 year ago

Android application type

.NET Android (net7.0-android, etc.)

Affected platform version

VS 2022 17.4.5, .NET 7.0.103

Description

We created Android Binding Library project over native third-party .aar file (mobmetricalib-5.3.0.aar), successfully build it in Release configuration and get library DLL (YandexMetrica.Binding.Android.DLL). Here is library .csproj file

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net7.0-android</TargetFramework>
    <IsBindingProject>true</IsBindingProject>
    <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Xamarin.Kotlin.StdLib" Version="1.8.10" />
  </ItemGroup>
  <ItemGroup>
    <AndroidLibrary Include="mobmetricalib-5.3.0.aar">
      <Bind>true</Bind>
      <Pack>true</Pack>
    </AndroidLibrary>
  </ItemGroup>
</Project>

Then we added YandexMetrica.Binding.Android.DLL to our .NET Android app project.

Screenshot 2023-04-12 at 14 02 04

  <ItemGroup>
    <Reference Include="YandexMetricaBinding.Android">
      <HintPath>..\Dependencies\YandexMetricaBinding.Android.dll</HintPath>
    </Reference>
  </ItemGroup>

We run the app project and get runtime crash on app startup when trying to create class instance "com.yandex.metrica.YandexMetricaConfig":

Didn't find class "com.yandex.metrica.YandexMetricaConfig" on path: DexPathList[[zip file "/data/app/~~Lm0T2DYjFpZvzfbMp2z1tw==/com.ILive.Droid-RGemdbm_9-rKS9r5P4-XGQ==/base.apk"],nativeLibraryDirectories=[/data/app/~~Lm0T2DYjFpZvzfbMp2z1tw==/com.ILive.Droid-RGemdbm_9-rKS9r5P4-XGQ==/lib/arm64, /data/app/~~Lm0T2DYjFpZvzfbMp2z1tw==/com.ILive.Droid-RGemdbm_9-rKS9r5P4-XGQ==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]]

YandexMetricaConfig is a class from the library DLL and as decompiler shows the DLL contains it.

Screenshot 2023-04-12 at 14 35 54

But it seems this class is not presented in resulting Dex files.

We try to turn on MultiDex option in the app project but it doesn't help. Also we don't use Code Shrinker or any Proguard configurations.

We suppose it may be some packing problem of Android Binding Library based on .NET 7. We attached an archive with our Android Binding Library project as well. YandexMetricaBinding.Android.zip

Steps to Reproduce

  1. Create Android Binding Library project over native third-party .aar file.
  2. Build the project and get library DLL.
  3. Add the library DLL to Android app project.
  4. Run the app project in Release configuration.
  5. Get crash on app startup when trying to create class instance defined in the library DLL.

Did you find any workaround?

No workaround for now

Relevant log output

---> Java.Lang.ClassNotFoundException: Didn't find class "com.yandex.metrica.YandexMetricaConfig" on path: DexPathList[[zip file "/data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/base.apk"],nativeLibraryDirectories=[/data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/lib/arm64, /data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]]
   at Java.Interop.JniEnvironment.Types.TryFindClass(String classname, Boolean throwOnError) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 97
   at Java.Interop.JniEnvironment.Types.FindClass(String classname) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 36
   at Java.Interop.JniType..ctor(String classname) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniType.cs:line 49
   at Java.Interop.JniType.GetCachedJniType(JniType& cachedType, String classname) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniType.cs:line 104
   at Java.Interop.JniPeerMembers.get_JniPeerType() in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:line 83
   at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String method, String signature) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 58
   at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String encodedMember) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 34
   at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String encodedMember, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 150
   at Com.Yandex.Metrica.YandexMetricaConfig.NewConfigBuilder(String p0) in /Users/ddp/Projects/Vmeste.Mobile/YandexMetricaBinding.Android/YandexMetricaBinding.Android/obj/Release/net7.0-android/generated/src/Com.Yandex.Metrica.YandexMetricaConfig.cs:line 1075
   at ILive.SharedServices.Utilities.AppMetrica.get_Config() in /Users/ddp/Projects/Vmeste.Mobile/ILive/ILive.SharedServices/Utilities/AppMetrica.cs:line 26
   at ILive.Droid.MainApplication.ActivateAppMetrica() in /Users/ddp/Projects/Vmeste.Mobile/ILive/ILive.Droid/MainApplication.cs:line 70
   at ILive.Droid.MainApplication..ctor(IntPtr javaReference, JniHandleOwnership transfer) in /Users/ddp/Projects/Vmeste.Mobile/ILive/ILive.Droid/MainApplication.cs:line 49
   at System.Reflection.ConstructorInvoker.InterpretedInvoke(Object obj, Span`1 args, BindingFlags invokeAttr)
  --- End of managed Java.Lang.ClassNotFoundException stack trace ---
java.lang.ClassNotFoundException: Didn't find class "com.yandex.metrica.YandexMetricaConfig" on path: DexPathList[[zip file "/data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/base.apk"],nativeLibraryDirectories=[/data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/lib/arm64, /data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:207)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    at crc64a135041df264f3e8.MainApplication.n_registerActivityLifecycleCallbacks(Native Method)
    at crc64a135041df264f3e8.MainApplication.registerActivityLifecycleCallbacks(MainApplication.java:25)
    at com.google.android.gms.common.api.internal.BackgroundDetector.initialize(com.google.android.gms:play-services-basement@@18.1.0:3)
    at com.google.firebase.FirebaseApp$GlobalBackgroundStateListener.ensureBackgroundStateListenerRegistered(FirebaseApp.java:698)
    at com.google.firebase.FirebaseApp$GlobalBackgroundStateListener.access$100(FirebaseApp.java:683)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:287)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:271)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:256)
    at com.google.firebase.provider.FirebaseInitProvider.onCreate(FirebaseInitProvider.java:51)
    at android.content.ContentProvider.attachInfo(ContentProvider.java:2411)
    at android.content.ContentProvider.attachInfo(ContentProvider.java:2381)
    at com.google.firebase.provider.FirebaseInitProvider.attachInfo(FirebaseInitProvider.java:45)
    at android.app.ActivityThread.installProvider(ActivityThread.java:7664)
    at android.app.ActivityThread.installContentProviders(ActivityThread.java:7141)
    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7034)
    at android.app.ActivityThread.access$1600(ActivityThread.java:265)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2049)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:236)
    at android.app.ActivityThread.main(ActivityThread.java:8096)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:620)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1011)

  --- End of managed Java.Lang.ClassNotFoundException stack trace ---
java.lang.ClassNotFoundException: Didn't find class "com.yandex.metrica.YandexMetricaConfig" on path: DexPathList[[zip file "/data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/base.apk"],nativeLibraryDirectories=[/data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/lib/arm64, /data/app/~~6wL53POs94VjuuKKNJChiA==/com.ILive.Droid-scTscUdlI_7lyPDevMrj7Q==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:207)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    at crc64a135041df264f3e8.MainApplication.n_registerActivityLifecycleCallbacks(Native Method)
    at crc64a135041df264f3e8.MainApplication.registerActivityLifecycleCallbacks(MainApplication.java:25)
    at com.google.android.gms.common.api.internal.BackgroundDetector.initialize(com.google.android.gms:play-services-basement@@18.1.0:3)
    at com.google.firebase.FirebaseApp$GlobalBackgroundStateListener.ensureBackgroundStateListenerRegistered(FirebaseApp.java:698)
    at com.google.firebase.FirebaseApp$GlobalBackgroundStateListener.access$100(FirebaseApp.java:683)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:287)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:271)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:256)
    at com.google.firebase.provider.FirebaseInitProvider.onCreate(FirebaseInitProvider.java:51)
    at android.content.ContentProvider.attachInfo(ContentProvider.java:2411)
    at android.content.ContentProvider.attachInfo(ContentProvider.java:2381)
    at com.google.firebase.provider.FirebaseInitProvider.attachInfo(FirebaseInitProvider.java:45)
    at android.app.ActivityThread.installProvider(ActivityThread.java:7664)
    at android.app.ActivityThread.installContentProviders(ActivityThread.java:7141)
    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7034)
    at android.app.ActivityThread.access$1600(ActivityThread.java:265)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2049)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:236)
    at android.app.ActivityThread.main(ActivityThread.java:8096)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:620)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1011)
jpobst commented 1 year ago

https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/OneDotNetEmbeddedResources.md isn't very explicit about it, but in .NET 6+ binding projects the .aar is not embedded into the resulting .dll like it was in Xamarin.Android classic. This means that the user is responsible for providing both the .aar and the .dll to the consuming application.

For a <ProjectReference> and a <PackageReference> this is done for you, however for a regular <Reference>, you are just specifying an arbitrary file location and there is no way to automatically find the .aar unless it is next to the .dll.

That is, your file system should look like this:

- \Dependencies
  - YandexMetricaBinding.Android.dll
  - mobmetricalib-5.3.0.aar

When it pulls in the .dll, it will pull in any .aar files that are in the same directory.

ghost commented 1 year ago

Hi @anasonov1992. We have added the "need-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

anasonov1992 commented 1 year ago

@jpobst thank you very much! Your solution is working.