Could this code be causing a memory leak? - c#

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.

Related

SQLite-NET-PCL Stuck on CreateTableAsync<Type>()

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

Serialization taking way too long

So I am working on an UWP app that I'm creating in a portable class library (Xamarin). I need to save information that are typed in (e.g. in TextBoxes) by a user in an xml file.
Therefore I created a class in PCL where I get the info from those TextBoxes:
namespace myProject
{
public class XMLData
{
[XmlRoot("MyRootElement")]
public class MyRootElement
{
[XmlAttribute("MyAttribute1")] //name of the xml element
public string MyAttribute1 //name of a textboxt e.g.
{
get;
set;
}
[XmlAttribute("MyAttribute2")]
public string MyAttribute2
{
get;
set;
}
[XmlElement("MyElement1")]
public string MyElement1
{
get;
set;
}
}
}
On each Page there is a "continue" button. When clicked, the data gets saved:
async void Continue_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new Page2());
XMLData.MyRootElement mre = new XMLData.MyRootElement
{
MyAttribute1 = editor1.ToString(),
MyAttribute2 = editor2.ToString(),
MyElement1 = editor3.ToString()
};
}
At the last button click the file should be created and saved:
private void CreateandSave_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
XMLData xmldat = new XMLData();
using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
{
XmlSerializer serializer = new XmlSerializer(typeof(XMLData));
serializer.Serialize(sw, xmldat);
}
}
Here my code in the UWP class (for dependencyService I created a class called FileHelper to get a stream and create a saving location + file)
namespace myProject.UWP
{
public class FileHelper: IFileHelper //IFileHelper is a simple interface I made with the Stream MakeFileStream(); method in it
{
public async Task<IRandomAccessStream> MakeFileStreamAsync()
{
StorageFolder sf = KnownFolders.DocumentsLibrary;
var file = await sf.CreateFileAsync("data.xml", CreationCollisionOption.OpenIfExists);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
return stream;
}
}
Stream IFileHelper.MakeFileStream()
{
var task = MakeFileStreamAsync();
task.Wait();
return task.Result.AsStreamForWrite();
}
}
}
The problem is that whenever I reach the CreateandSave button and click on it, the app just freezes. No errors, nothing, everything looks fine. After I break up the debugging I can see that there is an xml file created in the folder I wanted but it's empty (0 bytes). What's wrong with the code? Anyone an idea?
In this piece of code:
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
return stream;
}
you are returning an instance created by the using block. It will be disposed before the return, and as a result, you're returning a disposed object.
Change it to just return stream. The StreamWriter you use is in a using block itself, so during its disposal it will dispose the underlying stream:
The StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called.
Your freeze problem is that the Task.Wait() instruction blocks the main UI thread until MakeFileStreamAsync() method finishes execution.
You should make this method async and returning a Task<Stream> type, and call the MakeFileStreamAsync method using the await keyword:
async Task<Stream> IFileHelper.MakeFileStream()
{
var stream = await MakeFileStreamAsync();
return stream.AsStreamForWrite();
}
Therefore, your code for the creation click should be something like:
private void CreateandSave_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
XMLData xmldat = new XMLData();
// Here you should await your `s` Task:
await s;
using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
{
XmlSerializer serializer = new XmlSerializer(typeof(XMLData));
serializer.Serialize(sw, xmldat);
}
}
Hope it helps!
EDIT:
Regarding your empty xml file problem, I think it's because you save the data in another page, but don't do anything with it. Which means you lose them when loading the Page2. Therefore, they are not available in the CreateandSave_Clicked method and you currently save an empty XMLData object.
The more intuitive way is passing the data to your Page2 constructor and add this datatype as a public property of Page2. So your Continue_Clicked method would look like:
async void Continue_Clicked(object sender, EventArgs e)
{
// Note you must REVERSE instructions here
// Create first your object (save the user data in it)
XMLData.MyRootElement mre = new XMLData.MyRootElement
{
MyAttribute1 = editor1.ToString(),
MyAttribute2 = editor2.ToString(),
MyElement1 = editor3.ToString()
};
// Pass it to Page2 through the constructor
await Navigation.PushAsync(new Page2(mre));
}
And so the Page2 class/contructor becomes:
public class Page2 : SomeParentClass
{
...
// add your XMLData property
public XMLData.MyRootElement mre { get; set; }
...
// the constructor
public Page2(XMLData.MyRootElement data){
// Save the user data in xmldat property. So this data could be reused later.
this.mre = data;
}
}
Then, if it's Page2 that is responsible of creating and saving the XML file itself, you can reuse the object you passed through the constructor.
private void CreateandSave_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
// You want to remove that here as you created a public property of type XMLData.MyRootElement (called mre) holding user data instead
//XMLData xmldat = new XMLData();
// Here you should await your `s` Task:
await s;
using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
{
// Change of serialized type here
XmlSerializer serializer = new XmlSerializer(typeof(XMLData.MyRootElement));
// Here, just seralize the property saved through constructor
serializer.Serialize(sw, mre);
}
}
Note that if it's Page3 or whatever Page that is responsible of saving the XML file, just keep passing the saved user data from page to page and save them once when you call the CreateandSave_Clicked method.
Another point is that I am not sure of the usefulness of nesting MyRootElement class into XMLData. You could just remove the XMLData nesting class and just keep the MyRootElement class as the "main" one.
You could also achieve the same using static fields as well and therefore not using the constructor. But from my point of view, it's less intuitive and moreover, less clean.
Enjoy,

