Android Tutorial ImageDownloader - The simple way to download images

Status
Not open for further replies.
Don't use this. SimpleMediaManager is 100 times more powerful: [B4X] SimpleMediaManager (SMM) - framework for images, videos and more

Downloading images with HttpUtils2 is quite simple.

However correctly managing multiple downloads without downloading the same image multiple times, for example when the user changes the screen orientation, is more complicated.

ImageDownloader makes it very simple to efficiently download images and show them in ImageViews.

Example code:
B4X:
Sub Globals
   Dim ImageView3 As ImageView
   Dim ImageView2 As ImageView
   Dim ImageView1 As ImageView
   Dim ImageView4 As ImageView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
End Sub

Sub Activity_Resume
   Dim links As Map
   links.Initialize
   links.Put(ImageView1, "http://www.b4x.com/basic4android/images/SS-2012-08-29_12.55.42.png")
   links.Put(ImageView2, "http://www.b4x.com/basic4android/images/SS-2013-03-04_11.42.38.png")
   links.Put(ImageView3, "http://www.b4x.com/basic4android/images/SS-2013-03-04_11.52.19.png")
   links.Put(ImageView4, "http://www.b4x.com/basic4android/images/SS-2012-02-06_12.45.56.png")
   CallSubDelayed2(ImageDownloader, "Download", links)
End Sub

Sub Activity_Pause (UserClosed As Boolean)
   CallSub(ImageDownloader, "ActivityIsPaused")
End Sub

How to use?

1. In Activity_Resume you should create a Map with the ImageViews as keys and the links as values. Use CallSubDelayed to call ImageDownloader.Download.
2. In Activity_Pause you should use CallSub to call ImageDownloader.ActivityIsPaused.
3. That's it.

SS-2013-07-09_16.03.45.png
 

Attachments

  • ImageDownloader.zip
    8.1 KB · Views: 7,300
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User

Attachments

  • ImageDownloader2.zip
    10.4 KB · Views: 4,825

Erel

B4X founder
Staff member
Licensed User
Longtime User
FlickrViewer based on ImageDownloader service:

SS-2013-07-09_17.38.23.jpg


Note that in this example I've added the following method to ImageDownloader:
B4X:
Public Sub ClearCache
   cache.Clear
End Sub

Each time that we get new images we clear the cache to release the memory required for the old bitmaps. Unlike the previous example, in that example we ask for nine completely new images so the previous cached images are no longer relevant.
 

Attachments

  • FlickrViewer.zip
    7.4 KB · Views: 3,607

ferya

Member
Licensed User
Longtime User
Nice job.
How can use it in a listview?
In case of 100 or more images is it possible to keep images in an array of bitmaps for using somewhere else ? how?
Thank you in advance.
 

kanaida

Active Member
Licensed User
Longtime User
After using this and using picasso, I ended up using this because the other seems pretty buggy. The only thing missing here is a cache size param so we can use it like a FIFO buffer and not run out of memory. I havent quite figured out how to remove the first item from the map though.
 

kanaida

Active Member
Licensed User
Longtime User
Here's some edits i've done to update it to do auto fit and center.
First it makes it fit the target view's dimensions keeping aspect ratio, then caches the resulting image so cache hits are only memory, not cpu.

