Android Tutorial ImageDownloader - The simple way to download images

Discussion in 'Tutorials & Examples' started by Erel, Jul 9, 2013.

Thread Status:
Not open for further replies.
  1. Erel

    Erel Administrator Staff Member Licensed User

    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:
    Code:
    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.basic4ppc.com/basic4android/images/SS-2012-08-29_12.55.42.png")
       links.Put(ImageView2, 
    "http://www.basic4ppc.com/basic4android/images/SS-2013-03-04_11.42.38.png")
       links.Put(ImageView3, 
    "http://www.basic4ppc.com/basic4android/images/SS-2013-03-04_11.52.19.png")
       links.Put(ImageView4, 
    "http://www.basic4ppc.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.

    [​IMG]
     

    Attached Files:

  2. Erel

    Erel Administrator Staff Member Licensed User

    Attached Files:

  3. Erel

    Erel Administrator Staff Member Licensed User

    FlickrViewer based on ImageDownloader service:

    [​IMG]

    Note that in this example I've added the following method to ImageDownloader:
    Code:
    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.
     

    Attached Files:

    gudino jose luis likes this.
  4. ferya

    ferya Member Licensed 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.
     
    gudino jose luis likes this.
  5. Erel

    Erel Administrator Staff Member Licensed User

    ListView doesn't allow you to update the image. So you will need to wait for the image to be ready. I recommend you to use CustomListView as in the example above.
     
  6. kanaida

    kanaida Active Member Licensed 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.
     
  7. kanaida

    kanaida Active Member Licensed 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.

    Code:
    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.
    Code:
    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
     
    Ratna Fang, somed3v3loper and Erel like this.
  8. somed3v3loper

    somed3v3loper Well-Known Member Licensed User

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

    kanaida Active Member Licensed 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.
     
  10. kanaida

    kanaida Active Member Licensed 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.
     
  11. kanaida

    kanaida Active Member Licensed 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.
     
  12. Erel

    Erel Administrator Staff Member Licensed User

    You will need to use HttpUtils2 code modules instead of the library and remove the log messages.
     
  13. kanaida

    kanaida Active Member Licensed 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:
     
  14. kanaida

    kanaida Active Member Licensed User

  15. mvpmedia

    mvpmedia Member Licensed 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.
     
  16. Erel

    Erel Administrator Staff Member Licensed User

    See post #3. If you want to reuse ImageDownloader then you can add the ClearCache sub and call it to remove unnecessary images.
     
  17. hyokim07

    hyokim07 Member Licensed User

    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.
     
  18. Erel

    Erel Administrator Staff Member Licensed User

  19. Rorry

    Rorry Member Licensed 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: Feb 1, 2014
  20. lock255

    lock255 Well-Known Member Licensed 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?
     
Thread Status:
Not open for further replies.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice