Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[regression/8.0.3] Android - Cannot create instance of type, no Java peer type found #18819

Closed
kernshen opened this issue Nov 17, 2023 · 6 comments · Fixed by dotnet/android#8904
Labels
partner/android Issues for the Android SDK platform/android 🤖 potential-regression This issue described a possible regression on a currently supported version., verification pending s/needs-attention Issue has more information and needs another look s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working

Comments

@kernshen
Copy link

Description

Encountered this while upgrading my project to .Net Maui 8.

The error occurs when you have a class library with a class that derives from a base class inheriting from Java.Lang.Object in another class library.

Steps to Reproduce

  1. Create a .NET Maui 8 class library with a base class inheriting from Java.Lang.Object
    MauiLib1
  • BaseClass
  • DerivedClass1 : No error when creating an instance
  1. Create a .NET Maui 8 mobile app with a derived class of the base class in step 1.
    MauiApp1
  • DerivedClass2 : No error when creating an instance
  1. Create a .Net Maui 8 class library with a derived class of the base class in step 1.
    MauiLib2
  • DerivedClass3
  1. Create an instance of all 3 derived class in mobile app in step 2.
    Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.

Link to public reproduction project repository

https://github.com/kernshen/NoJavaPeer.git

Version with bug

8.0.3

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

7.0.49

Affected platforms

Android

Affected platform versions

No response

Did you find any workaround?

Move derived class from class library to MauiApp project.

Relevant log output

Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.

 at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 22
   at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
   at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 139
   at Java.Lang.Object..ctor() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.Object.cs:line 37
   at MauiLib1.BaseClass..ctor()
   at MauiLib2.DerivedClass..ctor() in C:\Project\Maui\MauiApp1\MauiLib2\Platforms\Android\DerivedClass.cs:line 7
   at MauiApp1.App..ctor(IServiceProvider serviceProvider) in C:\Project\Maui\MauiApp1\MauiApp1\App.xaml.cs:line 18
@kernshen kernshen added the t/bug Something isn't working label Nov 17, 2023
@drasticactions
Copy link
Contributor

Could you try reproducing this in a .NET Android app without the MAUI UI tooling? (dotnet new android)

This feels like something that would fail regardless of the MAUI UI Framework, if it fails in a .NET Android app then this should probably be moved to xamarin-android.

@drasticactions drasticactions added the s/needs-info Issue needs more info from the author label Nov 17, 2023
@ghost
Copy link

ghost commented Nov 17, 2023

Hi @kernshen. We have added the "s/needs-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.

@kernshen
Copy link
Author

@drasticactions, indeed it fails in a .NET Android app (added to repos).

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels Nov 18, 2023
@drasticactions
Copy link
Contributor

@jonathanpeppers Do you feel this is a xamarin-android issue? Could we please move it there if it is?

@samhouts samhouts added the potential-regression This issue described a possible regression on a currently supported version., verification pending label Dec 6, 2023
@samhouts samhouts changed the title .NET MAUI 8 Android - Cannot create instance of type, no Java peer type found [regression/8.0.3] Android - Cannot create instance of type, no Java peer type found Dec 7, 2023
@samhouts samhouts added the partner/android Issues for the Android SDK label Feb 2, 2024
@Zhanglirong-Winnie Zhanglirong-Winnie added s/verified Verified / Reproducible Issue ready for Engineering Triage s/triaged Issue has been reviewed labels Mar 12, 2024
@Zhanglirong-Winnie
Copy link

Zhanglirong-Winnie commented Mar 12, 2024

Verified this issue with Visual Studio 17.10.0 Preview 1. Can repro on Android platform with sample project.
https://github.com/kernshen/NoJavaPeer.git
image

@jonathanpeppers
Copy link
Member

As a workaround for this one, you can add a method that isn't called:

