如何解决Xamarin.Android Java interop

时间:2017-06-17 10:25:39

标签: c# android .net xamarin xamarin.android

我正在编写一个Xamarin.Android应用程序,它为代码提供语法高亮显示。它窒息了大文件。我调试了我的应用程序并在处理它时随机停止了它。令我惊讶的是,我几乎一直都停在这条线上:

var span = new ForegroundColorSpan(color);

这是我在语法高亮时经常调用的唯一一段Java代码。 This是该方法的实现,它是一个分配给int字段的单行。显然,Xamarin的Java互操作必定是罪魁祸首。

我不确定如何解决这个问题。我尝试使用ForegroundColorSpan为给定的Color缓存Dictionary,但由于Spannable API的设计方式,您无法调用SpannableString.setSpan {{3}对于不同的范围。我还有其他选择吗?

编辑:抱歉,互操作,而不是编组。我改变了标题。

编辑2:好的,我一直在深入研究这个问题。首先,我提出了一个最小的复制品:

var list = new List<ForegroundColorSpan>();
for (int i = 0; i < 100000; i++)
{
    list.Add(new ForegroundColorSpan(Color.Blue));
}

尝试在空白的Xamarin应用程序中运行它,这将需要永远。然而,真正非常奇怪的是,与迭代次数成比例的时间 。尝试将迭代次数设置为40000,并在一秒钟内完成。尝试将其设置为50000,它将永远。如果你随机暂停调试器并检查i,你会发现它总是在45 / 46K左右。在45K左右的元素处似乎有一个神奇的截止点,其中循环突然变得非常慢 - 而且,我的意思是每秒创建 5个新对象

其次,我通过阅读with the same span instance文档(特别是Working with JNI)来查看构造函数在底层所做的工作,然后我手动为ForegroundColorSpan编写了绑定,以便我可以调试它们: / p>

[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);
    }
}

我将代码改为使用new FastForegroundColorSpan而不是new ForegroundColorSpan。当我现在在调试器中随机停止代码时,它总是在SetHandle处中断。我查看了this part,确实有很多东西在那里,包括对JNIEnv和锁定/弱引用的许多互操作调用。然而,我仍然可以解释为什么应用程序总是在~45K跨度之后的某个魔术截止点减速(并且如此显着)。

1 个答案:

答案 0 :(得分:1)

  然而,真正非常奇怪的是,它所花费的时间与迭代次数并不成正比。尝试将迭代次数设置为40000,并在一秒钟内完成。尝试将其设置为50000,它将永远。

问题是你有太多的Java.lang.object实例同时存在。如果您参考Global Reference Message,您可以找到以下声明:

  

不幸的是,Android模拟器只允许存在2000个全局引用   一次。硬件具有52000个全局引用的更高限制。该   在仿真器上运行应用程序时,下限可能会有问题,因此   知道实例的来源非常有用。

您可以通过以下方式启用全局参考loggig(GREF)日志记录:

adb shell setprop debug.mono.log gref

您还可以参考similar case

因此,对于解决方案,您需要删除此循环,并尝试根据您的要求解决此问题。