Xamarin.Forms with Net.Http [closed]

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;
}
}
}

Multi threading using async/await and httpclient in c#

I wrote a console application for downloading YouTube preview-images.But I think this program is running synchronously instead async. What did I do wrong and how do I make multi-loading files from web use async/await?
using System;
using System.IO;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace YoutubePreviewer
{
class Node
{
public string Path { get; private set; }
public string Title { get; private set; }
public string Source { get; private set; }
public string Id { get; private set; }
public Previews Previews { get; set; }
public Node(string p, string t, string s, string i)
{
Path = p;
Title = t;
Source = s;
Id = i;
}
}
class Previews
{
public string[] Urls { get; private set; }
public static Previews Get(Node n)
{
string[] resolutions = {"default", "hqdefault", "mqdefault", "maxresdefault"};
for (int i = 0; i < resolutions.Length; i++)
{
string end = resolutions[i] + ".jpg";
resolutions[i] = "https://img.youtube.com/vi/" + n.Id + "/" + resolutions[i] + ".jpg";
}
Previews pr = new Previews();
pr.Urls = resolutions;
return pr;
}
}
static class Operations
{
public static async Task<string> DownloadUrl(string address)
{
HttpClient http = new HttpClient();
return await http.GetStringAsync(address);
}
public static async Task<Node> Build(string url)
{
var source = await Operations.DownloadUrl(url);
var title = Regex.Match(source, "<title>(.*)</title>").Groups[1].Value;
var id = Regex.Match(url, #"watch\?v=(.+)").Groups[1].Value;
Node node = new Node(url, title, source, id);
node.Previews =await Task<Previews>.Factory.StartNew(()=>Previews.Get(node);
return node;
}
public static async Task WriteToDisk(Node n, string path = "C:/Downloads")
{
Console.WriteLine($"Starting downloading {n.Path} previews");
var securedName = string.Join("_", n.Title.Split(Path.GetInvalidFileNameChars()));
Directory.CreateDirectory(Path.Combine(path, securedName));
HttpClient http = new HttpClient();
foreach (var preview in n.Previews.Urls)
{
try
{
var arr = await http.GetByteArrayAsync(preview);
await Task.Delay(100);
string name = preview.Substring(preview.LastIndexOf("/") + 1);
using (FileStream fs = new FileStream(Path.Combine(path, securedName, name), FileMode.Create,
FileAccess.ReadWrite))
{
await fs.WriteAsync(arr, 0, arr.Length);
}
}
catch (Exception e)
{
Console.WriteLine($"Can't download and save preview {preview}");
Console.WriteLine(e.Message);
Console.WriteLine(new string('*', 12));
}
Console.WriteLine($"{preview} is saved!");
}
}
public static async Task Load(params string[] urls)
{
foreach (var url in urls)
{
Node n = await Build(url);
await WriteToDisk(n);
}
}
}
class Program
{
static void Main(string[] args)
{
Task t= Operations.Load(File.ReadAllLines("data.txt"));
Task.WaitAll(t);
Console.WriteLine("Done");
Console.ReadKey();
}
}
}
Your code is downloading URLs and writing them to disk one at a time. It is operating asynchronously, but serially.
If you want it to run asynchronously and concurrently, then you should be using something like Task.WhenAll:
public static async Task LoadAsync(params string[] urls)
{
var tasks = urls.Select(url => WriteToDisk(Build(url)));
await Task.WhenAll(tasks);
}
(This code assumes that Build is a synchronous method, which it should be).
There are also a number of unrelated issues that jump out:
node.Previews =await Task<Previews>.Factory.StartNew(()=>Previews.Get(node); is sending trivial work to the thread pool for no real reason. It should be node.Previews = Previews.Get(node);.
This means that Operations.Build doesn't need to be async, and indeed it shouldn't be.
You should be using a single shared instance of HttpClient rather than creating a new one for each request.
Task.WaitAll(t); is quite odd. It can be just t.Wait();.
await Task.Delay(100); is also unusual.
To add to #Stephen Cleary's excellent answer - as he said, this is technically running asynchronously, but that's not actually helping you at all because it's doing things serially - i.e. it is asynchronous but the performance is no better than if it actually was just running synchronously.
The key thing to remember here is that async/await will only help you if it actually allows the machine to do more work than it would have done otherwise in a certain amount of time (or if it allows the machine to finish a certain set of tasks faster).
Just to use my favorite analogy: suppose that you're at a restaurant with 9 other people. When the waiter comes by to take orders, the first guy he calls on isn't ready. Clearly, the most efficient thing to do would be to take the order of the other 9 people and then come back to him. Suppose, however, the first guy said, "it's OK to come back to me later, as long as you wait for me to be ready to order first." (This is essentially what you have above - "it's OK to come back to my method to process the download later, as long as you wait for me to finish the download first"). This analogy isn't perfect by any means, but I think that captures the essence of what needs to happen here.
The key thing to remember is that there's only an improvement here if the waiter can accomplish more in the same amount of time or can accomplish a certain set of tasks faster. In this case, he only saves time if he decreases the total amount of time that he spends taking the table's order.
One other thing to remember: it's acceptable to do something like Task.WaitAll(...) in a console application (as long as you're not using a synchronization context) but you want to make sure you don't do something like that in a WPF application or something else with a synchronization context as that could cause a deadlock.
It's very important to control concurrency, so you efficiently utilize the network channel and don't get throttled. So I would suggest to use the AsyncEnumerator NuGet Package with such code:
using System.Collections.Async;
static class Operations
{
public static async Task Load(params string[] urls)
{
await urls.ParallelForEachAsync(
async url =>
{
Node n = await Build(url);
await WriteToDisk(n);
},
maxDegreeOfParallelism: 10);
}
}

Object is not set in ViewModel class and control is directly move from data access class to View

I want to fetch first record from AboutUs table and display as a content label.
I have created 4 classes in MVVM pattern.
First is Model class AboutUs.cs
[Table("tblAboutUs")]
public class AboutUs
{
[PrimaryKey, AutoIncrement, NotNull]
public int IDP { get; set; }
public string Content { get; set; }
}
Second is Data Access class
SQLiteAboutUs.cs
public class SQLiteAboutUs
{
private static readonly AsyncLock Mutex = new AsyncLock();
private SQLiteAsyncConnection dbConn;
public int StatusCode { get; set; }
public SQLiteAboutUs(ISQLitePlatform sqlitePlatform, string dbPath)
{
if (dbConn == null)
{
var connectionFunc = new Func<SQLiteConnectionWithLock>(() =>
new SQLiteConnectionWithLock
(
sqlitePlatform,
new SQLiteConnectionString(dbPath, storeDateTimeAsTicks: false)
));
dbConn = new SQLiteAsyncConnection(connectionFunc);
dbConn.CreateTableAsync<Model.AboutUs>();
}
}
public SQLiteAboutUs()
{
}
public async Task Save(Model.AboutUs content)
{
using (await Mutex.LockAsync().ConfigureAwait(false))
{
StatusCode = 0;
await dbConn.InsertAsync(new Model.AboutUs { Content = content.Content });
StatusCode = 1;
}
//For Get first Row from Table
public async Task<Model.AboutUs> GetAllData()
{
return await dbConn.Table<Model.AboutUs>().Where(x => x.IDP == 1).FirstOrDefaultAsync();
}
}
Third class ViewModel Class
AboutUsViewModel.cs
public class AboutUsViewModel
{
readonly SQLiteAboutUs _db;
public string AboutUsContent { get; set; }
//public string AboutUs;
public AboutUsViewModel()
{
_db = new SQLiteAboutUs();
}
public async void FirstRecord()
{
Model.AboutUs obj = await _db.GetAllData();
this.AboutUsContent = obj.Content;
}
}
Forth one is Code behind file of my xaml pages.
AboutUs.xaml.cs
public partial class AboutUs : ContentPage
{
readonly AboutUsViewModel aboutUsViewModel;
public AboutUs()
{
InitializeComponent();
aboutUsViewModel = new AboutUsViewModel();
aboutUsViewModel.FirstRecord();
lblContent.Text = aboutUsViewModel.AboutUsContent;
}
}
I have debug code but problem is In AboutUsViewModel.cs class in FirstRecord Method object can not be set that's why AboutUsContent string property is also not set.
I can't figure out why my debugger directly jump from GetAllData() method in SQLiteAboutUs.cs to label.text in code behind file of view?
Welcome in the wonderfull world of asynchronicity. I encourage you to read carefully about how await is working: How and When to use `async` and `await`
It is not a blocking call. Thus you create the view AboutUs. It creates the ViewModel AboutUsViewModel. It calls
aboutUsViewModel.FirstRecord();
But does not wait for the call to be complete (dont't forget you marked your FirstRecord function as async...)
So it calls
Model.AboutUs obj = await _db.GetAllData();
And directly return to the caller because of the await operator.
That's why it directly jump to
lblContent.Text = aboutUsViewModel.AboutUsContent;
What you would like is Something like
await aboutUsViewModel.FirstRecord();
To wait the call to be complete before going to the next line. But of course you can't do that, because you are in a constructor and you can't have an async constructor. And calling a database (or actually anything that could likely failed) in a constructor is a bad practice anyway.
I would advise you to let only InitializeComponent() in the constructor, and then use Something like the OnDataLoaded() event of your view to perform your async call with a await.
Hope it helps.

Categories

Resources