ChoreographerTimer.cs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reactive.Disposables;
  4. using System.Threading.Tasks;
  5. using Android.OS;
  6. using Android.Views;
  7. using Avalonia.Rendering;
  8. using Java.Lang;
  9. namespace Avalonia.Android
  10. {
  11. internal sealed class ChoreographerTimer : Java.Lang.Object, IRenderTimer, Choreographer.IFrameCallback
  12. {
  13. private readonly object _lock = new object();
  14. private readonly Thread _thread;
  15. private readonly TaskCompletionSource<Choreographer> _choreographer = new TaskCompletionSource<Choreographer>();
  16. private readonly ISet<AvaloniaView> _views = new HashSet<AvaloniaView>();
  17. private Action<TimeSpan> _tick;
  18. private int _count;
  19. public ChoreographerTimer()
  20. {
  21. _thread = new Thread(Loop);
  22. _thread.Start();
  23. }
  24. public bool RunsInBackground => true;
  25. public event Action<TimeSpan> Tick
  26. {
  27. add
  28. {
  29. lock (_lock)
  30. {
  31. _tick += value;
  32. _count++;
  33. if (_count == 1)
  34. {
  35. _choreographer.Task.Result.PostFrameCallback(this);
  36. }
  37. }
  38. }
  39. remove
  40. {
  41. lock (_lock)
  42. {
  43. _tick -= value;
  44. _count--;
  45. }
  46. }
  47. }
  48. internal IDisposable SubscribeView(AvaloniaView view)
  49. {
  50. lock (_lock)
  51. {
  52. _views.Add(view);
  53. if (_views.Count == 1)
  54. {
  55. _choreographer.Task.Result.PostFrameCallback(this);
  56. }
  57. }
  58. return Disposable.Create(
  59. () =>
  60. {
  61. lock (_lock)
  62. {
  63. _views.Remove(view);
  64. }
  65. }
  66. );
  67. }
  68. private void Loop()
  69. {
  70. Looper.Prepare();
  71. _choreographer.SetResult(Choreographer.Instance);
  72. Looper.Loop();
  73. }
  74. public void DoFrame(long frameTimeNanos)
  75. {
  76. _tick?.Invoke(TimeSpan.FromTicks(frameTimeNanos / 100));
  77. lock (_lock)
  78. {
  79. if (_count > 0 && _views.Count > 0)
  80. {
  81. Choreographer.Instance.PostFrameCallback(this);
  82. }
  83. }
  84. }
  85. }
  86. }