I'm trying to download images and display them in a 5x5 grid, and I want each image to show up as soon as it's downloaded.
I have a VerticalFieldManager, and to it I add 5 HorizontalFieldManagers. As I download each image (using HttpConnection), I convert it to a Bitmap then place it in a BitmapField then add the field to one of the HorizontalFieldManagers.
This works, except that I don't see any of the images until after all 25 have been downloaded, at which point I see them all.
I've tried calling invalidate() on everything involved after each image is downloaded, but it doesn't seem to have any effect.
Are you doing the download off the UI event thread? If not, the download will block all UI updates until finished, which would explain the behavior you see.
edit based on new information in comment:
HttpConnection is blocking, so you need to do the IO operation on a thread outside of the UI thread. Your application is downloading the images fast enough that blocking the UI thread isn't killing the whole app. On a slow connection, your app would be killed by the OS while doing these downloads.
This interface performs blocking Input and Output operations. An application will lock if an implementation of this interface opens a connection from within the main event thread. Prevent an application from locking by opening a connection from within a thread that is separate from the main event thread. SeeConnector for more information.
1) You should download and create BitmapField on a separate (non-UI) Thread.
2) When you have a BitmapField to add, then do something like this:
final BitmapField b = ... // your code to get the BitmapField
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
yourContainer.add(b);
yourContainer.invalidate(); // may not need this - try comment out
}
});
Related
I wondered if anyone could provide advice on how I can ‘force’ the UI to update during a particularly intensive function (on the main thread) in Swift.
To explain: I am trying to add an ‘import’ feature to my app, which would allow a user to import items from a backup file (could be anything from 1 - 1,000,000 records, say, depending on the size of their backup) which get saved to the app’s CodeData database. This function uses a ‘for in’ loop (to cycle through each record in the backup file), and with each ‘for’ in that loop, the function sends a message to a delegate (a ViewController) to update its UIProgressBar with the progress so the user can see the live progress on the screen. I would normally try to send this intensive function to a background thread, and separately update the UI on the main thread… but this isn't an option because creating those items in the CoreData context has to be done on the main thread (according to Swift’s errors/crashes when I initially tried to do it on a background thread), and I think this therefore is causing the UI to ‘freeze’ and not update live on screen.
A simplified version of the code would be:
class CoreDataManager {
var delegate: ProgressProtocol?
// (dummy) backup file array for purpose of this example, which could contain 100,000's of items
let backUp = [BackUpItem]()
// intensive function containing 'for in' loop
func processBackUpAndSaveData() {
let totalItems: Float = Float(backUp.count)
var step: Float = 0
for backUpItem in backUp {
// calculate Progress and tell delegate to update the UIProgressView
step += 1
let calculatedProgress = step / totalItems
delegate?.updateProgressBar(progress: calculatedProgress)
// Create the item in CoreData context (which must be done on main thread)
let savedItem = (context: context)
}
// loop is complete, so save the CoreData context
try! context.save()
}
}
// Meanwhile... in the delegate (ViewController) which updates the UIProgressView
class ViewController: UIViewController, ProgressProtocol {
let progressBar = UIProgressView()
// Delegate function which updates the progress bar
func updateProgressBar(progress: Float) {
// Print statement, which shows up correctly in the console during the intensive task
print("Progress being updated to \(progress)")
// Update to the progressBar is instructed, but isn't reflected on the simulator
progressBar.setProgress(progress, animated: false)
}
}
One important thing to note: the print statement in the above code runs fine / as expected, i.e. throughout the long ‘for in’ loop (which could take a minute or two), the console continuously shows all the print statements (showing the increasing progress values), so I know that the delegate ‘updateProgressBar’ function is definitely firing correctly, but the Progress Bar on the screen itself simply isn’t updating / doesn’t change… and I’m assuming it’s because the UI is frozen and hasn’t got ‘time’ (for want of a better word) to reflect the updated progress given the intensity of the main function running.
I am relatively new to coding, so apologies in advance if I ask for clarification on any responses as much of this is new to me. In case it is relevant, I am using Storyboards (as opposed to SwiftUI).
Just really looking for any advice / tips on whether there are any (relatively easy) routes to resolve this and essentially 'force' the UI to update during this intensive task.
You say "...Just really looking for any advice / tips on whether there are any (relatively easy) routes to resolve this and essentially 'force' the UI to update during this intensive task."
No. If you do time-consuming work synchronously on the main thread, you block the main thread, and UI updates will not take effect until your code returns.
You need to figure out how to run your code on a background thread. I haven't worked with CoreData in quite a while. I know it's possible to do CoreData queries on a background thread, but I no longer remember the details. That's what you're going to need to do.
As to your comment about print statements, that makes sense. The Xcode console is separate from your app's run loop, and is able to display output even if your code doesn't return. The app UI can't do that however.
Does async operation in iOS, internally create a new thread, and allocate task to it ?
An async operation is capable to internally create a new thread and allocate task to it. But in order for this to happen you need to run an async operation which creates a new thread and allocates task to it. Or in other words: There is no direct correlation.
I assume that by async you mean something like DispatchQueue.main.async { <#code here#> }. This does not create a new thread as main thread should already be present. How and why does this work can be (if oversimplified) explained with an array of operations and an endless loop which is basically what RunLoop is there for. Imagine the following:
Array<Operations> allOperations;
int main() {
bool continueRunning = true;
for(;continueRunning;) {
allOperations.forEach { $0.run(); }
allOperations.clear();
}
return 0;
}
And when you call something like DispatchQueue.main.async it basically creates a new operation and inserts it into allOperations. The same thread will eventually go into a new loop (within for-loop) and call your operation asynchronously. Again keep in mind that this is all over-simplified just to illustrate the idea behind all of it. You can from this also imagine how for instance timers work; the operation will evaluate if current time is greater then the one of next scheduled execution and if so it will trigger the operation on timer. That is also why timers can not be very precise since they depend on rest of execution and thread may be busy.
A new thread on the other hand may be spawned when you create a new queue DispatchQueue(label: "Will most likely run on a new thread"). When(if) exactly will a thread be made is not something that needs to be fixed. It may vary from implementations and systems being run on. The tool will only guarantee to perform what it is designed for but not how it will do it.
And then there is also Thread class which can generate a new thread. But the deal is same as for previous one; it might internally instantly create a new thread or it might do it later, lazily. All it guarantees is that it will work for it's public interface.
I am not saying that these things change over time, implementation or system they run on. I am only saying that they potentially could and they might have had.
Hi in my application i have two types of syncs. 1.Auto sync 2.Manual Sync. In both the syncs i am downloding a bunch of files from server. If I choose auto sync all files will get download.
Code is like this
for(int i=0;i<filescount;i++)
{
[self downloadfiles];
}
-(void)download files
{
//Here i am creating `NSInvocationOperation`.
if(!synchingfilecount)
totalreceiveddata=0;
}
Based on totalreceiveddata I am updating progress bar. Now the issue is if it autosync it is working fine.While downloading files using autosync and in middle if i click manual sync that time [self downloadfiles]; method will get called but the issue is synchingfilescount is not updating immediately it's completeing the autosyncfiles download and synchingfilescount become 0 due to this reason totalreceiveddata become 0 and progress bar is disappearing. After complete this opertiona again synchingfilecount becomes 4 but i cannot able to see the progress bar due to above situation. Please any one help me how can I come out from this situation.
Ok, if I understand your question correctly, it sounds like you need to create some flags so that you can manage the flow of your code. You can do this by making a boolean property and setting it as you need in the completion block of these sync methods. That way you can call a method or only execute a method after the call is complete.
I noticed that all my UI tests fail when the network is slow. For instance a user would try to login and then the next screen wouldn't load fast enough in order for another UIElement to be on screen.
How can I handle a slow network connection without using a delay() ?
You should definitely take a look at multi-threading. When handling network connections, you should make all this processing in a secondary thread. If not, the main thread will be blocked and the app will become unresponsive to the user.
Multi-threading is a very big subject. I recommend you to start looking at Apple's reference for this. You can also refer to a great course on iTunes U (lecture 11).
If you just want to give it a shot, here's the actual code (similar) that you will need:
dispatch_queue_t newQueue = dispatch_queue_create("networkQueue", NULL);
dispatch_async(newQueue, ^{
// here you need to call the networking processes
dispatch_async(dispatch_get_main_queue(), ^{
// if you need to update your UI, you need to get back to the main queue.
// This block will be executed in your main queue.
});
});
The only way I know of is using a delay. I usually have a activity indicator when loading stuff from the internet. So I add a delay while the activity indicator is displaying
while (activityIndicator.isVisible())
{
UIALogger.logMessage("Loading");
UIATarget.localTarget().delay(1);
}
Check out pushTimeout and popTimeout methods in the UIATarget. You can find the docs here.
Here is one code example from our iOS app UIAutomation tests:
// Tap "Post" button, which starts a network request
mainWindow.buttons()["post.button.post"].tap();
// Wait for maximum of 30 seconds to "OKAY" button to be valid
target.pushTimeout(30);
// Tap the button which is shown from the network request success callback
mainWindow.buttons()["dialog.button.okay"].tap();
// End the wait scope
target.popTimeout();
I have a background thread which loads images (either from disk or a server), with the goal of eventually passing them to the main thread to draw. When this second thread is loading GIF images using the VCL's TGIFImage class, this program sometimes leaks several handles each time the following line executes in the thread:
m_poBitmap32->Assign(poGIFImage);
That is, the just-opened GIF image is being assigned to a bitmap owned by the thread. None of these are shared with any other threads, i.e. are entirely localised to the thread. It is timing-dependent, so doesn't occur every time the line is executed, but when it does occur it happens only on that line. Each leak is one DC, one palette, and one bitmap. (I use GDIView, which gives more detailed GDI information than Process Explorer.) m_poBitmap32 here is a Graphics32 TBitmap32 object, but I have reproduced this using plain VCL-only classes, i.e. using Graphics::TBitmap::Assign.
Eventually I get an EOutOfResources exception, probably indicating the desktop heap is full:
:7671b9bc KERNELBASE.RaiseException + 0x58
:40837f2f ; C:\Windows\SysWOW64\vclimg140.bpl
:40837f68 ; C:\Windows\SysWOW64\vclimg140.bpl
:4084459f ; C:\Windows\SysWOW64\vclimg140.bpl
:4084441a vclimg140.#Gifimg#TGIFFrame#Draw$qqrp16Graphics#TCanvasrx11Types#TRectoo + 0x4a
:408495e2 ; C:\Windows\SysWOW64\vclimg140.bpl
:50065465 rtl140.#Classes#TPersistent#Assign$qqrp19Classes#TPersistent + 0x9
:00401C0E TLoadingThread::Execute(this=:00A44970)
How do I solve this and safely use TGIFImage in a background thread?
And secondly, will I encounter this same problem with the PNG, JPEG or BMP classes? I haven't so far, but given it's a threading / timing issue that doesn't mean I won't if they use similar code to TGIFImage.
I am using C++ Builder 2010 (part of RAD Studio.)
More details
Some research showed I'm not the only person to encounter this. To quote from one thread,
Help (2007) says:
In multi-threaded applications that use Lock to protect a canvas, all calls that use the canvas must be protected by a call to
Lock. Any thread that does not lock the canvas before using it will
introduce potential bugs.
[...]
But this statement is absolute false: you MUST lock the canvas in
secondary thread even if other threads don't touch it. Otherwise the
canvas's GDI handle can be freed in main thread as unused at any
moment (asynchronously).
Another reply indicates something similar, that it may be to do with the GDI object cache in graphics.pas.
That's scary: an object created and used entirely in one thread can have some of its resources freed asynchronously in the main thread. Unfortunately, I don't know how to apply the Lock advice to TGIFImage. TGIFImage has no Canvas, although it does have a Bitmap which has a canvas. Locking that has no effect. I suspect that the problem is actually in TGIFFrame, an internal class. I also do not know if or how I should lock any TBitmap32 resources. I did try assigning a TMemoryBackend to the bitmap, which avoids using GDI, but it had no effect.
Reproduction
You can reproduce this very easily. Create a new VCL app, and make a new unit which contains a thread. In the thread's Execute method, place this code:
while (!Terminated) {
TGraphic* poGraphic = new TGIFImage();
TBitmap32* poBMP32 = new TBitmap32();
__try {
poGraphic->LoadFromFile(L"test.gif");
poBMP32->Assign(poGraphic);
} __finally {
delete poBMP32;
delete poGraphic;
}
}
You can use Graphics::TBitmap if you don't have Graphics32 installed.
In the app's main form, add a button which creates and starts the thread. Add another button which executes similar code to the above (once only, no need to loop. Mine also stores the TBitmap32 as a member variable instead of creating it there, and invalidates so it will eventually paint it to the form.) Run the program and click the button to start the thread. You will probably see GDI objects leak already, but if not press the second button which runs the similar code once in the main thread - once is enough, it seems to trigger something - and it will leak. You will see memory usage rise, and that it leaks GDI handles at the rate of several dozen per second.
Unfortunately, the fix is very, very ugly. The basic idea is that the background thread must acquire a lock that the main thread holds when it's between messages.
The naive implementation is like this:
Lock canvas mutex.
Spawn background thread.
Wait for message.
Release canvas mutex.
Process message.
Lock canvas mutex.
Go to step 3.
Note that this means the background thread can only access GDI objects while the main thread is busy, not while it's waiting for a message. And this means the background thread cannot own any canvasses while it does not hold the mutex. These two requirements tend to be too painful. So you may need to refine the algorithm.
One refinement is to have the background thread send the main thread a message when it needs to use a canvas. This will cause the main thread to more quickly release the canvas mutex so the background thread can get it.
I think this will be enough to make you give up this idea. Instead, perhaps, read the file from the background thread but process it in the main thread.