Browse Source

Apply optimizations in Avalonia Android (#20968)

* Apply optimizations around Avalonia Android

* More changes

* Move benchmarks to run on Android

* Remove Android Benchmark from build

* More changes

* Removed Android Benchmarks project
pull/21048/head
Javier Suárez 7 months ago
committed by GitHub
parent
commit
3edf20b931
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 21
      src/Android/Avalonia.Android/AndroidDispatcherImpl.cs
  2. 17
      src/Android/Avalonia.Android/AvaloniaAccessHelper.cs
  3. 16
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  4. 25
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

21
src/Android/Avalonia.Android/AndroidDispatcherImpl.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using Android.OS; using Android.OS;
using Avalonia.Controls.Documents; using Avalonia.Controls.Documents;
using Avalonia.Threading; using Avalonia.Threading;
@ -19,8 +20,7 @@ namespace Avalonia.Android
private readonly Runnable _timerSignaler; private readonly Runnable _timerSignaler;
private readonly Runnable _wakeupSignaler; private readonly Runnable _wakeupSignaler;
private readonly MessageQueue _queue; private readonly MessageQueue _queue;
private readonly object _lock = new(); private int _signaled;
private bool _signaled;
private bool _backgroundProcessingRequested; private bool _backgroundProcessingRequested;
@ -46,8 +46,7 @@ namespace Avalonia.Android
public event Action? Signaled; public event Action? Signaled;
private void OnSignaled() private void OnSignaled()
{ {
lock (_lock) Interlocked.Exchange(ref _signaled, 0);
_signaled = false;
Signaled?.Invoke(); Signaled?.Invoke();
} }
@ -68,13 +67,11 @@ namespace Avalonia.Android
public void Signal() public void Signal()
{ {
lock (_lock) if (Interlocked.CompareExchange(ref _signaled, 1, 0) != 0)
{ {
if(_signaled) return;
return;
_signaled = true;
_handler.Post(_signaler);
} }
_handler.Post(_signaler);
} }
readonly Stopwatch _clock = Stopwatch.StartNew(); readonly Stopwatch _clock = Stopwatch.StartNew();
@ -133,11 +130,9 @@ namespace Avalonia.Android
// "background" jobs not being processed // "background" jobs not being processed
// So we need to examine the queue state to prevent that scenario // So we need to examine the queue state to prevent that scenario
lock (_lock) if (Volatile.Read(ref _signaled) != 0)
{ {
// There are higher priority jobs enqueued, we'll be called again return;
if (_signaled)
return;
} }
if (CanQueryPendingInput) if (CanQueryPendingInput)

17
src/Android/Avalonia.Android/AvaloniaAccessHelper.cs

@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Android.OS; using Android.OS;
using AndroidX.Core.View.Accessibility; using AndroidX.Core.View.Accessibility;
using AndroidX.CustomView.Widget; using AndroidX.CustomView.Widget;
@ -134,9 +134,18 @@ namespace Avalonia.Android
protected override bool OnPerformActionForVirtualView(int virtualViewId, int action, Bundle? arguments) protected override bool OnPerformActionForVirtualView(int virtualViewId, int action, Bundle? arguments)
{ {
return (GetNodeInfoProvidersFromVirtualViewId(virtualViewId) ?? []) var providers = GetNodeInfoProvidersFromVirtualViewId(virtualViewId);
.Select(x => TryPerformNodeAction(x, action, arguments)) if (providers == null)
.Aggregate(false, (a, b) => a | b); {
return false;
}
var result = false;
foreach (var provider in providers)
{
result |= TryPerformNodeAction(provider, action, arguments);
}
return result;
} }
private static bool TryPerformNodeAction(INodeInfoProvider nodeInfoProvider, int action, Bundle? arguments) private static bool TryPerformNodeAction(INodeInfoProvider nodeInfoProvider, int action, Bundle? arguments)

16
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -4,6 +4,7 @@ using Android.App;
using Android.Content; using Android.Content;
using Android.Graphics; using Android.Graphics;
using Android.Graphics.Drawables; using Android.Graphics.Drawables;
using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
using AndroidX.AppCompat.App; using AndroidX.AppCompat.App;
@ -144,6 +145,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
private readonly TopLevelImpl _tl; private readonly TopLevelImpl _tl;
private Size _oldSize; private Size _oldSize;
private double _oldScaling; private double _oldScaling;
private Paint? _clearPaint;
public SurfaceViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context) public SurfaceViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context)
{ {
@ -159,11 +161,13 @@ namespace Avalonia.Android.Platform.SkiaPlatform
// can be seen below, but it does not. // can be seen below, but it does not.
if (OperatingSystem.IsAndroidVersionAtLeast(29)) if (OperatingSystem.IsAndroidVersionAtLeast(29))
{ {
// Android 10+ does this (BlendMode was new) if (_clearPaint == null)
var paint = new Paint(); {
paint.SetColor(0); _clearPaint = new Paint();
paint.BlendMode = BlendMode.Clear; _clearPaint.SetColor(0);
canvas.DrawRect(0, 0, Width, Height, paint); _clearPaint.BlendMode = BlendMode.Clear;
}
canvas.DrawRect(0, 0, Width, Height, _clearPaint);
} }
else else
{ {
@ -384,7 +388,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
if(Input != null) if(Input != null)
{ {
var args = new RawTextInputEventArgs(AndroidKeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, InputRoot!, text); var args = new RawTextInputEventArgs(AndroidKeyboardDevice.Instance!, (ulong)SystemClock.UptimeMillis(), InputRoot!, text);
Input(args); Input(args);
} }

25
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -10,6 +10,18 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{ {
internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl
{ {
private static readonly string[] s_asciiStringCache = InitAsciiStringCache();
private static string[] InitAsciiStringCache()
{
var cache = new string[128];
for (int i = 0; i < 128; i++)
{
cache[i] = ((char)i).ToString();
}
return cache;
}
private readonly TView _view; private readonly TView _view;
public bool HandleEvents { get; set; } public bool HandleEvents { get; set; }
@ -79,7 +91,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
AndroidKeyboardDevice.Instance!, AndroidKeyboardDevice.Instance!,
Convert.ToUInt64(e.EventTime), Convert.ToUInt64(e.EventTime),
inputRoot, inputRoot,
unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString() unicodeTextInput ?? CharToString(e.UnicodeChar)
); );
_view.Input?.Invoke(rawTextEvent); _view.Input?.Invoke(rawTextEvent);
@ -107,6 +119,15 @@ namespace Avalonia.Android.Platform.Specific.Helpers
return rv; return rv;
} }
private static string CharToString(int unicodeChar)
{
if (unicodeChar >= 0 && unicodeChar < s_asciiStringCache.Length)
{
return s_asciiStringCache[unicodeChar];
}
return char.ConvertFromUtf32(unicodeChar);
}
private static string? GetKeySymbol(int unicodeChar, PhysicalKey physicalKey) private static string? GetKeySymbol(int unicodeChar, PhysicalKey physicalKey)
{ {
// Handle a very limited set of control characters so that we're consistent with other platforms // Handle a very limited set of control characters so that we're consistent with other platforms
@ -126,7 +147,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
if (unicodeChar <= 0x7F) if (unicodeChar <= 0x7F)
{ {
var asciiChar = (char)unicodeChar; var asciiChar = (char)unicodeChar;
return KeySymbolHelper.IsAllowedAsciiKeySymbol(asciiChar) ? asciiChar.ToString() : null; return KeySymbolHelper.IsAllowedAsciiKeySymbol(asciiChar) ? s_asciiStringCache[asciiChar] : null;
} }
return char.ConvertFromUtf32(unicodeChar); return char.ConvertFromUtf32(unicodeChar);
} }

Loading…
Cancel
Save