B4X:
Sub JobDone(Job As HttpJob)
    ongoingTasks.Remove(Job.JobName)
    If Job.Success Then
        Dim bmp As Bitmap = Job.GetBitmap
             
     
        If tasks.IsInitialized Then
            For i = 0 To tasks.Size - 1
             
                Dim link As String = tasks.GetValueAt(i)
                If link = Job.JobName Then
             
                    Dim iv As ImageView = tasks.GetKeyAt(i)
                 
                    Dim b2 As Bitmap
                    b2.InitializeMutable(iv.Width,iv.Height)
     
                    Dim c As Canvas
                    c.Initialize2(b2)
                 
                    Dim r1 As Rect
                    r1.Initialize(0,0,bmp.Width,bmp.Height)
                                 
                    Dim xdif As Float = iv.Width / bmp.Width
                    Dim ydif As Float = iv.Height / bmp.Height

                    Dim NewX As Float = bmp.Width * xdif
                    Dim NewY As Float = bmp.Height * ydif
                 
                    Dim NewZ As Float = (iv.Width + iv.Height) / (bmp.Width + bmp.Height)
                 
                    Dim r2X As Float
                    Dim r2Y As Float
                     
                    If NewX > NewY Then
                        'Log("X")
                        r2X = bmp.Width * ydif
                        r2Y = NewY
                    Else
                        'Log("Y")
                          r2X = NewX
                        r2Y = bmp.Height * xdif
                    End If
                 
                    If r2X > iv.Width Then
                        Dim NewRatio As Float = (iv.Width / r2X)
                        r2X = r2X * NewRatio
                        r2Y = r2Y * NewRatio
                    End If

                    Dim NewLeft As Float = ((iv.Width - b2.Width) / 2)
                    Dim NewTop As Float = ((iv.Height - b2.Height) / 2)
                 
                    Dim r2 As Rect
                    r2.Initialize(NewLeft,NewTop,r2X,r2Y)
                                     
                    c.DrawColor(Colors.White)
                    c.DrawBitmap(bmp,r1,r2)
                    c = Null
                    cache.Put(Job.JobName, b2)
                    iv.SetBackgroundImage(b2)
                End If
         
            Next
        End If
    Else
        cache.Put(Job.JobName, NotFoundBmp)
     
        If tasks.IsInitialized Then
            For i = 0 To tasks.Size - 1
             
                Dim link As String = tasks.GetValueAt(i)
                If link = Job.JobName Then
                    Dim iv As ImageView = tasks.GetKeyAt(i)
                    iv.SetBackgroundImage(NotFoundBmp)
                End If
            Next
        End If
     
        'Log("Error downloading image: " & Job.JobName & CRLF & Job.ErrorMessage)
    End If
    Job.Release
End Sub

This is a bit crude for cache management, but stops crashes and errors from spaces in URLs.
B4X:
Sub Download (ImageViewsMap As Map)

    For i = 0 To ImageViewsMap.Size - 1
        tasks.Put(ImageViewsMap.GetKeyAt(i), ImageViewsMap.GetValueAt(i))
        Dim link As String = ImageViewsMap.GetValueAt(i)
        link = link.Replace(" ","%20")
        If cache.ContainsKey(link) Then
            Dim iv As ImageView = ImageViewsMap.GetKeyAt(i)
            iv.SetBackgroundImage(cache.Get(link))
          
        Else If ongoingTasks.ContainsKey(link) = False Then
            Log("Cache Size: " & cache.Size)
            If cache.Size > MAX_CACHE_SIZE Then
                cache.Clear
            End If
            ongoingTasks.Put(link, "")
            Dim j As HttpJob
            j.Initialize(link, Me)
            j.Download(link)
        End If  
    Next
  
End Sub
 

somed3v3loper

Well-Known Member
Licensed User
Longtime User
Here's some edits i've done to update it to do auto fit and center.
First it makes it fit the target view's dimensions keeping aspect ratio, then caches the resulting image so cache hits are only memory, not cpu.


This is a bit crude for cache management, but stops crashes and errors from spaces in URLs.
B4X:
Sub Download (ImageViewsMap As Map)

    For i = 0 To ImageViewsMap.Size - 1
        tasks.Put(ImageViewsMap.GetKeyAt(i), ImageViewsMap.GetValueAt(i))
        Dim link As String = ImageViewsMap.GetValueAt(i)
        link = link.Replace(" ","%20")
        If cache.ContainsKey(link) Then
            Dim iv As ImageView = ImageViewsMap.GetKeyAt(i)
            iv.SetBackgroundImage(cache.Get(link))
         
        Else If ongoingTasks.ContainsKey(link) = False Then
            Log("Cache Size: " & cache.Size)
            If cache.Size > MAX_CACHE_SIZE Then
                cache.Clear
            End If
            ongoingTasks.Put(link, "")
            Dim j As HttpJob
            j.Initialize(link, Me)
            j.Download(link)
        End If 
    Next
 
