Sam
Sam

Reputation: 185

System.Threading: Running into Java.Lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

In my Xamarin.Android-App, when opening a special page (activity), there is a table displayed on create. A ListAdapter defines the certain fields within that table. I have a function which gets me an external int. The int is amongst other data displayed in that table. But this function is only called once (on create). I want to detect dynamically during runtime if this received int changed, and if so change the display of the int in the table as well.

Please see my following code:

ListAdapter.cs:

var thread = new Thread(() => MethodToRun());
thread.Start();

[...]

void MethodToRun()
{
    while (true)
    {
        Thread.Sleep(1000);
        var a = new Activity();
        a.RunOnUiThread(() => {

            int number = Get_Number();
            if (number == 1)
            {
                v.FindViewById<TextView>(Resource.Id.textView).Text = "1";
            }
            if (number != 1)
            {
                v.FindViewById<TextView>(Resource.Id.textView).Text = "Not 1 anymore";
            }
        });
    }
}

Unfortunately, I am getting a runtime error in the line var a = new Activity(); saying

Java.Lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

Can anyone maybe tell me whats the problem / how to fix this?

Best regards

Edit: the Get_Number()-method looks like the following:

private int Get_Number(Geraet geraet)
{
    var request = HttpWebRequest.Create(string.Format(Constants.WebservicePath, Serverip, Constants.WebservicePort, geraet.Ip));
    request.ContentType = "application/json";
    request.Method = "GET";

    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
    {
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
            var content = reader.ReadToEnd();
            var result = System.Text.RegularExpressions.Regex.Replace(content, @"[^0-9]", "");

            if (result == string.Empty)
            {
                return -2;
            }
            else
            {
                return Int32.Parse(result);
            }
        }
    }
}

Upvotes: 0

Views: 527

Answers (1)

TheWanderer
TheWanderer

Reputation: 17874

Do what it says and call Looper.prepare():

void MethodToRun() {
    Looper.prepare();

    //...
}

However, you have other problems. You can't create a new instance of Activity yourself. If you want something to run on the main Thread, create a new Handler:

var handler = new Handler(Looper.GetMainLooper());

And then you can post with it:

handler.Post(() => {
    int number = Get_Number();

    //...
}

However, if you want to run a repeating action on the main Thread, what you're doing is exactly what Handlers were created to avoid.

Try something like this instead:

var handler = new Handler(Looper.GetMainLooper());

Runnable actions = new Runnable() => {
    int number = Get_Number(); //this needs to be async

    handler.Post(() => {
        TextView text = v.FindViewById(Resource.Id.textView);

        text.Text = number == 1 ? "1" : "Not 1 anymore";

        PostAction();
    }
}; //the syntax for this might be wrong, since I don't know C#

void PostAction() {
    handler.postDelayed(() => {
        new Thread(actions).start();
    }, 1000);
}

This way, you just have to call PostAction() once to get things going and then it'll keep looping over your logic.

If you want to stop the loop:

handler.RemoveCallbacks(actions);

Upvotes: 0

Related Questions