namespace MauiLib2
{
    public class DerivedClass3 : BaseClass
    {
        static Type FixMe() => typeof(Android.Views.View);

This causes MauiLib2.dll to have a reference to Mono.Android.dll and everything works.

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Apr 25, 2024
Fixes: dotnet/maui#18819

The sample creates a problem such as:

* Create a .NET Maui 8 class library with a base class inheriting from
  Java.Lang.Object: `MauiLib1`
  * `BaseClass`
    * `DerivedClass1` : No error when creating an instance

* Create a .NET Maui 8 mobile app with a derived class of the base
  class in step 1: `MauiApp1`
    * `DerivedClass2` : No error when creating an instance

* Create a .Net Maui 8 class library with a derived class of the base
  class in step 1: `MauiLib2`
    * `DerivedClass3`
    * Create an instance of all 3 derived class in mobile app in step
      2.
    * `NotSupportedException` creating `DerivedClass3`

    Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.
    at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 22
    at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
    at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 139
    at Java.Lang.Object..ctor() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.Object.cs:line 37
    at MauiLib1.BaseClass..ctor()
    at MauiLib2.DerivedClass..ctor() in C:\Project\Maui\MauiApp1\MauiLib2\Platforms\Android\DerivedClass.cs:line 7
    at MauiApp1.App..ctor(IServiceProvider serviceProvider) in C:\Project\Maui\MauiApp1\MauiApp1\App.xaml.cs:line 18

I could reproduce this problem in an existing `ProjectDependencies()`
test, by making some existing classes `Java.Lang.Object` and asserting
their JCWs are in the final `classes.dex` file.

The fix here appears to be our detection of "Android libraries" in:

    var tfi = assembly.GetMetadata ("TargetFrameworkIdentifier");
    if (string.Compare (tfi, "MonoAndroid", StringComparison.OrdinalIgnoreCase) == 0)
        return true;

This looks like it would only support Xamarin.Android class libraries,
as the metadata says:

    TargetPlatformIdentifier = Android

The only reason this works *at all*, is because we also look for
`Mono.Android.dll` assembly references: which is more of a fallback.

Update the code to look for `Android` in the
`TargetPlatformIdentifier`, and use `string.IndexOf()` for both
checks.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Apr 25, 2024
Fixes: dotnet/maui#18819

The sample creates a problem such as:

* Create a .NET Maui 8 class library with a base class inheriting from
  Java.Lang.Object: `MauiLib1`
  * `BaseClass`
    * `DerivedClass1` : No error when creating an instance

* Create a .NET Maui 8 mobile app with a derived class of the base
  class in step 1: `MauiApp1`
    * `DerivedClass2` : No error when creating an instance

* Create a .Net Maui 8 class library with a derived class of the base
  class in step 1: `MauiLib2`
    * `DerivedClass3`
    * Create an instance of all 3 derived class in mobile app in step
      2.
    * `NotSupportedException` creating `DerivedClass3`

    Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.
    at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 22
    at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
    at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 139
    at Java.Lang.Object..ctor() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.Object.cs:line 37
    at MauiLib1.BaseClass..ctor()
    at MauiLib2.DerivedClass..ctor() in C:\Project\Maui\MauiApp1\MauiLib2\Platforms\Android\DerivedClass.cs:line 7
    at MauiApp1.App..ctor(IServiceProvider serviceProvider) in C:\Project\Maui\MauiApp1\MauiApp1\App.xaml.cs:line 18

I could reproduce this problem in an existing `ProjectDependencies()`
test, by making some existing classes `Java.Lang.Object` and asserting
their JCWs are in the final `classes.dex` file.

The fix here appears to be our detection of "Android libraries" in:

    var tfi = assembly.GetMetadata ("TargetFrameworkIdentifier");
    if (string.Compare (tfi, "MonoAndroid", StringComparison.OrdinalIgnoreCase) == 0)
        return true;

This looks like it would only support Xamarin.Android class libraries,
as the metadata says:

    TargetPlatformIdentifier = Android

The only reason this works *at all*, is because we also look for
`Mono.Android.dll` assembly references: which is more of a fallback.

Update the code to look for `Android` in the
`TargetPlatformIdentifier`, and use `string.IndexOf()` for both
checks.
jonpryor pushed a commit to dotnet/android that referenced this issue Apr 26, 2024
…8904)

Fixes: dotnet/maui#18819

Context: https://github.com/kernshen/NoJavaPeer.git
Context: #4225 (comment)

An assembly's assembly references do not include transitive
dependencies.  Given:

	// Mono.Android.dll
	namespace Java.Lang {
	    public partial class Object {}
	}

	// MauiLib1.dll
	namespace MauiLib1 {
	    public class BaseClass : Java.Lang.Object {}
	}

