Xamarin: Load images asyn in view - c#

I m developing a wallpaper app. Therefore I am using Xamarin.Forms. There is a scroll view with all available wallpapers and on top of it, the user can see the selected one bigger.
The problem is that when I start the App I can see that it is a bit lagging. (Images are loading in view) And I don t like that. It looks not good. So I started a new Task in that I am getting the images from the embedded resources. But that doesn't t coast so much time. The time-consuming thing is to display the images in the view. And I can not do that in the task because it has to be an STA thread to access the view.
I want it like this:
The app starts You see a running activity indicator
The images are loaded from the resource without blocking the UI thread
Images are loaded into view without blocking the UI thread
Activity indicator disappears and main grid comes in front
So here is my code:
Check this pastebin for code.
Thank you.
Ok I didn't t fixed the problem but I have a solution:
Task.Factory.StartNew(() =>
{
return InitializeImages();
}).ContinueWith(r =>
{
Device.BeginInvokeOnMainThread(() =>
{
if (r.Result == GlobalSettings.ImageCount)
ButtonsGrid.ColumnDefinitions[1].Width = 0;
else
ButtonsGrid.ColumnDefinitions[1].Width = GridLength.Star;
for (int i = 0; i < r.Result; i++)
{
ImagesGrid.Children.Add(_previewImages[i]);
ImagesStackLayout.Children.Add(_images[i]);
}
});
Thread.Sleep(2000);
Device.BeginInvokeOnMainThread(async () =>
{
await ShowWallpaperInPreview();
await Loading(false);
});
});
I let it lagging in the background. and after a short time, I change from loading to normal view. This way it is lagging in the background (who cares=)

A very good candidate to keep your app responsive and cover cases that are not mentioned in your question is an open source project named FFImageLoading. Here is the list of supported features:
Configurable disk and memory caching
Multiple image views using the same image source (url, path, resource) will use only one bitmap which is cached in memory (less memory usage)
Error and loading placeholders support
Images can be automatically downsampled to specified size (less memory usage)
Can retry image downloads (RetryCount, RetryDelay)
and much more!
Check the Advanced Usage doc and Wiki for more details.

Related

How to Load Byte-Array on background of Hololens

I create an application for Hololens with Unity 2020.3.12f with the MixedRealityToolKit.
I have a problem loading a file to a byte array in background of the Hololens.
To visualise png-grafics ( > 100 files) on my Hololens App, i create sprites dynamicly from ByteArrays.
To load an image (~96 KB) into a byte-Array with File.ReadAllBytes takes around 300 ms (very slow).
To keep the process of updating the shown image fast, the Byte-Arrays of all images should be preloaded on a GameObjects script.
I decided to load 20 files into ByteArray and show up the first image as sprite.
After this process the other files should be loaded in the backgroud, so the App still allows interaction. The function I Call from a Button should look like:
public void LoadSomething()
{
UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
{
#Load First Byte-Arrays and show up first Image
#Load the rest of the Bytes-Arrays without blocking Interaction on App
LoadBytes();
}, false);
}
The files to load are stored as Storagefiles on a Gameobject "Manager".
The function for loading could be like:
private void LoadBytes()
{
foreach (Storagefile storageFile in manager.listOfStorageFiles)
{
byte[] filedata = File.ReadAllBytes(storageFile.Path);
manager.byteList.Add(fileData);
}
}
I tried to start the function with Task.Run(() => LoadBytes ), but this freezes the UI and nothing happens. I think there is a problem to work with a GameObject on CPU.
Have anyone a solution for calling this function from the UIThread and still keep my UI interactive ?
Since you have any I/O-bound needs, it is recommended to improve LoadBytes() as an asynchronous method that performs a non-blocking wait on the background job. For more information please see: Asynchronous programming patterns

IronPDF deadlocks when running in parallel

