forked from unoplatform/uno
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNativeViewInstanceHelper.Android.cs
More file actions
154 lines (132 loc) · 4.46 KB
/
Copy pathNativeViewInstanceHelper.Android.cs
File metadata and controls
154 lines (132 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using Android.Runtime;
using Uno.Extensions;
using Uno.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Uno.UI
{
public class NativeInstanceHelper
{
private static readonly string _statsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "cache", "nativeInstances.cache");
private static Dictionary<Type, System.Tuple<IntPtr, IntPtr>> _typeCache = new Dictionary<Type, System.Tuple<IntPtr, IntPtr>>(Uno.Core.Comparison.FastTypeComparer.Default);
private static bool _cacheInitialized;
static NativeInstanceHelper()
{
InitializeCachedInstances();
ScheduleCacheDump();
}
/// <summary>
/// Schedules the writing of the content of the cache to the disk.
/// </summary>
private static void ScheduleCacheDump()
{
ThreadPool.QueueUserWorkItem(
async _ =>
{
await Task.Delay(TimeSpan.FromMinutes(1));
WriteUsageStats();
}
);
}
/// <summary>
/// Pre-feteches the cached natived instances for the current application package, on a background thread.
/// </summary>
public static void InitializeCachedInstances()
{
if(_cacheInitialized)
{
// This will always happen, as the method is called in the
// cctor. Yet, it gives the method more visibility, as it must
// be called as soon as possible at app launch time.
return;
}
_cacheInitialized = true;
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
if (File.Exists(_statsFilePath))
{
var types = File.ReadAllLines(_statsFilePath);
var cachedTypes = types
.Select(t => Type.GetType(t, false))
.Trim();
foreach (var type in cachedTypes)
{
if (typeof(NativeInstanceHelper).Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
typeof(NativeInstanceHelper).Log().DebugFormat("Pre-fecthing native type information {0}", type);
}
CreateTypeReference(type);
}
}
}
catch (Exception ex)
{
typeof(NativeInstanceHelper).Log().Error("Failed to initialize native view cache", ex);
}
});
}
/// <summary>
/// Creates a native view using a fast instanciation path, by caching references to the native class and constructor.
/// </summary>
/// <param name="type">The .NET class top-level type</param>
/// <param name="target">The .NET class instance being created</param>
/// <param name="context">The context of the View to be created</param>
/// <param name="setInstance">A delegate that provides access to the <see cref="Java.Lang.Object.SetHandle(IntPtr, JniHandleOwnership)"/> method.</param>
public static unsafe void CreateNativeInstance(Type type, Java.Lang.Object target, Android.Content.Context context, Action<IntPtr, JniHandleOwnership> setInstance)
{
System.Tuple<IntPtr, IntPtr> typeRefInfo;
typeRefInfo = CreateTypeReference(type);
JValue* ptr = stackalloc JValue[1];
*ptr = new JValue(context);
// Create the allocation, this only works if the target SDK is higher than 10.
var instance = JNIEnv.AllocObject(typeRefInfo.Item1);
// Set the instance on the target object.
setInstance(instance, JniHandleOwnership.TransferLocalRef);
// invoke the native constructor
JNIEnv.FinishCreateInstance(target.Handle, typeRefInfo.Item1, typeRefInfo.Item2, ptr);
}
private static unsafe System.Tuple<IntPtr, IntPtr> CreateTypeReference(Type type)
{
System.Tuple<IntPtr, IntPtr> typeRefInfo;
lock(_typeCache)
{
if (!_typeCache.TryGetValue(type, out typeRefInfo))
{
// cache the resolutions, which cost a lot of processing.
var typeRef = JNIEnv.FindClass(type);
var ctorRef = JNIEnv.GetMethodID(typeRef, "<init>", "(Landroid/content/Context;)V");
_typeCache[type] = typeRefInfo = Tuple.Create(typeRef, ctorRef);
}
}
return typeRefInfo;
}
/// <summary>
/// Writes the content of the already resolved instances, to be
/// reused at the next application startup.
/// </summary>
private static void WriteUsageStats()
{
Directory.CreateDirectory(Path.GetDirectoryName(_statsFilePath));
using (var stream = File.OpenWrite(_statsFilePath))
{
using (var writer = new StreamWriter(stream))
{
lock(_typeCache)
{
foreach (var pairs in _typeCache)
{
writer.WriteLine(pairs.Key.AssemblyQualifiedName);
}
}
}
}
}
}
}