我已经下载了Xamarin的试用版,目前正在使用它(在Visual Studio 2010中)。
我想做的其中一个测试是看看如何创建一个Activity,我通过BackgroundWorker线程在GUI上更新控件 - 具体来说,我有兴趣看看Mono语法与a的不同之处有多么不同常规Windows窗体(C#)语法。
为了测试这一点,我创建了一个针对API Level 17(Android 4.2)的Android应用程序(再次在VS2010中)。该应用程序的一般功能将从_DoWork()BackgroundWorker事件处理程序中更改EditText控件的文本值。
这是代码..
//Xamarin.Android app
[Activity(Label = "Cross-thread Test", MainLauncher = true)]
public class Activity1 : Activity
{
EditText labelDisplay;
BackgroundWorker bgWorker;
int counter = 0;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
this.bgWorker = new BackgroundWorker();
this.bgWorker.WorkerSupportsCancellation = true;
this.bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
Button buttonStart = FindViewById<Button>(Resource.Id.buttonStart);
buttonStart.Click += new EventHandler(buttonStart_Click);
Button buttonStop = FindViewById<Button>(Resource.Id.buttonStop);
buttonStop.Click += new EventHandler(buttonStop_Click);
labelDisplay = FindViewById<EditText>(Resource.Id.labelDisplay);
labelDisplay.Text = "Click Start";
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (this.bgWorker.CancellationPending)
{
RunOnUiThread(() => labelDisplay.Text = "Click Start");
break;
}
else
{
counter++;
// This causes GREF to increase to 2001
RunOnUiThread(() => labelDisplay.Text = counter.ToString());
}
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
if (this.bgWorker != null && !this.bgWorker.IsBusy)
this.bgWorker.RunWorkerAsync();
}
private void buttonStop_Click(object sender, EventArgs e)
{
if (this.bgWorker != null && this.bgWorker.IsBusy)
this.bgWorker.CancelAsync();
}
}
无论价值多少,我都试图以RunOnUiThread()
的方式使用Invoke()
,就像我在Windows Forms
这样使用//Regular Windows Forms app
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (this.bgWorker.CancellationPending)
break;
else
{
counter++;
//This isn't possible in a Xamarin.Android app, which is why I'm using RunOnUiThread() instead
this.Invoke((Action)(() => { this.labelDisplay.Text = counter.ToString(); }));
}
}
}
一样......
09-28 18:09:39.231 D/dalvikvm( 731): GREF has increased to 1701
09-28 18:09:39.461 D/dalvikvm( 731): GREF has increased to 1801
09-28 18:09:40.192 D/dalvikvm( 731): GREF has increased to 1901
09-28 18:09:40.271 D/dalvikvm( 731): GC_CONCURRENT freed 305K, 7% free 6175K/6599K, paused 3ms+5ms
09-28 18:09:40.531 D/dalvikvm( 731): GREF has increased to 2001
09-28 18:09:40.531 W/dalvikvm( 731): JNI global reference table (0x475fd0) dump:
09-28 18:09:40.531 W/dalvikvm( 731): Last 10 entries (of 2001):
09-28 18:09:40.531 W/dalvikvm( 731): 2000: 0x40fa4e90 java.lang.NoClassDefFoundError
09-28 18:09:40.541 W/dalvikvm( 731): 1999: 0x40fb2e78 mono.java.lang.RunnableImplementor
.
.
.
09-28 18:09:40.581 E/dalvikvm( 731): Excessive JNI global references (2001) //OOPS!
09-28 18:09:40.581 E/dalvikvm( 731): VM aborting
09-28 18:09:40.581 E/mono-rt ( 731): Stacktrace:
09-28 18:09:40.581 E/mono-rt ( 731):
09-28 18:09:40.592 E/mono-rt ( 731): at <unknown> <0xffffffff>
09-28 18:09:40.592 E/mono-rt ( 731): at (wrapper managed-to-native) object.wrapper_native_0x408027e9 (intptr,intptr) <IL 0x00026, 0xffffffff>
09-28 18:09:40.592 E/mono-rt ( 731): at Android.Runtime.JNIEnv.NewGlobalRef (intptr) [0x00000] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.8.2-branch/bdc709d1/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.cs:389
Xamarin.Android应用程序将崩溃,其中Debug输出显示以下错误(我只包含了显着的信息)...
RunOnUiThread()
我看了Xamarin's Troubleshooting page说明了
Dalvik的JNI层仅支持有限数量的JNI对象 引用在任何给定时间点都有效。当这个限制是 事情破裂了。
GREF(全局引用)限制是模拟器中的2000个引用, 和~52000硬件参考。
当你看到时,你知道你开始创造太多的GREF Android调试日志中的消息:
你会在错误日志中注意到我的代码将GREF增加到了2001.
根据上面的故障排除说明,我假设{{1}}正在为while循环的每次迭代创建一个JNI对象。如果是这种情况,为什么会发生这种情况,我该如何安全地从另一个线程写入GUI?
答案 0 :(得分:2)
RunOnUiThread
排队工作。
用于更新UI的技术是可以的,只是在循环中没有暂停,该队列几乎立即就会发现2000个项目。
我打赌在那里有一个短Thread.Sleep()
就好了。
答案 1 :(得分:0)
尝试在此
中编写您的UI更新Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
// code to execute
});