Loading collection view with task async method - c#

I am trying to load thumbnails with async task method with depency service :
In my pcl page I have this :
protected override void OnAppearing()
{
Device.BeginInvokeOnMainThread(() => UserDialogs.Instance.ShowLoading("Loading...", MaskType.Black));
Task.Run(async () =>
{
directoryPath = await getThumbnails.GetBitmaps(fileInfo.FullName);
List<ThumbnailsModel> thumbnailsModels = new List<ThumbnailsModel>();
int i = 1;
Directory.GetFiles(directoryPath).ToList<string>().ForEach(delegate (string thumbnailsEmplacement)
{
thumbnailsModels.Add(new ThumbnailsModel(i, thumbnailsEmplacement));
i++;
});
CollectionViewThumbnails.ItemsSource = thumbnailsModels;
}).ContinueWith(result => Device.BeginInvokeOnMainThread(() =>
{
UserDialogs.Instance.HideLoading();
}
)
);
}
My method to get the thumbnails :
public async Task<string> GetBitmaps(string filePath)
{
//TODO-- WORK ON THIS
var appDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
string fileName = System.IO.Path.GetFileNameWithoutExtension(filePath);
string directoryPath = System.IO.Path.Combine(appDirectory, "thumbnailsTemp", System.IO.Path.GetFileNameWithoutExtension(fileName));
var stream = new MemoryStream();
using (Stream resourceStream = new FileStream(filePath, FileMode.Open))
{
resourceStream.CopyTo(stream);
}
Document document = new Document(stream);
int count = document.Pages.Count;
for(int i = 0; i<= count; i++) {
TallComponents.PDF.Rasterizer.Page page = document.Pages[0];
using (var outputStream = new FileStream(System.IO.Path.Combine(directoryPath, fileName + "Thumbnails" + i + ".png"), FileMode.Create, FileAccess.Write))
{
await Task.Run(() =>
{
page.SaveAsBitmap(outputStream, CompressFormat.Png, 5);
});
}
}
return directoryPath;
}
The problem is that my application is going in my Dependency service method then going back in my pcl OnAppearing method before the thumbnails are done and going at this line
UserDialogs.Instance.HideLoading();

Seems like you have an unhandled exception. That continuation will run even if an exception is thrown on the Task you're continuing.
This can be changed using something like TaskContinuationOptions.OnlyOnRanToCompleted (and others) in the overload for ContinueWith. The default is TaskContinuationOptions.None if not specified.
Alternatively, you can access result.Exception in your continuation if you want it to run on failure and handle it.

Related

Error with assign StorageFile with CreateFileAsync in UWP

