1

Xamarin の試用版をダウンロードし、現在 (Visual Studio 2010 で) 試しています。

私がやりたかったテストの 1 つは、BackgroundWorker スレッドを介して GUI 上のコントロールを更新するアクティビティを作成する方法を確認することでした。具体的には、Mono 構文が通常の Windows フォームと比較してどのように異なるかを確認することに興味がありました。 (C#) 構文。

これをテストするために、API レベル 17 (Android 4.2) をターゲットとする Android アプリケーション (これも VS2010) を作成しました。アプリの一般的な機能は、EditText コントロールのテキスト値を _DoWork() BackgroundWorker イベント ハンドラー内から変更します。

これがコードです..

//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(); })); 
        }
    }
}

Xamarin.Android アプリケーションは、デバッグ出力に次のエラーが表示される場所でクラッシュします (重要な情報のみを記載しています)。

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 のトラブルシューティング ページを読みました。

Dalvik の JNI レイヤーは、限られた数の JNI オブジェクト参照のみをサポートしており、任意の時点で有効です。この制限を超えると、物事が壊れます。

GREF (グローバル参照) の制限は、エミュレータでは 2000 参照、ハードウェアでは ~52000 参照です。

Android Debug Log に次のようなメッセージが表示されたら、作成し始めた GREF が多すぎることがわかります。

エラー ログを見ると、コードによって GREF が 2001 に増加したことがわかります。

上記のトラブルシューティング ノートに基づいてRunOnUiThread()、while ループの反復ごとに が JNI オブジェクトを作成していると想定しています。これが事実である場合、なぜこれが発生するのですか?別のスレッドから安全に GUI に書き込むにはどうすればよいですか?

4

2 に答える 2