	// MauiLib2.dll
	namespace MauiLib2 {
	    public class DerivedClass3 : MauiLib1.BaseClass {}
	}

then the assembly references for `MauiLib1.dll` will include
`Mono.Android.dll` (it directly references a type from it),
while the assembly references for `MauiLib2.dll` will include
`MauiLib1.dll` (it directly references a type from it) *but*
`MauiLib2.dll` *will not* have an assembly reference to
`Mono.Android.dll`.

This is how things have worked since .NET Framework 1.0.  This should
not be surprising.

[As part of the .NET for Android][0] [`SignAndroidPackage`][1] target,
Java Callable Wrappers (JCWs) need to be emitted for all
`Java.Lang.Object` subclasses.  This in turn requires
*loading all assemblies* to *find* the `Java.Lang.Object` subclasses.
As a performance optimization, we only load assemblies which we
believed could contain `Java.Lang.Object` subclasses:

 1. Assemblies with `'%(TargetFrameworkIdentifier)' == 'MonoAndroid'`,
    which is "carry over" from how Xamarin.Android did things,
    and works if a .NET for Android project references a
    Xamarin.Android project.

 2. Assemblies with an assembly reference to `Mono.Android.dll`.

Assemblies with transitive dependencies were caught by (1)…
in Xamarin.Android.

With .NET for Android, that is no longer the case:
`%(TargetFrameworkIdentifier)` is now always `.NETCoreApp`.  This in
turn meant that the only assemblies that could be used to generate
JCWs were those which directly referenced `Mono.Android.dll`!

Enter dotnet/maui#18819 and kernshen/NoJavaPeer, which contains
MAUI and .NET for Android solutions with the above transitive reference
structure:

 1. `MauiLib1.dll` / `AndroidLib1.dll` references `Mono.Android.dll`,
    exports `BaseClass`

 2. `MauiLib2.dll` / `AndroidLib2.dll` references `*Lib1.dll` *and not*
    `Mono.Android.dll`; exports `DerivedClass3` which inherits `BaseClass`.

 3. App project attempts to instantiate `DerivedClass3`.

The result: a runtime exception:

	Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.
	   at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 22
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 139
	   at Java.Lang.Object..ctor() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.Object.cs:line 37
	   at MauiLib1.BaseClass..ctor()
	   at MauiLib2.DerivedClass..ctor() in C:\Project\Maui\MauiApp1\MauiLib2\Platforms\Android\DerivedClass.cs:line 7
	   at MauiApp1.App..ctor(IServiceProvider serviceProvider) in C:\Project\Maui\MauiApp1\MauiApp1\App.xaml.cs:line 18

The exception occurs because there is no JCW for `DerivedClass3`, and
there isn't a JCW for `DerivedClass3` because `MauiLib2.dll` was not
processed at all, because it had no assembly reference to
`Mono.Android.dll`.

As a workaround, update `MauiLib2.dll` to contain an assembly reference
to `Mono.Android.dll`.

*Fix* this scenario by updating
`MonoAndroidHelper.IsMonoAndroidAssembly()` to consider these to be
.NET for Android assemblies:

 1. Assemblies with `%(TargetFrameworkIdentifier)` *containing*
   `Android`.  (This doesn't actually change anything; it's a
    simplification.)

 2. Assemblies with `%(TargetPlatformIdentifier)` *containing*
    `Android`.

    *This* causes `MauiLib2.dll` to be treated as a .NET for Android
    assembly, fixing the bug.

 3. Assemblies with an assembly reference to `Mono.Android.dll`.

The addition of check (2) allows assemblies with only transitive
(non-) references to `Mono.Android.dll` to be properly considered,
allowing JCWs to be emitted for types within them.

Update the `BuildWithLibraryTests.ProjectDependencies()` unit test to
better check for this scenario.

[0]: https://github.com/xamarin/xamarin-android/wiki/Blueprint#after-build
[1]: https://learn.microsoft.com/en-us/dotnet/android/building-apps/build-targets#signandroidpackage
jonathanpeppers added a commit to dotnet/android that referenced this issue Apr 26, 2024
…8904)

Fixes: dotnet/maui#18819

Context: https://github.com/kernshen/NoJavaPeer.git
Context: #4225 (comment)

An assembly's assembly references do not include transitive
dependencies.  Given:

	// Mono.Android.dll
	namespace Java.Lang {
	    public partial class Object {}
	}

	// MauiLib1.dll
	namespace MauiLib1 {
	    public class BaseClass : Java.Lang.Object {}
	}

	// MauiLib2.dll
	namespace MauiLib2 {
	    public class DerivedClass3 : MauiLib1.BaseClass {}
	}