I'm trying to generate multiple PDFs in parallel using IronPDFs HTML to PDF feature. But it appears to be deadlocking when started from ASP.NET :(
I've recreated the problem here: https://github.com/snebjorn/ironpdf-threading-issue-aspnet
Here's a snippet with the essential parts.
Calling GetSequential() works. But is not executing in parallel.
GetSimple() is running in parallel but deadlocks.
public class TestController : Controller
{
[HttpGet]
[Route("simple")]
public async Task<IActionResult> GetSimple()
{
var tasks = Enumerable
.Range(1, 10)
.Select(i => HtmlToDocumentAsync("hello", i));
var pdfs = await Task.WhenAll(tasks);
using var pdf = PdfDocument.Merge(pdfs);
pdf.SaveAs("output.pdf");
return Ok();
}
[HttpGet]
[Route("seq")]
public async Task<IActionResult> GetSequential()
{
var pdfs = new List<PdfDocument>();
foreach (var i in Enumerable.Range(1, 10))
{
pdfs.Add(await HtmlToDocumentAsync("hello", i));
}
using var pdf = PdfDocument.Merge(pdfs);
pdf.SaveAs("output.pdf");
return Ok();
}
private async Task<PdfDocument> HtmlToDocumentAsync(string html, int i)
{
using var renderer = new HtmlToPdf();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return pdf;
}
}
According to https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d it's because the thread executing the controller method isn't a main thread. So it just gets added to the thread pool and at some point we're waiting for the controller thread to continue but it's not getting scheduled back in. This happens when we mix async/await with .Wait/.Result.
So am I right to assume that there are .Wait/.Result calls happening inside the IronPDF.Threading package?
Is there a workaround?
UPDATE:
I updated to IronPdf 2021.9.3737 and it now appears to work 🎉
Also updated https://github.com/snebjorn/ironpdf-threading-issue-aspnet
Just wanted to add to this that IronPdf's multi-threading support on MVC web apps is non-existent. You will end up with indefinite deadlocks if you're rendering in the context of an HTTP request.
We have been strung along with the promise of an updated renderer that would resolve the issue (which we were told should be released June/July 2021) but that appears to have been pushed back. I tested the updated renderer using their 'early-access package' and the deadlocking has been replaced by 10 second thread blocks and seemingly random C++ exceptions, so it's far from being fixed. Performance is better single-threaded.
Darren's reply is incorrect - I've stepped through our render calls countless times trying to fix this, and the deadlock comes on the HtmlToPdf.StaticRenderHtmlAsPdf call, not on a PdfDocument.Merge call. It's a threading issue.
I suggest avoiding IronPdf if you haven't already bought their product. Find another solution.
I used IronPDF on the 2021.9.3737 branch without any threading issues on windows and Linux today thanks to Darren's help in another thread.
Documentation: https://ironpdf.com/object-reference/api/
Nuget: https://www.nuget.org/packages/IronPdf/
Source of information: C# PDF Generation (using IronPDF on Azure)
I agree with abagonhishead that StaticRenderHtmlAsPdf used to create a que of PDF documents to be rendered, and on an under-provisioned server it ended in a thread deadlock... the que getting longer and longer as the server struggled to render PDFs.
Solution that worked for me:
moving to a well provisioned server (Azure B1 for example)
(and/or) moving to IronPDF latest Nuget 2021.9.3737
Support for Iron Software here.
Our engineers tested your sample project, increasing to 150 iterations and saw it running without issue.
Our expectation of your use-case is that you are creating multiple threads to generate PDF files and store these files into an array for merging later?
Assuming this is the case, the likely cause of this issue is sending too large an array to the merge method, which requires a large amount of RAM to process. The crash is the memory not handling the large number of PDFS to merge.
as you can see from the attached image, I tested your code with 1000 iterations and it works without issues, I believe the problem may occur when you increase the iterations or input big HTML size that reaches the max CPU and memory capacity that can't handle.
also, I don't agree with abagonhishead because there is not an alternative solution in the market that offer all these features

CefSharp offscreen - wait for page for render

