Ich schreibe eine Xamarin.Android App, die Syntaxhervorhebung für Code bietet. Es erstickt an großen Dateien. Ich debuggte meine App und stoppte sie zufällig, während sie eine verarbeitete. Zu meiner Überraschung hörte ich auf dieser Strecke fast die ganze Zeit:Wie zur Umgehung der Langsamkeit von Xamarin.Android Java Interop
var span = new ForegroundColorSpan(color);
Dies ist das einzige Stück von Java-Code, den ich sehr häufig anrufen, während ich bin Syntax-Hervorhebung. This ist die Implementierung der Methode, die ein One-Liner ist, der einem Feld int
zuweist. Offensichtlich muss Xamarins Java-Interop der Schuldige sein.
Ich bin mir nicht sicher, wie ich das umgehen kann. Ich versuchte, die ForegroundColorSpan
für eine gegebene unter Verwendung Dictionary
zwischenzuspeichern, aber wegen der Art, wie die spannable APIs entworfen werden, können Sie SpannableString.setSpan
with the same span instance für verschiedene Bereiche nicht benennen. Habe ich andere Optionen hier?
bearbeiten: Sorry, Interop, nicht Marshalling. Ich habe den Titel geändert.
bearbeiten 2: OK, ich habe einen viel tieferen Einblick in dieses Problem genommen. Zuerst habe ich eine minimale Repro:
var list = new List<ForegroundColorSpan>();
for (int i = 0; i < 100000; i++)
{
list.Add(new ForegroundColorSpan(Color.Blue));
}
Versuchen Sie, dies in einer leeren Xamarin App laufen und es wird ewig dauern. Das wirklich, wirklich seltsame daran ist jedoch, dass die Zeit nicht proportional zur Anzahl der Iterationen ist. Versuchen Sie, die Anzahl der Iterationen auf 40000
festzulegen, und es wird in einer Sekunde abgeschlossen. Versuchen Sie es auf 50000 zu setzen, und es wird ewig dauern. Wenn Sie den Debugger nach dem Zufallsprinzip pausieren und die i
überprüfen, werden Sie sehen, dass es immer um 45/46K ist. Es scheint einen magischen Grenzpunkt bei etwa 45K Elementen zu geben, wo die Schleife plötzlich unbrauchbar langsam wird - und damit meine ich, dass 5 neue Objekte erzeugt werden pro Sekunde.
Zweitens sah ich in dem, was der Konstruktor der Haube tut unter durch das Working with JNI Dokument zu lesen, speziell this part, und ich schrieb manuell Bindungen für ForegroundColorSpan
so konnte ich sie debuggen:
[Register("android/text/style/ForegroundColorSpan", DoNotGenerateAcw = true)]
internal class FastForegroundColorSpan : JavaObject
{
private static readonly IntPtr class_ref = JNIEnv.FindClass("android/text/style/ForegroundColorSpan");
private static IntPtr id_ctor_I;
[Register(".ctor", "(I)V", "")]
public unsafe FastForegroundColorSpan(int color)
: base(IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
{
if (Handle != IntPtr.Zero)
{
return;
}
if (id_ctor_I == IntPtr.Zero)
{
id_ctor_I = JNIEnv.GetMethodID(class_ref, "<init>", "(I)V");
}
var args = stackalloc JValue[1];
args[0] = new JValue(color);
var handle = JNIEnv.NewObject(class_ref, id_ctor_I, args);
SetHandle(handle, JniHandleOwnership.TransferLocalRef);
}
}
ich meinen Code geschaltet Verwenden Sie new FastForegroundColorSpan
anstelle von new ForegroundColorSpan
. Wenn ich den Code im Debugger jetzt zufällig stoppe, bricht er immer um SetHandle
. Ich schaute in die source code of SetHandle
, und tatsächlich dort ist eine Menge Sachen, die dort gehen, einschließlich vieler Interopaufrufe zu JNIEnv
und locking/schwache Hinweise. Was ich aber immer noch nicht erklären kann ist, warum die App an einem magischen Grenzpunkt nach ~ 45K Spannweiten immer langsamer (und so dramatisch) wird.
Wäre schön, wenn Sie ein wenig Code teilen könnten, der das Problem repliziert hat. – jzeferino
@jzeferino Ich tat, es ist der One-Liner-Ausschnitt. Wenn Sie suchen, wie ich es in meiner App verwende, finden Sie das [hier] (https://github.com/jamesqo/Repository/blob/wip-spannabletext/Repository/EditorServices/Highlighting/SyntaxColorer.cs#L33) , aber wirklich alles, was Sie tun müssen, ist eine For-Schleife zu schreiben, die oft läuft und 'ForegroundColorSpan()' aufruft. –