Using Threads to Download Remote Images with RAD Studio
So what happens if you are loading remote bitmaps into your mobile app and they are slow to load? You’ll likely end up with an application that the user will not be too fond of using. The scrolling of user interface controls will be jerky while your app is waiting for the bitmap data to be transferred over.
To visualize this, download this 1 minute video from the Woll2Woll channel
With mobile apps, responsiveness of the user interface (especially with slow internet) is critically important. We can achieve this with Delphi and FireMonkey by creating a background thread for our application that is responsible for loading the bitmaps from the remote server. By using a separate thread, the user responsiveness when touch scrolling will still be smooth. We are going to walk you through how to set this up with a complete working demo. The cool thing about this demo is that a slow server that feeds the bitmap does not degrade the user performance in scrolling through the data.
First download the working app from …
Then compile and run the demo for Windows. It will also run in iOS, Android, and OSX. If you can run in iOS, this will be even be better since you can more readily simulate a slower internet connection by disabling wifi and instead force it to use your cellular connection. Then when you compare the performance without using a thread to the performance when you use a thread, you will notice the remarkable difference in responsiveness of your application. The app allows you to toggle between thread or no thread by clicking on the combo at the top right.
Once you run the app, notice that for a brief moment while the bitmaps are being loaded it will show the text “Loading”. You do not need to wait for the loading to complete, as you can immediately start scrolling the images vertically. The records will scroll and the bitmaps are updated when they are loaded from the remote location. Your application is no longer blocked during the loading of bitmaps.
Now change the thread combo to select “Without Thread”. The program will then load the first page. It may take a few seconds if you have a slower connection. Now try to scroll down. You will immediately notice that your user-interface kind of freezes while it loads the next page of bitmaps. Once it has finished loading up these 4 images, it will then allow the user interface to become responsive again. Obviously this is not a desirable user experience.
Creating a separate thread to download the images
So let us go over the major steps to accomplish background loading of remote bitmaps.
First let us explain where the data is coming from in this demo. There is a local in-memory table (Pictures) that contains two fields (ID and Picture). Picture is the name of the room and correlates with an image on the remote server. You can of course set this part up any way you want.
Now to start a background thread, the demo uses the method StartThreadLoadVisiblePictures, which is called by the forms OnShow event. It creates a thread that executes the procedure LoadVisiblePictures. This procedure runs continually until the program terminates and is responsible for checking which bitmaps need to be downloaded and assigned to their respective TImageControl. The downloading is accomplished by the method DownLoadStream, and is called for each bitmap that is currently visible on the screen. The thread does not load all the bitmaps as this could be cpu intensive and unnecessary if you have a lot of pictures. Once a bitmap is downloaded into a stream, it then loads the image stream into the displayed TImageControl.
It’s important to realize that TBitmap is not thread-safe, which means that any code updating the TImageControl’s bitmap needs to be executed in the main thread. This is accomplished with the following code in the thread which is executed after calling DownloadStream.
if (row>=TopRecNo) and
(row<TopRecNo + multiCount) then
The above code loads the visible TImageControls with their bitmap, hides the TLabel that was displaying ‘Loading Text’, sets the dirty property to false, and then repaints the TVertScrollBox containing all the images.
You may have different data structures that you are updating, but the critical point is to call the method DownloadStream to retrieve the remote image into a stream, and then to load the TImageControl’s bitmap by using the main thread through Synchronize. If you do this, then the main thread which is responsible for the user-interface will not become stalled when you download the remote bitmap.
Also note that the thread will sleep within the thread loop to avoid taking up all the CPU resources and give other tasks a chance to execute.
Other cool features of this demo
User-clickable selections to make bitmap full screen - This project allows you to click on one of the bitmaps and it will expand the bitmap to full screen. It does this through the OnClick event for non-touch devices and OnTap through touch devices.
Record’s display is determined by a visual template - The project uses the TLayout TemplatePictureItem to define the format of the record’s display. This is located on the 2nd tab page of the demo. You can change the position of the controls in TemplatePictureItem if you wish to reconfigure the layout.
The program automatically handles the resizing or rotation of the screen to reposition the records. See PictureContainerResize
You can click on the refresh button on the top right if you wish to redownload all the bitmaps without having to restart the program. This will allow you to restart the downloading, and then you can try scrolling when this is going on.
Demo Included with FirePower
FirePower also includes a demo showing remote image downloading and additionally shows how you can use transitions to change pages. It also uses the FirePower TwwLayoutGrid to easily customize the layout of your repeated items and scroll them horizontally, vertically or both.