Alrighty, so I understand that this general question has been asked numerous times here, but I have yet to find an answer that makes sense to me. Almost every answer I've seen just says some blurb like, "hey, just throw this in your method and you're good", but I'm not seeing full examples, and what I've tried is not working either.
Here's the error I receive:
[mono] android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
So, simply put, I have an activity that grabs some information from a web service and then throws the web service results into a couple of TextViews. Could someone please help me figure out where and how I need to use the RunOnUiThread()? Here's the code:
using Android.App;
using Android.OS;
using System;
using System.Web;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Android.Widget;
namespace DispatchIntranet
{
[Activity (Label = "@string/Summary")]
public class SummaryActivity : Activity
{
private static readonly Log LOG = new Log(typeof(SummaryActivity));
private TextView summaryTotalRegularLabel;
private TextView summaryTotalRollover;
private TextView summaryScheduledLabel;
private TextView summaryRemainingRegular;
private string url;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// SET THE LAYOUT TO BE THE SUMMARY LAYOUT
SetContentView(Resource.Layout.Summary);
// INITIALIZE CLASS MEMBERS
init();
if (LOG.isInfoEnabled())
{
LOG.info("Making call to rest endpoint . . .");
if (LOG.isDebugEnabled())
{
LOG.debug("url: " + this.url);
}
}
try
{
// BUILD REQUEST FROM URL
HttpWebRequest httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(this.url));
// SET METHOD TO 'GET'
httpReq.Method = GetString(Resource.String.web_service_method_get);
// ASK FOR JSON RESPONSE
httpReq.Accept = GetString(Resource.String.web_service_method_accept);
// INVOKE ASYNCHRONOUS WEB SERVICE
httpReq.BeginGetResponse((ar) => {
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse (ar))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
// PUT RESPONSE INTO STRING
string content = reader.ReadToEnd();
// CONVERT STRING TO DYNAMIC JSON OBJECT
var json = JsonConvert.DeserializeObject<dynamic>(content);
if (LOG.isDebugEnabled())
{
LOG.debug("content: " + content);
LOG.debug("json: " + json);
LOG.debug("TOTAL_REGULAR_PTO_HOURS: " + json.d[0].TOTAL_REGULAR_PTO_HOURS);
}
// ** THIS IS WHAT WILL NOT WORK **
this.summaryTotalRegularLabel.Text = json.d[0].TOTAL_REGULAR_PTO_HOURS;
this.summaryTotalRollover.Text = json.d[0].TOTAL_ROLLOVER_PTO_HOURS;
this.summaryScheduledLabel.Text = json.d[0].TOTAL_USED_PTO_HOURS;
this.summaryRemainingRegular.Text = json.d[0].TOTAL_REMAINING_PTO_HOURS;
}
}
}, httpReq);
}
catch (Exception e)
{
LOG.error("An exception occurred while attempting to call REST web service!", e);
}
}
private void init()
{
// GET GUID FROM PREVIOUS INTENT AND DETERMINE CURRENT YEAR
string guid = Intent.GetStringExtra("guid");
int year = DateTime.Now.Year;
// BUILD URL
this.url = GetString(Resource.String.web_service_url)
+ GetString(Resource.String.ws_get_pto_summary)
+ "?" + "guid='" + HttpUtility.UrlEncode(guid) + "'"
+ "&" + "year=" + HttpUtility.UrlEncode(year.ToString());
// GET THE SUMMARY LABELS
this.summaryTotalRegularLabel = FindViewById<TextView>(Resource.Id.SummaryTotalRegular);
this.summaryTotalRollover = FindViewById<TextView>(Resource.Id.summaryTotalRollover);
this.summaryScheduledLabel = FindViewById<TextView>(Resource.Id.summaryScheduledLabel);
this.summaryRemainingRegular = FindViewById<TextView>(Resource.Id.SummaryRemainingRegular);
}
}
}
When you make a web service call, HttpWebRequest creates a new thread to run the operation on. This is done to keep your user interface from locking up or skip frames. Once your web service call is complete, you need to go back to the UI Thread to update the UI components that live on that thread. You can do that a couple of different ways.
First, you can wrap your code in an anonymous function call like so:
RunOnUiThread(()=>{
this.summaryTotalRegularLabel.Text = json.d[0].TOTAL_REGULAR_PTO_HOURS;
this.summaryTotalRollover.Text = json.d[0].TOTAL_ROLLOVER_PTO_HOURS;
this.summaryScheduledLabel.Text = json.d[0].TOTAL_USED_PTO_HOURS;
this.summaryRemainingRegular.Text = json.d[0].TOTAL_REMAINING_PTO_HOURS;
});
Or you can call a function via RunOnUiThread (jsonPayload is a field on the class):
jsonPayload = json;
RunOnUiThread(UpdateTextViews);
...
void UpdateTextViews()
{
this.summaryTotalRegularLabel.Text = jsonPayload.d[0].TOTAL_REGULAR_PTO_HOURS;
this.summaryTotalRollover.Text = jsonPayload.d[0].TOTAL_ROLLOVER_PTO_HOURS;
this.summaryScheduledLabel.Text = jsonPayload.d[0].TOTAL_USED_PTO_HOURS;
this.summaryRemainingRegular.Text = jsonPayload.d[0].TOTAL_REMAINING_PTO_HOURS;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With