End Sub

I think this tiny modification can help in maintaining a few bitmaps' cache instead of clearing it completely , what do you think?
B4X:
If cache.Size > MAX_CACHE_SIZE Then
          cache.Remove(cache.GetKeyAt(0))
End If
 

kanaida

Active Member
Licensed User
Longtime User
Hmm... that's the same code i tried, but my map.size didn't stay at my max size, kept growing. I possibly had a typo, lemme try again.
 

kanaida

Active Member
Licensed User
Longtime User
It seems to work this time. Now we just need to add some sort of algorithm to calculate free memory so we can say to only cache up to say until 50% of the memory is used, or reduce it's size automatically. This should let the cache grow efficiently without risking other parts of the app crashing. Also keep as many images cached as possible.

There's still a bug in there somewhere in my AutoFit and center algorithm. It works about 90% of the time it seems.
 

kanaida

Active Member
Licensed User
Longtime User
One last thing, I'm have HttpUtils 2.01 and HTTP 1.31, but I have no way to turn off logging 404 errors that I'm aware of. I'd like to though, as there as very many missing images, it becomes hard to read the logs.
 

kanaida

Active Member
Licensed User
Longtime User
Awesome, I didn't notice they came in library and modules. Just converted to the modules. :)

One other thing that i found i have to control is the number of ongoing tasks. If my pages of 20+ images each are changed too quickly, then it all goes awry and stops loading things. Seeing if I can use the same approach as the cache size. Is there a way to properly cancel an active/queued request? Ideally i'd find the first ongoingtask, cancel it's request, then remove it from the list and free resources so it's not just limiting, but removing unnecessary jobs.

I recall this, but i'm not quite seeing expected results:
The advantages of HttpUtils2 over HttpUtils are:
  • Any number of jobs can run at the same time (each job is made of a single task)
 

mvpmedia

Member
Licensed User
Longtime User
FYI, i wasted 2 days trying to figure out why i have memory errors and crashes after loading in 500 or so images (150 at a time). The imagedownloader service creates imageviews inside jobdone.

i added inside jobdone: If ongoingTasks.Size=0 Then stopservice("imagedownloader") and that frees up the memory of the imageviews created once it destroys the service. If you don't destroy the service then you will be loading imageview after imageview in the cache.
 

hyokim07

Member
Licensed User
Longtime User
SS-2013-07-09_16.34.28.png


In this example the list of images is first extracted from an online page and then all the images are downloaded. This example is based on: Download list of images with HttpUtils2 and CustomListView

However as the code is based on the ImageDownloaded service it is much simpler.
I like this one and I am trying to modify by adding CheckBox to each on the right of Label. So I simply added a CheckBox. So I added a CheckBox on top of the Label1, added a Dim CheckBox1 As CheckBox. And it displayed the same number of CheckBoxes with the Label1. As the Value is displayed on the Activity.Title when either an image or a Label is clicked, I wish to do the same when the CheckBox is checked. Any suggestion? Should I use Sub clv_CheckedChange (Checked As Boolean)? As you know, I am new to this and learning one at a time. Your suggestion or help will be greatly appreciated.
Thank you in advance.
 

Rorry

Member
Licensed User
Longtime User
Nice but just a question.
Why it downloads randomly when i try to download around 20 pics ( ~100Ko each) ?
i mean,sometimes i get only 1 pic downloaded ,sometimes 6,sometimes 3....
Thank you

Edit:

It looks to be because of the debugger. in release mode,it works like a charm. Thank you
 
Last edited:

lock255

Well-Known Member
Licensed User
Longtime User
I'm finding it difficult to render an local image in my ImageView, if not found the image that I put in the link.
Could you post a sample code that I do for me?
 
Status
Not open for further replies.
Top