I'm currently working toward a mobile android application. The main thing that this app will have trouble with for load times is a Webservice json string that at this current stage is taking too long to load and sometimes causing the app to force close (stalling for too long).
Splash -> MainActivity -> HomeActivity This is how our application starts.
First we display a Splash, and behind that we run the MainActivity, which consists of the following code:
public class HomeActivity : Activity
{
NewsObject[] news;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
var request = HttpWebRequest.Create(string.Format(#"http://rapstation.com/webservice.php"));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if(string.IsNullOrWhiteSpace(content)) {
Console.Out.WriteLine("Response contained empty body...");
Toast toast = Toast.MakeText (this, "No Connection to server, Application will now close", ToastLength.Short);
toast.Show ();
}
else {
news = JsonConvert.DeserializeObject<NewsObject[]>(content);
}
}
Console.Out.WriteLine ("Now: \r\n {0}", news[0].title);
}
var list = FindViewById<ListView> (Resource.Id.list);
list.Adapter = new HomeScreenAdapter (this, news);
list.ItemClick += OnListItemClick;
var Listen = FindViewById<Button> (Resource.Id.btnListen);
var Shows = FindViewById<Button> (Resource.Id.btnShows);
Listen.Click += (sender, e) => {
var second = new Intent (this, typeof(RadioActivity));
StartActivity (second);
};
Shows.Click += (sender, e) => {
var second = new Intent (this, typeof(ShowsActivity));
StartActivity (second);
};
}
protected void OnListItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var listView = sender as ListView;
var t = news[e.Position];
var second = new Intent (this, typeof(NewsActivity));
second.PutExtra ("newsTitle", t.title);
second.PutExtra ("newsBody", t.body);
second.PutExtra ("newsImage", t.image);
second.PutExtra ("newsCaption", t.caption);
StartActivity (second);
Console.WriteLine("Clicked on " + t.title);
}
}
The problem I am running into is the app will stick on the Splash page and the Application output will tell me that I am running too much on the Main thread.
What is a way to separate the download request to work in the background?
private class myTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
}
#Override
protected Void doInBackground(Void... params) {
// Runs on the background thread
return null;
}
#Override
protected void onPostExecute(Void res) {
}
}
and to run it
new myTask().execute();
Yes there is, you need to use AsyncTask, this should help too.
If the .Net/Mono version you're using supports async/await then you can simply do
async void DisplayNews()
{
string url = "http://rapstation.com/webservice.php";
HttpClient client = new HttpClient();
string content = await client.GetStringAsync(url);
NewsObject[] news = JsonConvert.DeserializeObject<NewsObject[]>(content);
//!! Your code to add news to some control
}
if Not, then you can use Task's
void DisplayNews2()
{
string url = "http://rapstation.com/webservice.php";
Task.Factory.StartNew(() =>
{
using (var client = new WebClient())
{
string content = client.DownloadString(url);
return JsonConvert.DeserializeObject<NewsObject[]>(content);
}
})
.ContinueWith((task,y) =>
{
NewsObject[] news = task.Result;
//!! Your code to add news to some control
},null,TaskScheduler.FromCurrentSynchronizationContext());
}
Related
I have app(net4.7.2) like this:
Program is simple, when user presses OK, im sending request to steam market to get informations about item which user entered (item steam market url) to textbox.
But when im trying to send request, first click event of button not working:
private void btnOK_Click(object sender, EventArgs e)
{
if (txtItemURL.Text.StartsWith("https://steamcommunity.com/market/listings/730/") == true)
{
Helpers.Helper.BuildURL(txtItemURL.Text);
SteamMarketItem SMI = Helpers.Helper.GetItemDetails();
lblPrice.Text = SMI.LowestPrice.ToString() + "$";
pbItemImage.ImageLocation = SMI.ImagePath;
Helpers.Helper.Kontrollar_BerpaEt();
}
else
{
Helpers.Helper.Kontrollar_SifirlaYanlisDaxilEdilib();
}
}
Method GetItemDetails():
public static SteamMarketItem GetItemDetails()
{
WinForms.Control.CheckForIllegalCrossThreadCalls = false;
Task.Run(() =>
{
try
{
using (HttpClient client = new HttpClient())
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
/* Get item info: */
var ResultFromEndpoint1 = client.GetAsync(ReadyEndpointURL1).Result;
var Json1 = ResultFromEndpoint1.Content.ReadAsStringAsync().Result;
dynamic item = serializer.Deserialize<object>(Json1);
marketItem.LowestPrice = float.Parse(((string)item["lowest_price"]).Replace("$", "").Replace(".", ","));
/* Get item image: */
var ResultFromEndpoint2 = client.GetAsync(ReadyEndPointURL2).Result;
var Json2 = ResultFromEndpoint2.Content.ReadAsStringAsync().Result;
var html = ((dynamic)serializer.Deserialize<object>(Json2))["results_html"];
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
marketItem.ImagePath = htmlDoc.DocumentNode.SelectSingleNode("//img[#class='market_listing_item_img']").Attributes["src"].Value + ".png";
Kontrollar_BerpaEt();
}
}
catch
{
Kontrollar_SifirlaYanlisDaxilEdilib();
}
});
return marketItem;
}
Class SteamMarketItem:
public class SteamMarketItem
{
public string ImagePath { get; set; }
public float LowestPrice { get; set; }
}
When im using Task.Run() first click not working, without Task.Run() working + but main UI thread stopping when request not finished.
I have no idea why this happens, I cant find problem fix myself, I will be glad to get help from you. Thanks.
If you want to use async you need to change your event handler to async so you can use await, please see the following:
1. Change your Event handler to async void, async void is acceptable on event handler methods, you should try to use async Task in place of async void in most other cases, so change your method signature to the following:
private async void btnOK_Click(object sender, EventArgs e)
{
if (txtItemURL.Text.StartsWith("https://steamcommunity.com/market/listings/730/") == true)
{
Helpers.Helper.BuildURL(txtItemURL.Text);
//here we use await to await the task
SteamMarketItem SMI = await Helpers.Helper.GetItemDetails();
lblPrice.Text = SMI.LowestPrice.ToString() + "$";
pbItemImage.ImageLocation = SMI.ImagePath;
Helpers.Helper.Kontrollar_BerpaEt();
}
else
{
Helpers.Helper.Kontrollar_SifirlaYanlisDaxilEdilib();
}
}
2. You shouldn't need to use Task.Run, HttpClient exposes async methods and you can make the method async, also, calling .Result to block on an async method is typically not a good idea and you should make the enclosing method async so you can utilize await:
//Change signature to async and return a Task<T>
public async static Task<SteamMarketItem> GetItemDetails()
{
WinForms.Control.CheckForIllegalCrossThreadCalls = false;
//what is marketItem?? Where is it declared?
try
{
using (HttpClient client = new HttpClient())
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
/* Get item info: */
var ResultFromEndpoint1 = await client.GetAsync(ReadyEndpointURL1);
var Json1 = await ResultFromEndpoint1.Content.ReadAsStringAsync();
dynamic item = serializer.Deserialize<object>(Json1);
marketItem.LowestPrice = float.Parse(((string)item["lowest_price"]).Replace("$", "").Replace(".", ","));
/* Get item image: */
var ResultFromEndpoint2 = await client.GetAsync(ReadyEndPointURL2);
var Json2 = await ResultFromEndpoint2.Content.ReadAsStringAsync();
var html = ((dynamic)serializer.Deserialize<object>(Json2))["results_html"];
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
marketItem.ImagePath = htmlDoc.DocumentNode.SelectSingleNode("//img[#class='market_listing_item_img']").Attributes["src"].Value + ".png";
Kontrollar_BerpaEt();
}
}
catch
{
Kontrollar_SifirlaYanlisDaxilEdilib();
}
//what is marketItem?? Where is it declared?
return marketItem;
}
I have a problem with my server uwp side.
I try do unhide some components like this but my app hangs on :
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using(var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => this.txtBlk_Events.Text = request);
if (request.Length > 0)
{
btnSend.Visibility = Visibility.Visible;
}
}
It is possible to do that or not ?
Thanks for your answer
Visibility is a UI property and must be set on the UI thread. In your case it will run on the same thread the ConnectionReceived event handler is run on (thanks to async/await) which is likely not the UI thread. You should instead set the Visibility within the Dispatcher.RunAsync call to make sure it runs on UI thread.
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using(var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
var setVisibility = request.Length > 0;
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.txtBlk_Events.Text = request;
if ( setVisibility )
{
btnSend.Visibility = Visibility.Visible;
}
});
}
I created an app for Android and Windows Phone. For data access i'm using sqllite.net async. I created the simple example solution with a PCL liblary, Xamarin Android project and Windows Phone 8 silverligth project. This is my DataService in PCL:
public class DataService
{
private SQLiteAsyncConnection _dbConnection;
public DataService(ISQLitePlatform platform, string path)
{
var connectionFactory = new Func<SQLiteConnectionWithLock>
(() => new SQLiteConnectionWithLock(platform, new SQLiteConnectionString(path, true)));
_dbConnection = new SQLiteAsyncConnection(connectionFactory);
}
public async Task Initialize()
{
await _dbConnection.CreateTableAsync<ToDo>().ContinueWith(t =>
{
Debug.WriteLine("Create");
});
}
public async Task<int> AddNewToDo(ToDo item)
{
var result = await _dbConnection.InsertAsync(item);
return result;
}
public async Task<List<ToDo>> GetAllToDos()
{
var result = await _dbConnection.Table<ToDo>().OrderByDescending(t => t.TimeStamp).ToListAsync();
return result;
}
....
}
This is using in Windows Phone:
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var db = new DataService(new SQLitePlatformWP8(), "my.db");
await db.Initialize();
await db.AddNewToDo(new ToDo {Text = "Hello world"});
var items = await db.GetAllToDos();
Debug.WriteLine("Count - {0}",items.Count);
}
output in Windows Phone:
Create
Count - 1
It is ok. Debugging is works.
This is using in Xamarin Android:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Click += delegate
{
TestDb();
};
}
private async void TestDb()
{
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var path = Path.Combine(documentsPath, "my.db");
var db = new DataService(new SQLitePlatformAndroid(), path);
await db.Initialize();
await db.AddNewToDo(new ToDo { Text = "Hello world" });
var items = await db.GetAllToDos();
Console.WriteLine("count - {0}",items.Count);
}
output:
[0:]
Create
[0:] Create
02-18 00:46:01.167 I/mono-stdout(19234): Create
count - 1
02-18 00:46:01.675 I/mono-stdout(19234): count - 1
Why are invoked more than once? Debugging not working. When I stop at code with await, next step just drops out of the method without touching my return calls or anything.
This is a simple example, and I do not understand why this is happening. Maybe I'm doing something wrong.
Your code have a problem:
button.Click += delegate
{
TestDb();
};
TestDb is an async method and you're calling it asynchronously without the await.
This will make the call to TestDb to happens after you leave the Click event code.
I suggest you to await the call:
button.Click += async delegate
{
await TestDb();
};
I started Windows Phone programming with this example from Microsoft:
http://code.msdn.microsoft.com/wpapps/Hybrid-Web-App-75b7ef74/view/SourceCode
The app only displays the browser and load a URL.
Now I want to load an other URL directly from a .txt file.
For example: http://www.test.de/appurl.txt and then I want to load the URL in the Windows Phone App.
--> For example: http://anotherserver.de/index.html?mobileApp
My problem is, that the URL have to load synchronous and not asynchronous. I implement a AutoResetEvent, but it donĀ“t work...
Hope somebody can help me, thx!
Here is my Code:
public partial class MainPage : PhoneApplicationPage
{
// URL zur WebApp
// TODO: URL muss aus diesem TEXT-File ausgelesen werden!
private string _appURL = "http://www.test.de/appurl.txt";
public string _homeURL = "";
//private string _homeURL = "http://anotherserver.de/index.html?mobileApp";
// URL zur Registrierung von Angeboten
private string _registrationURL = "http://anotherserver.de/index.html?bereich=registrierung&mobileApp";
// Secondary tile data
//private Uri _currentURL;
//private Uri _tileImageURL;
//private string _pageTitle = "Shop ";
// Serialize URL into IsoStorage on deactivation for Fast App Resume
private Uri _deactivatedURL;
private IsolatedStorageSettings _userSettings = IsolatedStorageSettings.ApplicationSettings;
// To indicate when we're navigating to a new page.
private ProgressIndicator _progressIndicator;
// Constructor
public MainPage()
{
InitializeComponent();
//Read the URL from a txt file and set the _homeURL
ReadFile(_appURL);
// Setup the progress indicator
_progressIndicator = new ProgressIndicator();
_progressIndicator.IsIndeterminate = true;
_progressIndicator.IsVisible = false;
SystemTray.SetProgressIndicator(this, _progressIndicator);
// Event handler for the hardware back key
BackKeyPress += MainPage_BackKeyPress;
// Fast app resume events
PhoneApplicationService.Current.Deactivated += Current_Deactivated;
PhoneApplicationService.Current.Closing += Current_Closing;
}
//AutoResetEvent are = new AutoResetEvent(false);
public void ReadFile(string address)
{
var webClient = new WebClient();
webClient.OpenReadAsync(new Uri(address));
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
// lock the thread until web call is completed
//are.WaitOne();
//finally call the NotifyComplete method to end the background agent
//NotifyComplete();
}
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
using (var reader = new StreamReader(e.Result))
{
string downloaded = reader.ReadToEnd();
Debug.WriteLine("downloaded= " + downloaded);
_homeURL = downloaded;
//work = false;
}
}
catch
{
Debug.WriteLine("Please check your data connection");
MessageBox.Show("Please check your data connection");
}
//signals locked thread that can now proceed
//are.Set();
}
#region App Navigation Events
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Browser event handlers
Browser.Navigating += Browser_Navigating;
Browser.Navigated += Browser_Navigated;
Browser.NavigationFailed += Browser_NavigationFailed;
Browser.IsScriptEnabled = true;
// Try to get the URL stored for fast app resume.
try
{
_deactivatedURL = (Uri)(_userSettings["deactivatedURL"]);
}
catch (System.Collections.Generic.KeyNotFoundException keyNotFound)
{
Debug.WriteLine(keyNotFound.Message);
}
// Were we started from a pinned tile?
if (NavigationContext.QueryString.ContainsKey("StartURL"))
{
// Navigate to the pinned page.
Browser.Navigate(new Uri(NavigationContext.QueryString["StartURL"], UriKind.RelativeOrAbsolute));
}
else if ((_deactivatedURL != null) && (e.NavigationMode != NavigationMode.Reset))
{
// If there is a stored URL from our last
// session being deactivated, navigate there
if (Browser.Source != _deactivatedURL)
{
Browser.Navigate(_deactivatedURL);
}
}
else
{
// Not launched from a pinned tile...
// No stored URL from the last time the app was deactivated...
// So, just navigate to the home page
Browser.Navigate(new Uri(_homeURL, UriKind.RelativeOrAbsolute));
}
}
....
My problem is, that the URL have to load synchronous and not asynchronous
No you can't do it synchronously, but using async/await you can pretend it.
For this, You can use a method something like this (you can even write it as an extension method)
await Navigate(webBrowser1, "http://stackoverflow.com");
DoSomethingAfterNavigationCompleted();
Task Navigate(WebBrowser wb,string url)
{
var tcs = new TaskCompletionSource<object>();
WebBrowserDocumentCompletedEventHandler documentCompleted = null;
documentCompleted = (o, s) =>
{
wb.DocumentCompleted -= documentCompleted;
tcs.TrySetResult(null);
};
wb.DocumentCompleted += documentCompleted;
wb.Navigate(url);
return tcs.Task;
}
I am trying to get tweets from my twitter account and display the top most tweet on my shell tile. So i created a background agent(periodic task) to do it. Every 30 mins the background agent should access my twitter timeline and get my top most tweet and display it in the tile. The problem is my tile is getting updated only once i.e when i start the agent, afterwards its not getting updated.
Here is my background agent code:
protected override void OnInvoke(ScheduledTask task)
{
ShellToast popupMessage = new ShellToast()
{
Title = "My First Agent",
Content = "Background Task Launched",
};
WebClient twitter = new WebClient();
twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=dnivra26"));
popupMessage.Show();
}
private void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
return;
XElement xmlTweets = XElement.Parse(e.Result);
var message2 = (from tweet in xmlTweets.Descendants("status")
select tweet.Element("text").Value).FirstOrDefault();
UpdateAppTile(DateTime.Now.ToString() + message2.ToString());
}
private void UpdateAppTile(string message)
{
ShellTile appTile = ShellTile.ActiveTiles.First();
if (appTile != null)
{
StandardTileData tileData = new StandardTileData
{
BackContent = message
};
appTile.Update(tileData);
//NotifyComplete();
}
}
i am able to get the top tweet only once.
You must call NotifyComplete() when you are done. If not the scheduling of the task will be aborted. Why have you commented it out?
I never tried it before but this looks like a nice example to write a periodical background agent.
My guess is that you should call NotifyComplete() at the end, that tells the OS that your task is ready.
Periodical Agent on Windows Phone 7
As others have said, you need to call NotifyComplete() upon the completion. However, since you use a asynchronous event, WebClient.DownloadStringCompleted, you need to lock the execution until the download string have completed.
For this, I recommend to use the Task Parallel Library for Silverlight.
What you would need to do, is something like this:
protected override void OnInvoke(ScheduledTask task)
{
ShellToast popupMessage = new ShellToast()
{
Title = "My First Agent",
Content = "Background Task Launched",
};
popupMessage.Show();
UpdateTile().ContinueWith(x => NotifyComplete());
}
private Task<bool> UpdateTile()
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.AttachedToParent);
WebClient twitter = new WebClient();
twitter.DownloadStringCompleted += (sender, e) =>
{
if (e.Error != null)
{
tcs.TrySetResult(true);
}
else
{
XElement xmlTweets = XElement.Parse(e.Result);
var message2 = xmlTweets.Descendants("status")
.Select(x => x.Element("text").Value).FirstOrDefault();
ShellTile appTile = ShellTile.ActiveTiles.First();
if (appTile != null)
{
StandardTileData tileData = new StandardTileData
{
BackContent = DateTime.Now.ToString() + message2.ToString()
};
appTile.Update(tileData);
tcs.TrySetResult(true);
}
else
{
tcs.TrySetResult(true);
}
}
};
twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=dnivra26"));
return tcs.Task;
}