I'm suffered from making text files with UWP.
I've experienced how to make a text file in UWP.
but when I tried to make own my program, I got some problems with Creating File. I don't know where is the reason from. the lack of my knowledge about C# class p? or misuse of builtin Class(like storageFile etc...) function?
I made my application to read files from device and save as a another file.
but It doesn't work at all.
when I use break point to figure out what is problem.
Picture1. outputFile is setted as a null
you can see i.outputFile(type StorageFile) is setted as a null. but with my intent, it shouldn't be remained as a null.
because I set its(i's) outputFile with member function called "setOutFile(StorageFolder)". you can see in the picture above.
below is my source code which handle my ClassType. it stops when meet FileIO.WriteTextAsync ... because i.outPutFile is null.
public async Task<List<string>> DoRandom(FileLists fl, StorageFolder folder)
{
FileLists retLists = new FileLists();
List<string> encodingList = new List<string>();
foreach (UploadedFile i in fl)
{
// read stream from storagefile
Stream s = await i.originFile.OpenStreamForReadAsync();
// streamreader from stream
StreamReader sr = new StreamReader(s, Encoding.ASCII);
i.setOutFile(folder);
if (sr.CurrentEncoding == Encoding.ASCII)
{
encodingList.Add("ASCII " + i.outputName);
}
string str = await sr.ReadToEndAsync();
StringBuilder stringBuilder = new StringBuilder(str);
if (Option1)
{
doOption1(stringBuilder);
}
await FileIO.WriteTextAsync(i.outputFile, stringBuilder.ToString());
if (Option1);
};
return encodingList;
}
in Uploaded Class (you can just see setOutFile function).
class UploadedFile
{
public StorageFile originFile;
public StorageFile outputFile { get; set; }
public string inputName {get; private set; }
public string outputName {get; private set; }
public string fileSz{get; private set;}
public UploadedFile(StorageFile storageFile)
{
originFile = storageFile;
inputName = storageFile.Name;
}
public async Task GetFileSz()
{
var bp = await originFile.GetBasicPropertiesAsync();
this.fileSz = string.Format("{0:n0} Byte", bp.Size);
}
public async void setOutFile(StorageFolder folder)
{
var rand = new Random();
string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
StringBuilder result = new StringBuilder(13);
for (int i=0; i<13; i++)
{
result.Append(charset[rand.Next(charset.Length)]);
}
StringBuilder outputName = new StringBuilder();
outputName.Append(inputName.Substring(0, inputName.Length - 4));
outputName.Append("_");
outputName.Append(result);
outputName.Append(".txt");
this.outputName = outputName.ToString();
outputFile = await folder.CreateFileAsync(outputName.ToString(), CreationCollisionOption.ReplaceExisting);
for (int i = 0; i <= 10000; i++) // break point
i++;
}
when I insert a assignment(below) in constructor.
outputFile = storageFile;
it barely make a file in target directory with purposed fileName. but it has no data in it!!!..... I tried with below source Code but it has no data in it, either.
await FileIO.WriteTextAsync(i.outputFile, "constant String");
my app makes file with edited constructor, but it has no data in it.
I don't know what is my problem, C# Class syntax or ...what?
Thanks all of you, guys who commented on my posts.
I desperately tried to figure out what is problem, I met. I carefully read your comments and I think your advice is definitely good.
but the problem that I met was, Straightforwardly, sync,async- matter thing. I struggled with this problem with more than 5 hours, and I found the class's member function setOutfile has async function "StorageFoder.CreateFileAsync" and when the machine read that statement, It create asynchronously and begin to write some text(implemented in handler class) on It even It's not created.
...In myType Class, I changed my member function's type from async void to async Task.
public async Task setOutFile(StorageFolder folder)
{
var rand = new Random();
string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
StringBuilder result = new StringBuilder(13);
for (int i=0; i<13; i++)
{
result.Append(charset[rand.Next(charset.Length)]);
}
StringBuilder outputName = new StringBuilder();
outputName.Append(inputName.Substring(0, inputName.Length - 4));
outputName.Append("_");
outputName.Append(result);
outputName.Append(".txt");
this.outputName = outputName.ToString();
if (folder != null)
{
outputFile = await folder.CreateFileAsync(outputName.ToString(), CreationCollisionOption.ReplaceExisting);
}
}
and then in handler class member function, i just added await keyword before i.setOutFile(StorageFolder ..)
public async Task<List<string>> DoRandom(FileLists fl, StorageFolder folder)
{
FileLists retLists = new FileLists();
List<string> encodingList = new List<string>();
foreach (UploadedFile i in fl)
{
// read stream from storagefile
Stream s = await i.originFile.OpenStreamForReadAsync();
// streamreader from stream
StreamReader sr = new StreamReader(s, Encoding.ASCII);
await i.setOutFile(folder) ; // wait until setOutFile ends
if (sr.CurrentEncoding == Encoding.ASCII)
{
encodingList.Add("ASCII " + i.outputName);
}
string str = await sr.ReadToEndAsync();
StringBuilder stringBuilder = new StringBuilder(str);
if (Option1)
{
doOption1(stringBuilder);
}
await FileIO.WriteTextAsync(i.outputFile, stringBuilder.ToString());
if (Option1);
};
return encodingList;
}
and It works, thanks all you guys.

Capture Slow Output from Method

I have a slow running utility method that logs one line of output at a time. I need to be able to output each of those lines and then read them from other locations in code. I have attempted using Tasks and Streams similar to the code below:
public static Task SlowOutput(Stream output)
{
Task result = new Task(() =>
{
using(StreamWriter sw = new StreamWriter(output))
{
for(var i = 0; i < int.MaxValue; i++)
{
sw.WriteLine(i.ToString());
System.Threading.Thread.Sleep(1000);
}
}
}
}
And then called like this:
MemoryStream ms = new MemoryStream();
var t = SlowOutput(ms);
using (var sr = new StreamReader(ms))
{
while (!t.IsCompleted)
{
Console.WriteLine(sr.ReadLine())
}
}
But of course, sr.ReadLine() is always empty because as soon as the method's sw.WriteLine() is called, it changes the position of the underlying stream to the end.
What I'm trying to do is pipe the output of the stream by maybe queueing up the characters that the method outputs and then consuming them from outside the method. Streams don't seem to be the way to go.
Is there a generally accepted way to do this?
What I would do is switch to a BlockingCollection<String>.
public static Task SlowOutput(BlockingCollection<string> output)
{
return Task.Run(() =>
{
for(var i = 0; i < int.MaxValue; i++)
{
output.Add(i);
System.Threading.Thread.Sleep(1000);
}
output.Complete​Adding();
}
}
consumed by
var bc = BlockingCollection<string>();
SlowOutput(bc);
foreach(var line in bc.GetConsumingEnumerable()) //Blocks till a item is added to the collection. Leaves the foreach loop after CompleteAdding() is called and there are no more items to be processed.
{
Console.WriteLine(line)
}

Calculate loading time of multiple websites using AsyncTask in Xamarin Android and C#.Net

I have a string array which contains addresses of websites:
string[] arr = new string[]
{
"https://www/google.com",
"https://www.yahoo.com",
"https://www.microsoft.com"
};
I have to send these URLs as argument to the asynctask method so that I will be able to calculate the loading time of each website. I don't have to show the website pages, so I am not using webview.
I can use stopwatch or httprequest to calculate the loading time and my ultimate goal is that all the websites need to start loading at the same time asynchronously, and output has to look like the following
Loading time
google - 00:00:04:092345 (hr:min:sec:millisec) yahoo - 00:00:06:028458
How can I send an array to asynctask and how I can generate loading time without using await?
Here is a brief solution of what you could do.
This is not complete nor perfect. It will will give you the loading time of one URL. Also there is a suggestion of how you could extend this to multiple URLs.
You will need a WebView, either in code or from UI.
Load the URL into the WebView using webview.LoadUrl("https://www/google.com");.
Create a new class by extending it from WebViewClient as follows:
public class myWebViewClient : WebViewClient
{
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
Console.WriteLine("OnPageFinished for url : " + url + " at : " + DateTime.Now);
}
}
In your OnCreate() method add the following line of code :
webview.SetWebViewClient(new myWebViewClient());
So from here what you have to do is, Create a Dictionary with URL as key and Loading time as value. Set all the loading time to 0 initially. Update the value corresponding to each URL in the OnPageFinished(). Create an async Task function which would return you the populated dictionary.
public async Task<Dictionary<string, double>> myAsyncFunction()
{
await Task.Delay(5); //to make it async
//Wait till all the OnPageFinished events have fired.
while (myDictionary.Any(x=>x.Value == 0) == true)
{
//there are still websites which have not fully loaded.
await Task.Delay(1); //wait a millisecond before checking again
}
return myDictionary;
}
You can call myAsyncFunction() in a seprate thread than your UI and implement the ContinueWith() or just let it run in a separate thread and write that output into somewhere that you can check when required.
eg : Task.Run(async () => await myAsyncFunction());
UPDATE : based on OP's comments
In the UI thread :
var myClassList = new List<myClass>
{
new myClass{URL = "https://www/google.com", TimeTaken = null},
new myClass{URL = "https://www.yahoo.com", TimeTaken = null},
new myClass{URL = "https://www.microsoft.com", TimeTaken = null}
};
Console.WriteLine("Started at : " + DateTime.Now.ToShortTimeString());
var business = new BusinessLogic();
var loadtimetask = business.GetLoadTimeTakenAsync(myClassList);
await loadtimetask;
Console.WriteLine("Completed at : " + DateTime.Now.ToShortTimeString());
And implementation class :
public async Task<List<myClass>> GetLoadTimeTakenAsync(List<myClass> myClassList)
{
Parallel.ForEach(myClassList, myClassObj =>
{
using (var client = new HttpClient())
{
myClassObj.StartTime = DateTime.Now;
var stream = client.GetStreamAsync(myClassObj.URL)
.ContinueWith((s) =>
{
if (s.IsCompleted)
{
var myClassObjCompleted = myClassList.Where(x => x.URL == myClassObj.URL).First();
myClassObjCompleted.EndTime = DateTime.Now;
myClassObjCompleted.TimeTaken = myClassObj.EndTime - myClassObj.StartTime;
}
});
Task.Run(async () => await stream);
}
});
while (myClassList.Any(x => x.TimeTaken == null))
{
await Task.Delay(1);
}
return myClassList;
}
//Create TextView to display status of Wifi
TextView wifitext = FindViewById<TextView>(Resource.Id.WifiTextView);
//Configuring Wifi connection
var connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
var activeConnection = connectivityManager.ActiveNetworkInfo;
if (activeConnection != null && activeConnection.IsConnected)
{
wifitext.Text = "WIFI AVAILABLE";
string[] urladdress = new string[] { "https://www.google.com/", "https://www.yahoo.com/"};
for (int i = 0; i < urladdress.Length; i++)
{
string url = urladdress[i];
//Call async method
Task returnedTask = Task_MethodAsync(url);
}
}
else
wifitext.Text = "WIFI UNAVAILABLE";
}
public async Task Task_MethodAsync(string url)
{
LinearLayout ll = FindViewById<LinearLayout>(Resource.Id.linearLayout1);
WebClient client = new WebClient();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Stream listurl = client.OpenRead(url);
StreamReader reader = new StreamReader(listurl);
stopwatch.Stop();
// listurl.Close();
var time = Convert.ToString(stopwatch.Elapsed);

Why do some files result in 0KB when using WebClient.DownloadFileTaskAsync

I'm trying to download multiple files from an FTP server using WebClient.DownloadFileTaskAsync and repeatedly have the issue that several files end up being 0KB.
I've tried different suggested solutions but I just don't manage to get all files. What am I doing wrong?
class Program
{
static void Main()
{
Setup(); // This only sets up working folders etc
Task t = ProcessAsync();
t.ContinueWith(bl =>
{
if (bl.Status == TaskStatus.RanToCompletion)
Logger.Info("All done.");
else
Logger.Warn("Something went wrong.");
});
t.Wait();
}
private static void Setup() {...}
static async Task<bool> ProcessAsync()
{
var c = new Catalog();
var maxItems = Settings.GetInt("maxItems");
Logger.Info((maxItems == 0 ? "Processing all items" : "Processing first {0} items"), maxItems);
await c.ProcessCatalogAsync(maxItems);
return true; // This is not really used atm
}
}
public class Catalog
{
public async Task ProcessCatalogAsync(int maxItems)
{
var client = new Client();
var d = await client.GetFoldersAsync(_remoteFolder, maxItems);
var list = d as IList<string> ?? d.ToList();
Logger.Info("Found {0} folders", list.Count());
await ProcessFoldersAsync(list);
}
private async Task ProcessFoldersAsync(IEnumerable<string> list)
{
var client = new Client();
foreach (var mFolder in list.Select(folder => _folder + "/" + folder))
{
var items = await client.GetItemsAsync(mFolder);
var file = items.FirstOrDefault(n => n.ToLower().EndsWith(".xml"));
if (string.IsNullOrEmpty(file))
{
Logger.Warn("No metadata file found in {0}", mFolder);
continue;
}
await client.DownloadFileAsync(mFolder, file);
// Continue processing the received file...
}
}
}
public class Client
{
public async Task<IEnumerable<string>> GetItemsAsync(string subfolder)
{
return await GetFolderItemsAsync(subfolder, false);
}
public async Task<IEnumerable<string>> GetFoldersAsync(string subfolder, int maxItems)
{
var folders = await GetFolderItemsAsync(subfolder, true);
return maxItems == 0 ? folders : folders.Take(maxItems);
}
private async Task<IEnumerable<string>> GetFolderItemsAsync(string subfolder, bool onlyFolders)
{
// Downloads folder contents using WebRequest
}
public async Task DownloadFileAsync(string path, string file)
{
var remote = new Uri("ftp://" + _hostname + path + "/" + file);
var local = _workingFolder + #"\" + file;
using (var ftpClient = new WebClient { Credentials = new NetworkCredential(_username, _password) })
{
ftpClient.DownloadFileCompleted += (sender, e) => DownloadFileCompleted(sender, e, file);
await ftpClient.DownloadFileTaskAsync(remote, local);
}
}
public void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e, Uri remote, string local)
{
if (e.Error != null)
{
Logger.Warn("Failed downloading\n\t{0}\n\t{1}", file, e.Error.Message);
return;
}
Logger.Info("Downloaded \n\t{1}", file);
}
}
Seems like some of your tasks aren't completed. Try do like this (I do the same when putting bunch of files to ftp)
Create array of tasks for every file being downloaded. Run them in cycle like this:
Task[] tArray = new Task[DictToUpload.Count];
foreach (var pair in DictToUpload)
{
tArray[i] = Task.Factory.StartNew(()=>{/* some stuff */});
}
await Task.WhenAll(tArray);
Use await Task.WhenAll(taskArray) instead of await each task. This guarantees all your tasks are completed.
Learn some peace of TPL ;)

Task.WaitAll not waiting on other async methods

I'm asynchronously retrieving some rss articles with my Portable Class Library that uses the Microsoft.Bcl library (which doesn't have Task.WhenAll). Each article has a url to rss comments that I need to asynchronously retrieve as well.
The code below is my library. I call GetArticles() but it does not return any of the which creates a list of tasks that call GetComments() to asynchronously get the comments.
I've tried using Task.WaitAll in GetArticles to wait for the comments but it does not block the thread. Any help would be appreciated.
private const string ArticlesUri = "";
public async Task<List<ArticleBrief>> GetArticles()
{
var results = new List<ArticleBrief>();
try
{
var wfw = XNamespace.Get("http://wellformedweb.org/CommentAPI/");
var media = XNamespace.Get("http://search.yahoo.com/mrss/");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
var t = await WebHttpRequestAsync(ArticlesUri);
StringReader stringReader = new StringReader(t);
using (var xmlReader = System.Xml.XmlReader.Create(stringReader))
{
var doc = System.Xml.Linq.XDocument.Load(xmlReader);
results = (from e in doc.Element("rss").Element("channel").Elements("item")
select
new ArticleBrief()
{
Title = e.Element("title").Value,
Description = e.Element("description").Value,
Published = Convert.ToDateTime(e.Element("pubDate").Value),
Url = e.Element("link").Value,
CommentUri = e.Element(wfw + "commentRss").Value,
ThumbnailUri = e.Element(media + "thumbnail").FirstAttribute.Value,
Categories = GetCategoryElements(e.Elements("category")),
Creator = e.Element(dc + "creator").Value
}).ToList();
}
var tasks = new Queue<Task>();
foreach (var result in results)
{
tasks.Enqueue(
Task.Factory.StartNew(async ()=>
{
result.Comments = await GetComments(result.CommentUri);
}
));
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
// should do some other
// logging here. for now pass off
// exception to callback on UI
throw ex;
}
return results;
}
public async Task<List<Comment>> GetComments(string uri)
{
var results = new List<Comment>();
try
{
var wfw = XNamespace.Get("http://wellformedweb.org/CommentAPI/");
var media = XNamespace.Get("http://search.yahoo.com/mrss/");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
var t = await WebHttpRequestAsync(uri);
StringReader stringReader = new StringReader(t);
using (var xmlReader = System.Xml.XmlReader.Create(stringReader))
{
var doc = System.Xml.Linq.XDocument.Load(xmlReader);
results = (from e in doc.Element("rss").Element("channel").Elements("item")
select
new Comment()
{
Description = e.Element("description").Value,
Published = Convert.ToDateTime(e.Element("pubDate").Value),
Url = e.Element("link").Value,
Creator = e.Element(dc + "creator").Value
}).ToList();
}
}
catch (Exception ex)
{
// should do some other
// logging here. for now pass off
// exception to callback on UI
throw ex;
}
return results;
}
private static async Task<string> WebHttpRequestAsync(string url)
{
//TODO: look into getting
var request = WebRequest.Create(url);
request.Method = "GET";
var response = await request.GetResponseAsync();
return ReadStreamFromResponse(response);
}
private static string ReadStreamFromResponse(WebResponse response)
{
using (Stream responseStream = response.GetResponseStream())
using (StreamReader sr = new StreamReader(responseStream))
{
string strContent = sr.ReadToEnd();
return strContent;
}
}
private List<string> GetCategoryElements(IEnumerable<XElement> categories)
{
var listOfCategories = new List<string>();
foreach (var category in categories)
{
listOfCategories.Add(category.Value);
}
return listOfCategories;
}
Updated Code from Solution, just added .UnWrap() on the Enqueue method:
var tasks = new Queue<Task>();
foreach (var result in results)
{
tasks.Enqueue(
Task.Factory.StartNew(async ()=>
{
result.Comments = await GetComments(result.CommentUri);
}
).Unwrap());
}
Task.WaitAll(tasks.ToArray());
It is waiting appropriately. The problem is that you are creating a Task which creates another task (i.e. StartNew is returning a Task<Task> and you are only waiting on the outer Task which completes rather quickly (it completes before the inner Task is complete)).
The questions will be:
Do you really want that inner task?
If yes, then you can use Task.Unwrap to get a proxy task that represents the completion of both the inner and outer Task and use that to Wait on.
If no, then you could remove the use of async/await in StartNew so that there is not an inner task (I think this would be prefered, it's not clear why you need the inner task).
Do you really need to do a synchronous Wait on an asynchronous Task? Read some of Stephen Cleary's blog: http://blog.stephencleary.com/2012/02/async-unit-tests-part-1-wrong-way.html
As an aside, if you are not using C# 5, then watch out for closing over the foreach variable result See
Has foreach's use of variables been changed in C# 5?, and
http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx)
In Microsoft.Bcl.Async we couldn't add any static methods to Task. However, you can find most of the methods on TaskEx, for example, TaskEx.WhenAll() does exist.

Categories

Resources