AbstractMethodError: abstract method "android.util.Size androidx.camera.core.ImageAnalysis$Analyzer.getDefaultTargetResolution()"
Android application type
Android for .NET (net6.0-android, etc.)
Affected platform version
VS2022 17.6.5
Description
With the latest version of CameraX we are running into an exception when inheriting from ImageAnalysis.IAnalyzer:
JNI DETECTED ERROR IN APPLICATION: JNI CallObjectMethodA called with pending exception java.lang.AbstractMethodError: abstract method "android.util.Size androidx.camera.core.ImageAnalysis$Analyzer.getDefaultTargetResolution()"
at android.util.Size crc64c8b333e758042baf.MainActivity_MyAnalyzer.n_getDefaultTargetResolution() (MainActivity_MyAnalyzer.java:-2)
at android.util.Size crc64c8b333e758042baf.MainActivity_MyAnalyzer.getDefaultTargetResolution() (MainActivity_MyAnalyzer.java:34)
at androidx.camera.core.impl.UseCaseConfig androidx.camera.core.ImageAnalysis.onMergeConfig(androidx.camera.core.impl.CameraInfoInternal, androidx.camera.core.impl.UseCaseConfig$Builder) (ImageAnalysis.java:277)
at androidx.camera.core.impl.UseCaseConfig androidx.camera.core.UseCase.mergeConfigs(androidx.camera.core.impl.CameraInfoInternal, androidx.camera.core.impl.UseCaseConfig, androidx.camera.core.impl.UseCaseConfig) (UseCase.java:277)
at java.util.Map androidx.camera.core.internal.CameraUseCaseAdapter.calculateSuggestedStreamSpecs(int, androidx.camera.core.impl.CameraInfoInternal, java.util.Collection, java.util.Collection, java.util.Map) (CameraUseCaseAdapter.java:682)
We are not calling getDefaultTargetResolution() directly, just passing a ImageAnalysis.IAnalyzer object to the library.
Steps to Reproduce
Minimum reproducible app: AndroidApp7.zip
Tested & affected versions for: Xamarin.AndroidX.Camera.Camera2/Xamarin.AndroidX.Camera.Lifecycle: 1.2.3.1, 1.2.2
Did you find any workaround?
Revert back Xamarin.AndroidX.Camera.* back to 1.2.1
Relevant log output
No response
same issue
Adding DefaultTargetResolution property to the class being passed to SetAnalyzer method seems solving the issue:
imageAnalysis.SetAnalyzer(ContextCompat.GetMainExecutor(Context), this);
...
public Android.Util.Size DefaultTargetResolution => new Android.Util.Size(200, 200);
Adding
DefaultTargetResolutionproperty to the class being passed toSetAnalyzermethod seems solving the issue:
imageAnalysis.SetAnalyzer(ContextCompat.GetMainExecutor(Context), this);...public Android.Util.Size DefaultTargetResolution => new Android.Util.Size(200, 200);
Thank you.
Investigation notes (net8.0-android):
getDefaultTargetResolution is a default interface method on the Analyzer interface, it gets bound like this:
[Register("androidx/camera/core/ImageAnalysis$Analyzer", "", "AndroidX.Camera.Core.ImageAnalysis/IAnalyzerInvoker")]
public interface IAnalyzer : IJavaObject, IDisposable, IJavaPeerable {
unsafe Size? DefaultTargetResolution
{
[Register("getDefaultTargetResolution", "()Landroid/util/Size;", "GetGetDefaultTargetResolutionHandler:AndroidX.Camera.Core.ImageAnalysis/IAnalyzer, Xamarin.AndroidX.Camera.Core")]
get {
return Java.Lang.Object.GetObject<Size>(_members.InstanceMethods.InvokeVirtualObjectMethod("getDefaultTargetResolution.()Landroid/util/Size;", this, null).Handle, JniHandleOwnership.TransferLocalRef);
}
}
private static Delegate? cb_getDefaultTargetResolution;
private static Delegate GetGetDefaultTargetResolutionHandler()
{
if ((object)cb_getDefaultTargetResolution == null)
{
cb_getDefaultTargetResolution = JNINativeWrapper.CreateDelegate(new global::_JniMarshal_PP_L(n_GetDefaultTargetResolution));
}
return cb_getDefaultTargetResolution;
}
private static nint n_GetDefaultTargetResolution(nint jnienv, nint native__this)
{
return JNIEnv.ToLocalJniHandle(Java.Lang.Object.GetObject<IAnalyzer>(jnienv, native__this, JniHandleOwnership.DoNotTransfer).DefaultTargetResolution);
}
}
This appears to be an issue with Java calling a default interface method where the C# type has not provided a non-default method implementation.
Providing a non-default method implementation in C# fixes the crash.
As per discussion on Discord…
TL;DR: The problem is Desugaring. (TODO: links?)
Before API-24, in order to support some Java 8 features such as default interface methods and interface static methods, the Dalvik code had to be "desugared", which resulted in moving various methods into new $Foo-suffixed types. See also e.g.:
- https://github.com/dotnet/java-interop/commit/1f27ab552d03aeb74cdc6f8985fcffbfdb9a7ddf
- https://github.com/dotnet/java-interop/commit/bbaeda6f698369bdd48bfc2dd1a32a41c9d23229
The problem here is with default interface methods, and our binding tries to non-virtually invoke the interface method.
In a Desugared "environment", the default interface method body is not in the interface; it's "elsewhere". The interface does have an abstract method for the (former) default interface method, which we then try to non virtually invoke, which fails.
The Workaround: Set your minimum supported API level to API-26 or later. This means Desugaring isn't needed, and things work.
The Fix? (lol): Update dotnet/java-interop to support desugared default interface methods. (There is no current timeline for this.)
@jpobst: how easy or plausible would it be to:
- Detect if a given binding contains a default interface method or interface static method, and
- When found, include an
AndroidManifest.xmlwithin the binding that setsandroid:minSdkVersionto 26?
Setting the minimum to API-26 will prevent desugaring, thus avoiding this scenario.
am facing the same problem each time i add Plugin.maui.ocr, am new to programing how can i fix or add the suggested or mention solution?
Change the
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
in your .csproj file to:
<SupportedOSPlatformVersion>24</SupportedOSPlatformVersion>
(or add it with 24 if it doesn't exist.)
@jpobst thank you