I have a problem as below. I use the CefSharp offscreen for webpage automation as follows (I open only one and the same page):
1. Open page and wait untill it renders*.
2. With EvaluateScriptAsync I put on value to input form and then with the same method I click the button on webpage.
3. Then there is some JS on this webpage that check result and displays a message.
4. When the message is displayed I make a screenshot. **
However, I have two problems:
* My sulution has to be Internet speed proof. And As I used BrowserLoadingStateChanged event and IsLoading method, even though that the events fired the webpage did not load completly - when I started the EavluateScriptAsync method it gives back error because the page was not completly loaded. Sure, I can put sth like ThreadSleep but it does not always work - it is strongly dependent on Your internet speed.
** When I try to make a screenshot it does not always contain the result message displayed by JS - sometimes there is a loading circle instead of message. And here again I can use THreadSleep but it does not always work.
Do You have any ideas? Thanks in advance.
private static void BrowserLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
// Check to see if loading is complete - this event is called twice, one when loading starts
// second time when it's finished
// (rather than an iframe within the main frame).
if (!e.IsLoading)
{
// Remove the load event handler, because we only want one snapshot of the initial page.
browser.LoadingStateChanged -= BrowserLoadingStateChanged;
Thread.Sleep(1800); // e. g. but it isn't a solution in fact
var scriptTask = browser.EvaluateScriptAsync("document.getElementById('b-7').value = 'something'");
scriptTask = browser.EvaluateScriptAsync("document.getElementById('b-8').click()");
//scriptTask.Wait();
if (browser.IsLoading == false)
{
scriptTask.ContinueWith(t =>
{
//Give the browser a little time to render
//Thread.Sleep(500);
Thread.Sleep(500); // still not a solution
// Wait for the screenshot to be taken.
var task = browser.ScreenshotAsync();
task.ContinueWith(x =>
{
// Make a file to save it to (e.g. C:\Users\jan\Desktop\CefSharp screenshot.png)
var screenshotPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "CefSharp screenshot.png");
Console.WriteLine();
Console.WriteLine("Screenshot ready. Saving to {0}", screenshotPath);
// Save the Bitmap to the path.
// The image type is auto-detected via the ".png" extension.
task.Result.Save(screenshotPath);
// We no longer need the Bitmap.
// Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
task.Result.Dispose();
Console.WriteLine("Screenshot saved. Launching your default image viewer...");
// Tell Windows to launch the saved image.
Process.Start(screenshotPath);
Console.WriteLine("Image viewer launched. Press any key to exit.");
}, TaskScheduler.Default);
});
}
}
}
Ok, so in my case the best sollution was to use javascript to check if element by id exists. If yes then the page is loaded.
I noticed that render time may vary significantly depending on your hardware. It can take up to 5 seconds to render after EvaluateScriptAsync was called. So it always better to do longer delays before calling ScreenshotAsync() if you do not want to get outdated screenshot.
Thread.Sleep(5000);

Using await in async methods to prevent next line of code from running

I'm new to using async methods, so I think I'm misunderstanding something. I have a WinForms app with a button, and when the button is clicked, an async method gets called. This must be async, as I need to make javascript calls to a Chromium Web Browser control (using CefSharp). I need to ensure that this javascript has finished running and that the browser has updated before continuing with the next part of the method.
I'm basically trying to capture the entire web page into a single image. My approach was to use javascript to update the scroll position on the page, then take screenshots in each position using Graphics.CopyFromScreen. This mostly works, however occasionally the resulting image will have the wrong 'chunk' of webpage (e.g., the first bitmap is repeated twice). Here is my code:
// Calculate screen sizes, screenshot spacing etc.
for (int i = 0; i < screenshotCount; i++)
{
int scrollSize = i == 0 ? -PageHeight : (int)browserControlHeight;
string script = "(function() { window.scrollBy(0, " + scrollSize.ToString() + ") })();";
await browser.EvaluateScriptAsync(script);
// Take screenshot, add to list of bitmaps
}
// Combine resulting list of bitmaps
If I add the following
await Task.Delay(1000);
after the EvaluateScriptAsync() call, the final image comes out correct every time. I'm working on the assumption that the javascript is being called but doesn't complete before the screenshot begins. If this is the case, even adding a delay may not work (what if the javascript takes longer than a second to run?).
Am I misunderstanding the way that async/await works?
No, the issue is not with await, the issue is that the Task returned from EvaluateScriptAsync is being marked as completed before you're ready to continue. It's going to be marked as completed as soon as the javascript to inform the browser that it should scroll has executed, rather than being marked as completed after the browser has finished re-rendering the screen after being sent the scroll command.

