Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Guys and ladies can you tell me what a problem in this code or in which way I can change Xamarin settings for successful code execution? Xam.Plugin.Connectivity.CrossConnectivity says:"Device is connected to the internet", but in any realization DownloadCountriesListAsync() stucks (UWP doesnt works, Android with selected INTERNET parameter in manifest too). This code is working in c# console app.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace radacodeTestApp
{
public class ListsDownloader
{
public List<Country> Countries { get; private set; }
public ListsDownloader()
{
Countries = new List<Country>();
var task = DownloadCountriesListAsync();
}
public async Task<bool> DownloadCountriesListAsync()
{
try
{
var vkjsonResponse = await GetResponse(#"https://api.vk.com/api.php?oauth=1&method=database.getCountries&need_all=1&v=5.60");
var jsonObject = JObject.Parse(vkjsonResponse);
foreach (var jO in jsonObject["response"]["items"])
Countries.Add(JsonConvert.DeserializeObject<Country>(jO.ToString()));
}
catch (OperationCanceledException)
{
return false;
}
return true;
}
public async Task<string> GetResponse(string url)
{
using (var httpClient = new HttpClient())
return await httpClient.GetStringAsync(url);
}
}
public class Country
{
public int Cid { get; set; }
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
}
Call the DownloadCountriesListAsync method from a background thread; your code is currently calling DownloadCountriesListAsync from the Main Thread (also known as the UIThread), which can cause the UIThread to freeze.
I've updated your code below to show how to call the DownloadCountriesListAsync method from a background thread.
Async/Await is a tricky beast. Marking a method async doesn't mean it will automatically run on a background thread; it just means that the async method has the capability to yield its process to its parent thread until its process is complete. #Clancey did a great presentation on Async/Await at the most recent Xamarin conference. I highly recommend it!
https://www.youtube.com/watch?v=jgxJbshvCXQ
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace radacodeTestApp
{
public class ListsDownloader
{
public List<Country> Countries { get; private set; }
public ListsDownloader()
{
Countries = new List<Country>();
Task.Run(async () => await DownloadCountriesListAsync());
}
public async Task<bool> DownloadCountriesListAsync()
{
try
{
var vkjsonResponse = await GetResponse(#"https://api.vk.com/api.php?oauth=1&method=database.getCountries&need_all=1&v=5.60");
var jsonObject = JObject.Parse(vkjsonResponse);
foreach (var jO in jsonObject["response"]["items"])
Countries.Add(JsonConvert.DeserializeObject<Country>(jO.ToString()));
}
catch (OperationCanceledException)
{
return false;
}
return true;
}
public async Task<string> GetResponse(string url)
{
using (var httpClient = new HttpClient())
return await httpClient.GetStringAsync(url);
}
}
public class Country
{
public int Cid { get; set; }
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
}
Related
I'm working on a Xamarin mobile app using .NET Framework, and SQLite.NET-PCL. My XAML Form uses MVVM architecture, on my main form I am attempting to display all movies in a ListView, first I made sure the view itself works correctly, and now I am attempting to connect it to my DB service. When the DB service initializes and attempts to create any table using CreateTableAsyc() the program gets stuck in that function even after appearing to succeed.
Initially, I thought it was due to using created classes as properties, which SQLite does not support, so I corrected that. Next, I checked if it was due to not using Hardware Acceleration so I enabled it. I then attempted to debug using ContinueWith() and printed the result to the debug console, it says 'Migrated' but never exits the function. Xamarin XAML Hot Reload then times out. What could be causing this?
Example of one of the Types:
using SQLite;
public class Movie
{
public Movie() {}
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
public DateTime Length { get; set; }
}
DBService
using Xamarin.Essentials;
public static class DBService
{
private static SQLiteAsyncConnection dbConn;
public static async Task Init()
{
if (dbConn != null)
{
return;
}
string databasePath = Path.Combine(FileSystem.AppDataDirectory, "Movies.db");
dbConn = new SQLiteAsyncConnection(databasePath, false);
await dbConn.CreateTableAsync<Movie>().ContinueWith(results =>
{
Debug.WriteLine(results.Result.ToString()); // This prints migrated
});
Debug.WriteLine("After table created"); // This never prints
}
public static async Task<IEnumerable<Movie>> GetMovies()
{
await Init();
return await dbConn.Table<Movie>().ToListAsync();
}
}
MovieViewModel (View Model for main view)
public class MovieViewModel
{
public List<Movie> Movies { get; set; }
public MovieViewModel ()
{
Movies = (List<Movie>)DBService.GetMovies().Result;
}
}
Wrap awaitable in something that runs after constructor returns:
MainThread.BeginInvoke(async () => Movies = (await DBService.GetMovies()).ToList() );
Optional:
GetMovies could return a List, to match Movies declaration. Then would not need .ToList(), or (List<Movie>) cast:
public static async Task<List<Movie>> GetMovies() { ... }
MainThread.BeginInvoke(async () => Movies = await DBService.GetMovies() );
I have pulled some information using newtonsoft.json and HttpClient:
public class Main {
static async Task Main() {
using(var client = new HttpClient() {
url = "webapilink goes here";
var response = await client.GetStringAsync(url);
apiStuff(the class i used to match the JSON format) variableName =
JsonConvert.DeserializeObject<apiStuff>(response);
}
}
}
I want to get the variable "variableName" to a seperate class, and for the life of me, cannot figure it out. I get an error when trying to use it anywhere outside of the HttpClient part.
As written, your async method would return void if it was not async. But it could be rewritten to return an instance of apiStuff, so that other methods (and other classes) could get that instance of apiStuff and use it:
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class MainClass
{
public static async Task<apiStuff> Main()
{
using (var client = new HttpClient())
{
string url = "webapilink goes here";
var response = await client.GetStringAsync(url);
apiStuff variableName =
JsonConvert.DeserializeObject<apiStuff>(response);
return variableName;
}
}
}
public class SecondClass
{
public static async Task GetApiStuff()
{
apiStuff variableName = await MainClass.Main();
// do more work with variableName
}
}
public class apiStuff { /* defined elsewhere */ }
If this wasn't what you were asking about, please clarify in the original question. (And I assume the method is not really named Main - could be confusing.)
As a caveat I'm a novice with Rx (2 weeks) and have been experimenting with using Rx, RxUI and Roland Pheasant's DynamicData.
I have a service that initially loads data from local persistence and then, upon some user (or system) instruction will contact the server (TriggerServer in the example) to get additional or replacement data. The solution I've come up with uses a Subject and I've come across many a site discussing the pros/cons of using them. Although I understand the basics of hot/cold it's all based on reading rather than real world.
So, using the below as a simplified version, is this 'right' way of going about this problem or is there something I haven't properly understood somewhere?
NB: I'm not sure how important it is, but the actual code is taken from a Xamarin.Forms app, that uses RxUI, the user input being a ReactiveCommand.
Example:
using DynamicData;
using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
public class MyService : IDisposable
{
private CompositeDisposable _cleanup;
private Subject<Unit> _serverSubject = new Subject<Unit>();
public MyService()
{
var data = Initialise().Publish();
AllData = data.AsObservableCache();
_cleanup = new CompositeDisposable(AllData, data.Connect());
}
public IObservableCache<MyData, Guid> AllData { get; }
public void TriggerServer()
{
// This is what I'm not sure about...
_serverSubject.OnNext(Unit.Default);
}
private IObservable<IChangeSet<MyData, Guid>> Initialise()
{
return ObservableChangeSet.Create<MyData, Guid>(async cache =>
{
// inital load - is this okay?
cache.AddOrUpdate(await LoadLocalData());
// is this a valid way of doing this?
var sync = _serverSubject.Select(_ => GetDataFromServer())
.Subscribe(async task =>
{
var data = await task.ConfigureAwait(false);
cache.AddOrUpdate(data);
});
return new CompositeDisposable(sync);
}, d=> d.Id);
}
private IObservable<MyData> LoadLocalData()
{
return Observable.Timer(TimeSpan.FromSeconds(3)).Select(_ => new MyData("localdata"));
}
private async Task<MyData> GetDataFromServer()
{
await Task.Delay(2000).ConfigureAwait(true);
return new MyData("serverdata");
}
public void Dispose()
{
_cleanup?.Dispose();
}
}
public class MyData
{
public MyData(string value)
{
Value = value;
}
public Guid Id { get; } = Guid.NewGuid();
public string Value { get; set; }
}
And a simple Console app to run:
public static class TestProgram
{
public static void Main()
{
var service = new MyService();
service.AllData.Connect()
.Bind(out var myData)
.Subscribe(_=> Console.WriteLine("data in"), ()=> Console.WriteLine("COMPLETE"));
while (Continue())
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine($"Triggering Server Call, current data is: {string.Join(", ", myData.Select(x=> x.Value))}");
service.TriggerServer();
}
}
private static bool Continue()
{
Console.WriteLine("Press any key to call server, x to exit");
var key = Console.ReadKey();
return key.Key != ConsoleKey.X;
}
}
Looks very good for first try with Rx
I would suggest few changes:
1) Remove the Initialize() call from the constructor and make it a public method - helps a lot with unit tests and now you can await it if you need to
public static void Main()
{
var service = new MyService();
service.Initialize();
2) Add Throttle to you trigger - this fixes parallel calls to the server returning the same results
3) Don't do anything that can throw in Subscribe, use Do instead:
var sync = _serverSubject
.Throttle(Timespan.FromSeconds(0.5), RxApp.TaskPoolScheduler) // you can pass a scheduler via arguments, or use TestScheduler in unit tests to make time pass faster
.Do(async _ =>
{
var data = await GetDataFromServer().ConfigureAwait(false); // I just think this is more readable, your way was also correct
cache.AddOrUpdate(data);
})
// .Retry(); // or anything alese to handle failures
.Subscribe();
I'm putting what I've come to as my solution just in case there's others that find this while they're wandering the internets.
I ended up removing the Subjects all together and chaining together several SourceCache, so when one changed it pushed into the other and so on. I've removed some code for brevity:
public class MyService : IDisposable
{
private SourceCache<MyData, Guid> _localCache = new SourceCache<MyData, Guid>(x=> x.Id);
private SourceCache<MyData, Guid> _serverCache = new SourceCache<MyData, Guid>(x=> x.Id);
public MyService()
{
var localdata = _localCache.Connect();
var serverdata = _serverCache.Connect();
var alldata = localdata.Merge(serverdata);
AllData = alldata.AsObservableCache();
}
public IObservableCache<MyData, Guid> AllData { get; }
public IObservable<Unit> TriggerLocal()
{
return LoadLocalAsync().ToObservable();
}
public IObservable<Unit> TriggerServer()
{
return LoadServerAsync().ToObservable();
}
}
EDIT: I've changed this again to remove any chaining of caches - I just manage the one cache internally. Lesson is not to post too early.
I'm learning how to create a local database on mobile devices by using SQLite.Net.Async. I'm following a tutorial step by step but something wrong with my database path. The error message says 'Argument 1 cannot convert from string to System.Func SQLite.Net.SQLiteConnectionWithLock'. I was confused because this code works well on the tutorial sample.
This is my code:
using SQLite.Net.Async;
namespace ToDoList
{
public class TodoItemDatabase
{
readonly SQLiteAsyncConnection asyncdatabase;
public TodoItemDatabase (string dbPath)
{
asyncdatabase = new SQLiteAsyncConnection(dbPath);
//I haven't done further because I was stucked by dbPath error
}
}
}
The sample code works well:
using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
namespace Todo
{
public class TodoItemDatabase
{
readonly SQLiteAsyncConnection database;
public TodoItemDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<TodoItem>().Wait();
}
public Task<List<TodoItem>> GetItemsAsync()
{
return database.Table<TodoItem>().ToListAsync();
}
public Task<List<TodoItem>> GetItemsNotDoneAsync()
{
return database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
}
public Task<TodoItem> GetItemAsync(int id)
{
return database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
}
public Task<int> SaveItemAsync(TodoItem item)
{
if (item.ID != 0)
{
return database.UpdateAsync(item);
}
else {
return database.InsertAsync(item);
}
}
public Task<int> DeleteItemAsync(TodoItem item)
{
return database.DeleteAsync(item);
}
}
}
This is the package which is been used in the sample application sqlite-net-pcl
My app is leaking about 6MB of RAM every time I navigate to or from a page and I think this is what the cause is:
I've got the following class:
public class JSONclasses
{
public class RootObject
{
public ReleaseDates release_dates { get; set; }
public int id { get; set; }
public List<Result> results { get; set; }
}
//Another few hundred variables
}
And am using it in the following way on my pages:
using static TMDBclient.JSONclasses;
namespace TMDBclient
{
public sealed partial class MainPage : AltPage
{
public string defaultbackdrop;
public string defaultposter;
public int budget;
public long revenue;
//A copy of all the variables from the JSONclasses that I need
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string result = await SetData();
myTextBlock.Text = defaultbackdrop;
//Continue setting all the data to the UI and doing various operations on it.
}
public async Task<string> GetMovieData(string id)
{
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
using (Stream stream = response.Content.ReadAsStreamAsync().Result)
{
using (StreamReader reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
var root = JsonConvert.DeserializeObject<RootObject>(json);
defaultbackdrop = root.BackdropURL;
id = root.id;
//Continue setting all the variables in the same manner.
}
}
}
}
}
}
To recap, I'm:
Declaring JSONclasses as a static class
Setting tons of variables
Using those variables in my UI and performing various operations on them.
On another note, I've set NavigationCacheMode to Disabled, so it shouldn't be saving the entire page in memory.
Do you reckon this is what's leaking all that precious memory? I can tell this is particularly badly optimized code, and a few of you probably threw up a little inside your mouths reading it, but how can I fix it?
According to visual studio, there's definitely some And that's only by going back and forth to the same page a few dozen times.
Also, I've used ReSharper dotMemory to diagnose where the memory leak is coming from, and I'm 98% sure this is the source of the problem.