then the assembly references for `MauiLib1.dll` will include
`Mono.Android.dll` (it directly references a type from it),
while the assembly references for `MauiLib2.dll` will include
`MauiLib1.dll` (it directly references a type from it) *but*
`MauiLib2.dll` *will not* have an assembly reference to
`Mono.Android.dll`.

This is how things have worked since .NET Framework 1.0.  This should
not be surprising.

[As part of the .NET for Android][0] [`SignAndroidPackage`][1] target,
Java Callable Wrappers (JCWs) need to be emitted for all
`Java.Lang.Object` subclasses.  This in turn requires
*loading all assemblies* to *find* the `Java.Lang.Object` subclasses.
As a performance optimization, we only load assemblies which we
believed could contain `Java.Lang.Object` subclasses:

 1. Assemblies with `'%(TargetFrameworkIdentifier)' == 'MonoAndroid'`,
    which is "carry over" from how Xamarin.Android did things,
    and works if a .NET for Android project references a
    Xamarin.Android project.

 2. Assemblies with an assembly reference to `Mono.Android.dll`.

Assemblies with transitive dependencies were caught by (1)…
in Xamarin.Android.

With .NET for Android, that is no longer the case:
`%(TargetFrameworkIdentifier)` is now always `.NETCoreApp`.  This in
turn meant that the only assemblies that could be used to generate
JCWs were those which directly referenced `Mono.Android.dll`!

Enter dotnet/maui#18819 and kernshen/NoJavaPeer, which contains
MAUI and .NET for Android solutions with the above transitive reference
structure:

 1. `MauiLib1.dll` / `AndroidLib1.dll` references `Mono.Android.dll`,
    exports `BaseClass`

 2. `MauiLib2.dll` / `AndroidLib2.dll` references `*Lib1.dll` *and not*
    `Mono.Android.dll`; exports `DerivedClass3` which inherits `BaseClass`.

 3. App project attempts to instantiate `DerivedClass3`.

The result: a runtime exception:

	Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.
	   at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 22
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 139
	   at Java.Lang.Object..ctor() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.Object.cs:line 37
	   at MauiLib1.BaseClass..ctor()
	   at MauiLib2.DerivedClass..ctor() in C:\Project\Maui\MauiApp1\MauiLib2\Platforms\Android\DerivedClass.cs:line 7
	   at MauiApp1.App..ctor(IServiceProvider serviceProvider) in C:\Project\Maui\MauiApp1\MauiApp1\App.xaml.cs:line 18

The exception occurs because there is no JCW for `DerivedClass3`, and
there isn't a JCW for `DerivedClass3` because `MauiLib2.dll` was not
processed at all, because it had no assembly reference to
`Mono.Android.dll`.

As a workaround, update `MauiLib2.dll` to contain an assembly reference
to `Mono.Android.dll`.

*Fix* this scenario by updating
`MonoAndroidHelper.IsMonoAndroidAssembly()` to consider these to be
.NET for Android assemblies:

 1. Assemblies with `%(TargetFrameworkIdentifier)` *containing*
   `Android`.  (This doesn't actually change anything; it's a
    simplification.)

 2. Assemblies with `%(TargetPlatformIdentifier)` *containing*
    `Android`.

    *This* causes `MauiLib2.dll` to be treated as a .NET for Android
    assembly, fixing the bug.

 3. Assemblies with an assembly reference to `Mono.Android.dll`.

The addition of check (2) allows assemblies with only transitive
(non-) references to `Mono.Android.dll` to be properly considered,
allowing JCWs to be emitted for types within them.

Update the `BuildWithLibraryTests.ProjectDependencies()` unit test to
better check for this scenario.

[0]: https://github.com/xamarin/xamarin-android/wiki/Blueprint#after-build
[1]: https://learn.microsoft.com/en-us/dotnet/android/building-apps/build-targets#signandroidpackage
jonathanpeppers added a commit to dotnet/android that referenced this issue Apr 29, 2024
…8904)

Fixes: dotnet/maui#18819

Context: https://github.com/kernshen/NoJavaPeer.git
Context: #4225 (comment)

An assembly's assembly references do not include transitive
dependencies.  Given:

	// Mono.Android.dll
	namespace Java.Lang {
	    public partial class Object {}
	}

	// MauiLib1.dll
	namespace MauiLib1 {
	    public class BaseClass : Java.Lang.Object {}
	}

	// MauiLib2.dll
	namespace MauiLib2 {
	    public class DerivedClass3 : MauiLib1.BaseClass {}
	}

