You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reduce or remove reflection use around JNI method registration, and
rework how uncaught exceptions are handled.
Java.Interop provides an alternate mechanism to allow registering
Java native methods:
[the `[JniAddNativeMethodRegistrationAttribute]` custom attribute][0]
which can be placed on methods of a type to allow "manual"
insertion into the [`JNIEnv::RegisterNatives()`][2] process.
The `Java.Interop-Tests.dll` unit test assembly (130905e) uses this
alternate native method registration mechanism..
Unfortunately, `[JniAddNativeMethodRegistration]` is looked up via
Reflection, which isn't exactly "fast".
Given that `[JniAddNativeMethodRegistration]` is only used by one
assembly *in the world*, let's optimize the common case:
[dotnet/java-interop@b33d183d][3] made the
`[JniAddNativeMethodRegistration]` lookup *optional*. Disable the
custom attribute lookup *by default*, and allow it to be enabled by
setting the `$(_SkipJniAddNativeMethodRegistrationAttributeScan)`
MSBuild property to True. Yes, this property is "private". We'll
investigate more automatic and optimized ideas in the future.
Rework how Java-side unhandled exceptions are processed. Previously,
this was handled by the `Android.Runtime.UncaughtExceptionHandler`
class, which was constructed during process startup. A side effect
of this is that it required going through the (*normal* reflection-
based) JNI registration logic of `mono.android.Runtime.register()`,
contributing to startup for something that *ideally* would
Never Happen™. (Nobody likes unhandled exceptions.)
Changes this so that instead of a C#-side `UncaughtExceptionHandler`
type we instead have a Java-side
`mono.android.XamarinUncaughtExceptionHandler` type which is
created and provided to
`java.lang.Thread.setDefaultUncaughtExceptionHandler()` during
startup. `XamarinUncaughtExceptionHandler` doesn't do anything until
Java calls `XamarinUncaughtExceptionHandler.uncaughtException()`,
which in turn invokes `Runtime.propagateUncaughtException()`, which
in turn does what `UncaughtExceptionHandler` previously did: lookup
`AppDomain.DoUnhandledException()` via Reflection and invoke it, thus
raising the `AppDomain.UnhandledException` event.
In this manner all overheads associated with unhandled exceptions are
delayed until the exception happens, with minimal impact on startup.
Gains in startup time are quite nice:
* Device name: **Pixel 3 XL**
* Device architecture: **arm64-v8a**
* Number of test runs: **10**
* Test application: **Xamarin.Forms integration test**
| | **Native to managed** | **Runtime init** | **Displayed** | **Notes** |
|-----------------|------------------------|------------------|---------------|--------------------------------|
| **master** | 131.278 | 149.074 | 789.10 | preload enabled; 32-bit build |
| **this commit** | 49.446 | 66.989 | 764.30 | |
| **master** | 132.315 | 147.187 | 795.60 | preload disabled; 32-bit build |
| **this commit** | 48.876 | 63.822 | 754.30 | |
| **master** | 121.544 | 137.736 | 728.20 | preload enabled; 64-bit build |
| **this commit** | 45.350 | 61.464 | 699.50 | |
| **master** | 123.448 | 137.052 | 727.40 | preload disabled; 64-bit build |
| **this commit** | 44.765 | 58.047 | 689.00 | |
* Device name: **Pixel 3 XL**
* Device architecture: **arm64-v8a**
* Number of test runs: **10**
* Test application: Xamarin.Forms "Hello World" app with one label
| | **Native to managed** | **Runtime init** | **Displayed** | **Notes** |
|-----------------|------------------------|------------------|---------------|--------------------------------|
| **master** | 122.090 | 142.004 | 639.00 | preload enabled; 32-bit build |
| **this commit** | 44.370 | 63.803 | 586.10 | |
| **master** | 121.110 | 134.378 | 634.20 | preload disabled; 32-bit build |
| **this commit** | 45.085 | 57.992 | 580.40 | |
| **master** | 120.973 | 141.235 | 637.20 | preload enabled; 64-bit build |
| **this commit** | 44.767 | 63.846 | 578.50 | |
| **master** | 120.785 | 134.588 | 627.00 | preload disabled; 64-bit build |
| **this commit** | 44.859 | 57.590 | 575.40 | |
[0]: dotnet/java-interop@7d51163
[2]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives
[3]: dotnet/java-interop@b33d183
0 commit comments