how to preload images in a background thread?

In my WPF app i need to load some images. I only need to display one image at a time. If i load the image when it's needed, there is a slightly delay. So i thought to myself: "Hey, why not do some preloading in a background thread? Can't be that hard." I have some experience with threads, but not enough to know that this thought was wrong. I started programming and run into some problems. I fixed some of the problems and i probably could fix the other problems too, but that would result in spaghetti code. So, I think starting from the scratch would be the best. What initial planing is needed to build a nice and little preloading thread? Is there a pattern or something like that?
Here's my current setup:
LinkedList<string> to stores pathes to the pictures and navigate to the next picture
Dictionary<string, BitmapImage> to store the preloaded images
I'd use something like this:
class ImageManager
{
private Dictionary<string, Image> images=
new Dictionary<string,Image>();
public Image get(string s) { // blocking call, returns the image
return load(s);
}
private Image load(string s) { // internal, thread-safe helper
lock(images) {
if(!images.ContainsKey(s)) {
Image img=// load the image s
images.Add(s,img);
return img;
}
return images[s];
}
}
public void preload(params string[] imgs) { // non-blocking preloading call
foreach(string img in imgs) {
BackgroundWorker bw=new BackgroundWorker();
bw.DoWork+=(s,e)=>{ load(img); } // discard the actual image return
bw.RunWorkerAsync();
}
}
}
// in your main function
{
ImageManager im=new ImageManager();
im.preload("path1", "path2", "path3", "path4"); // non-blocking call
// then you just request images based on their path
// they'll become available as they are loaded
// or if you request an image before it's queued to be loaded asynchronously
// it will get loaded synchronously instead, thus with priority because it's needed
}
That certainly sounds like a good use for a background thread. Also, as your unit of work is quite large, there shouldn't be too much contention for the synchronization on your collections. You might find examples of similar algorithms, but I think you'll have to roll your own implementation - it's not that complicated.
One thing springs to mind, though: you will either have to keep a record of which images are currently in the process of being loaded, or tolerate multiple loads of the same image.
For example, if your UI requires an image that has not yet been loaded, you will probably want to load that image as a priority. If you know that the background thread is in the process of loading that image, you could just wait for it to become available. If instead you decide to just do the load on the UI thread, there is a possibility that the background thread will try to add a loaded image that is already present.
So, there will have to be some synchronization, but it shouldn't be too complicated.
Nick
Marcel,
WPF provides us already with great mechanisms of BackgroundWorker and Dispatcher to make you forget about writing your own thread mechanisms. However your problem doesn't seem to be that obvious for me. How many images do you need/where do you get these from? Please give us more info.
I was looking in to this yesterday and couldn't find much on the subject. There's actually quite a simple solution to the problem. Use WebClient to load the images asynchronously into a stream and then add that stream to a BitmapImage. Bellow is an example of how I implemented the preloading of a list of images. the example uses the Reactive Extensions Library(Rx) but it can easily be implemented in a non Rx way (Rx makes the code a lot more succinct and hides a lot of state).
public IEnumerable<BitmapImage> BitmapImages { get; private set }
private void PreloadImages(IEnumerbale<Uri> uriCollection)
{
var bitmapImages= new List<BitmapImage>();
uriCollection.ToObservable()
.SelectMany(LoadImageAsync)
.Catch(Observable.Empty<BitmapImage>())
.Subscribe(bitmapImages.Add,
() =>
{
BitmapImages = bitmapImages;
});
}
private IObservable<BitmapImage> LoadImageAsync(Uri uri)
{
return Observable.CreateWithDisposable<BitmapImage>(observer =>
{
var downloader = new WebClient();
downloader.OpenReadCompleted += (s, e) =>
{
if (e.Error != null)
{
observer.OnError(e.Error);
}
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = e.Result;
bitmapImage.EndInit();
observer.OnNext(bitmapImage);
observer.OnCompleted();
};
downloader.OpenReadAsync(uri);
return downloader;
});
}

Categories

Resources