then the assembly references for `MauiLib1.dll` will include
`Mono.Android.dll` (it directly references a type from it),
while the assembly references for `MauiLib2.dll` will include
`MauiLib1.dll` (it directly references a type from it) *but*
`MauiLib2.dll` *will not* have an assembly reference to
`Mono.Android.dll`.

This is how things have worked since .NET Framework 1.0.  This should
not be surprising.

[As part of the .NET for Android][0] [`SignAndroidPackage`][1] target,
Java Callable Wrappers (JCWs) need to be emitted for all
`Java.Lang.Object` subclasses.  This in turn requires
*loading all assemblies* to *find* the `Java.Lang.Object` subclasses.
As a performance optimization, we only load assemblies which we
believed could contain `Java.Lang.Object` subclasses:

 1. Assemblies with `'%(TargetFrameworkIdentifier)' == 'MonoAndroid'`,
    which is "carry over" from how Xamarin.Android did things,
    and works if a .NET for Android project references a
    Xamarin.Android project.

 2. Assemblies with an assembly reference to `Mono.Android.dll`.

Assemblies with transitive dependencies were caught by (1)…
in Xamarin.Android.

With .NET for Android, that is no longer the case:
`%(TargetFrameworkIdentifier)` is now always `.NETCoreApp`.  This in
turn meant that the only assemblies that could be used to generate
JCWs were those which directly referenced `Mono.Android.dll`!

Enter dotnet/maui#18819 and kernshen/NoJavaPeer, which contains
MAUI and .NET for Android solutions with the above transitive reference
structure:

 1. `MauiLib1.dll` / `AndroidLib1.dll` references `Mono.Android.dll`,
    exports `BaseClass`

 2. `MauiLib2.dll` / `AndroidLib2.dll` references `*Lib1.dll` *and not*
    `Mono.Android.dll`; exports `DerivedClass3` which inherits `BaseClass`.

 3. App project attempts to instantiate `DerivedClass3`.

The result: a runtime exception:

	Only System.NotSupportedException Message=Cannot create instance of type 'MauiLib2.DerivedClass3': no Java peer type found.
	   at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 22
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 139
	   at Java.Lang.Object..ctor() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.Object.cs:line 37
	   at MauiLib1.BaseClass..ctor()
	   at MauiLib2.DerivedClass..ctor() in C:\Project\Maui\MauiApp1\MauiLib2\Platforms\Android\DerivedClass.cs:line 7
	   at MauiApp1.App..ctor(IServiceProvider serviceProvider) in C:\Project\Maui\MauiApp1\MauiApp1\App.xaml.cs:line 18

The exception occurs because there is no JCW for `DerivedClass3`, and
there isn't a JCW for `DerivedClass3` because `MauiLib2.dll` was not
processed at all, because it had no assembly reference to
`Mono.Android.dll`.

As a workaround, update `MauiLib2.dll` to contain an assembly reference
to `Mono.Android.dll`.

*Fix* this scenario by updating
`MonoAndroidHelper.IsMonoAndroidAssembly()` to consider these to be
.NET for Android assemblies:

 1. Assemblies with `%(TargetFrameworkIdentifier)` *containing*
   `Android`.  (This doesn't actually change anything; it's a
    simplification.)

 2. Assemblies with `%(TargetPlatformIdentifier)` *containing*
    `Android`.

    *This* causes `MauiLib2.dll` to be treated as a .NET for Android
    assembly, fixing the bug.

 3. Assemblies with an assembly reference to `Mono.Android.dll`.

The addition of check (2) allows assemblies with only transitive
(non-) references to `Mono.Android.dll` to be properly considered,
allowing JCWs to be emitted for types within them.

Update the `BuildWithLibraryTests.ProjectDependencies()` unit test to
better check for this scenario.

[0]: https://github.com/xamarin/xamarin-android/wiki/Blueprint#after-build
[1]: https://learn.microsoft.com/en-us/dotnet/android/building-apps/build-targets#signandroidpackage
@github-actions github-actions bot locked and limited conversation to collaborators May 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
partner/android Issues for the Android SDK platform/android 🤖 potential-regression This issue described a possible regression on a currently supported version., verification pending s/needs-attention Issue has more information and needs another look s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants