Android Tutorial HttpUtils - Android web services are now simple!

Status
Not open for further replies.
OkHttpUtils2 is now available. OkHttpUtils2 is an improved version and is recommended for new projects. You shouldn't use HttpUtils (v1)!

HttpUtils is made of a code module and a service module. These two modules make it very simple to download online resources and upload data.

The advantages of using HttpUtils are:
  • Much simpler than working with HttpClient directly.
  • Handles parallel calls efficiently and correctly (protects from RejectedExecutionException exceptions).
  • Downloads are not affected by the activity life cycle.
Using HttpUtils

A simple example of downloading a page and returning the page as string:
B4X:
Sub Globals
 Dim b4a As String
 b4a = "http://www.b4x.com"
End Sub

Sub Activity_Create (FirstTime As Boolean)
 HttpUtils.CallbackActivity = "Main" 'Current activity name.
 HttpUtils.CallbackJobDoneSub = "JobDone"
 HttpUtils.Download("Job1", b4a)
End Sub

Sub JobDone (Job As String)
 Dim s As String
 If HttpUtils.IsSuccess(b4a) Then
  s = HttpUtils.GetString(b4a)
 End If
End Sub
First we configure the callback subs. Then we call HttpUtils.Download or HttpUtils.DownloadList. These calls submit a job request to HttpUtils.
A job is made of one or more links.
HttpUtils raises two types of events while processing a job. The UrlDone event is raised for each successful download with the downloaded url and the JobDone event is raised when the whole job finishes.
You cannot submit a new job while a current job is running (though a job can contain many links).

We have three ways to access a downloaded resource:
  • HttpUtils.GetString(Url As String) - Returns the resource as string
  • HttpUtils.GetBitmap(Url As String) - Returns the resource as bitmap
  • HttpUtils.GetInputStream(Url As String) - Returns an InputStream which allows you to manually read the downloaded resource.
These three methods should only be called after the job is done or inside the UrlDone event sub (for that specific Url).
After downloading a resource the Url serves as the key for that resource.

Inside JobDone event sub you should call HttpUtils.IsSuccess before accessing any Url as it is possible that some or all of the downloads have failed. This is not necessary in UrlDone event as UrlDone is called for each successful download.

Second example:
In this example we first download an image and set it as the activity background. Then we download another 6 Urls and print the last one as string. The code for this example is attached.
B4X:
Sub Process_Globals
    Dim ImageUrl As String
    ImageUrl = "http://www.b4x.com/android/images/logo2.png"
    Dim Job2Links As List
End Sub

Sub Globals

End Sub

Sub Activity_Create(FirstTime As Boolean)
    HttpUtils.CallbackActivity = "Main"
    HttpUtils.CallbackJobDoneSub = "JobDone"
    HttpUtils.CallbackUrlDoneSub = "UrlDone"
    Job2Links.Initialize
    Job2Links.AddAll(Array As String( _
        "http://www.google.com", "http://www.yahoo.com", _
        "http://www.bing.com", "http://www.cnn.com", _
        "http://www.twitter.com", "http://www.facebook.com"))
  
    HttpUtils.Download("Job1", ImageUrl)
End Sub

Sub Activity_Resume
    'Check whether a job has finished while the activity was paused.
    If HttpUtils.Complete = True Then JobDone(HttpUtils.Job)
End Sub
Sub UrlDone(Url As String)
    Log(Url & " done")
End Sub
Sub JobDone (Job As String)
    Select Job
        Case "Job1"
            If HttpUtils.IsSuccess(ImageUrl) Then
                Dim b As Bitmap
                b = HttpUtils.GetBitmap(ImageUrl)
                Activity.SetBackgroundImage(b)
            End If
            'Start the second job
            HttpUtils.DownloadList("Job2", Job2Links)
        Case "Job2"
            For i = 0 To HttpUtils.Tasks.Size - 1
                link = HttpUtils.Tasks.Get(i)
                Log(link & ": success=" & HttpUtils.IsSuccess(link))
            Next
            If HttpUtils.IsSuccess("http://www.google.com") Then
                Log(HttpUtils.GetString("http://www.google.com"))
            End If
    End Select
    HttpUtils.Complete = False 'Turn off the complete flag so we won't handle it again if the activity is resumed.
End Sub
What happens when the user presses on the Home key during a download?
The download will complete successfully (we are using a service for this).
However your activity will be paused and the UrlDone and JobDone events will not fire.

When our activity is resumed we should check if we missed anything. This is done with this code:
B4X:
Sub Activity_Resume
    'Check whether a job has finished while the activity was paused.
    If HttpUtils.Complete = True Then JobDone(HttpUtils.Job)
End Sub
We are calling JobDone ourselves if needed.
In Sub JobDone we reset the Complete flag so we know that this job was handled.
UrlDone event should be considered a "nice to have" feature. Your code should be prepared to handle the case where some UrlDone events were missed due to the activity being paused.

The FlickrViewer example was rewritten and the attached code uses this module.
In this example we first go to the "main" page. In this page we find 9 links to 9 images. We submit a second job with all these links.

We show each image as soon as it is ready by using the UrlDone event.
In JobDone we check if all Urls were handled. We can miss some of these events if the activity was paused during download.

flickr_viewer1.png



HttpUtils, similar to DBUtils, aims to simplify common tasks that many developers face. The code is available for you. So changes can be made as needed.

Updates:

V1.04 - The service is now destroyed when it is no longer needed and recreated when needed again. This version also fixes a bug that caused the application to crash if the service was started after the process was killed.

V1.02 - PostString, PostBytes and PostFile methods added to HttpUtils.
These methods make it easy to post data to a web service (using http POST method).
The behavior of these methods is similar to Download and DownloadList. JobDone event is raised after the response from the server is read.

The latest version (v1.04) is included in HttpUtilsExample.
 

Attachments

  • FlickrViewer.zip
    9.8 KB · Views: 3,366
  • HttpUtilsExample.zip
    7.8 KB · Views: 5,797
Last edited:

Inman

Well-Known Member
Licensed User
Longtime User
Sorry, I didn't get you. I am not sure how to initialize the process globals of one module from another module. I see that code modules have a .Process_Globals which is why HttpUtils.Process_Globals works fine. But HttpUtilsService and activity modules do not seem to have one.

Is there another way to initialize a module manually, like how the system handles it when the app is invoked via launcher?
 

Inman

Well-Known Member
Licensed User
Longtime User
This is the Service_Create in HttpUtilsService

B4X:
Sub Service_Create
   If TempFolder = "" Then TempFolder = File.DirInternalCache
   If hcIsInitialized = False Then
      hc.Initialize("hc")
      hcIsInitialized = True
   End If
End Sub

What should I add to this function to initialize the module manually? Sorry I need to keep troubling you and I know you will be busy today with the release of 1.70 and all. But this issue is puzzling me.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Try the following:
B4X:
Sub Process_Globals
   Dim hc As HttpClient
   Dim task As Int
   Dim countWorking As Int
   Dim finishTasks As Int
   Dim maxTasks As Int
   maxTasks = 10
   Dim taskToRequest As Map
   Dim TempFolder As String
   Dim Post As Boolean
   Dim PostBytes() As Byte
   Dim PostInputStream As InputStream
   Dim PostLength As Int
   Dim hcIsInitialized As Boolean
   Dim init As Boolean
   init = True
End Sub
Sub Service_Create
   If init = False Then Process_Globals
   If TempFolder = "" Then TempFolder = File.DirInternalCache
   If hcIsInitialized = False Then
      hc.Initialize("hc")
      hcIsInitialized = True
   End If
End Sub
 

Inman

Well-Known Member
Licensed User
Longtime User
Thank you for your patience. Unfortunately that doesn't work. It still freezes the phone. Feels like it is in some never ending loop. But the moment you execute the app with the launcher activity, everything works fine.
 

Inman

Well-Known Member
Licensed User
Longtime User
Yes. Nothing out of the ordinary

B4X:
Starting Job: Job1
** Activity (browser) Resume **
Displayed com.mysite.browserapp/.browser: +320ms
** Service (httputilsservice) Create **
** Service (httputilsservice) Start **
wakelock acquire, uid:10072 at elapsed real time: 8233742
wakelock release, uid:10072 at elapsed real time: 8233778
GC_CONCURRENT freed 374K, 47% free 3483K/6535K, external 0K/0K, paused 6ms+4ms
GC_EXPLICIT freed 164K, 53% free 2713K/5703K, external 0K/0K, paused 49ms
wakelock acquire, uid:1000 at elapsed real time: 8245172
wakelock release, uid:1000 at elapsed real time: 8245223
onUpdate
onUpdate - 103
GC_CONCURRENT freed 435K, 53% free 2796K/5831K, external 151K/512K, paused 4ms+3ms

Like that it goes
 

Inman

Well-Known Member
Licensed User
Longtime User
Just to let you know that I switched to manual implementation of httpclient instead of httputils and everything is working fine now. And fortunately I pass only 1 url at a time so hopefully there won't be a concurrent execution error.

Httputils is very convenient but unfortunately due to some limitation of Android system, it is not suitable for activities that are not invoked via launcher. Thanks for this code module anyway. I will be using it in my next app which is more of a normal one with only 1 major activity that gets invoked via launcher.
 

glouie

Member
Licensed User
Longtime User
PostString - submitting multiple requests as a job?

Thanks for making this helper library. When I first started using httpclient I didn't realize there was a limit to the number of jobs running, I wish Android would queue the jobs.

I was using the httputils to make a series of GET requests submitting them using HttpUtils.DownloadList and this worked great.

Is there a way to submit multiple POST using a job? In a loop if I call a series of Httputils.PostString after the first request the log says "Already working. Request ignored" and the other items never get processed.

Thank you,
Garrick
 

miguelconde

Member
Licensed User
Longtime User
Friends, I am a rookie with B4A, however through the power of this tool I could move my mobile application by 50% in just one week. I have a question about httputils. How do I capture the error message or description when HttpUtils.IsSuccess method is false?. Currently sending a generic message, something like "An error occurred with the server", but I would put the message
that if he could see in the log, in my case I am downloading a file, if not exist on the server, I can see the message in the LOG. "File does not exist", I need to get that message. Thanks for everything and congratulations on eta tool.
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
Replace the following subs in HttpUtils with

B4X:
Sub IsSuccess(URL As String) As Boolean
   Return SuccessfulUrls.ContainsKey(URL.Replace(" ", "%20"))
End Sub

'Get methods should be called only after the JobDone event or the UrlDone event.
Sub GetString(URL As String) As String
   If IsSuccess(URL) = False Then
      Log("Task not completed successfully.")
      Return ""
   End If
   Return File.GetText(HttpUtilsService.TempFolder, SuccessfulUrls.Get(URL.Replace(" ", "%20")))
End Sub

Sub GetBitmap(URL As String) As Bitmap
   Dim b As Bitmap
   If IsSuccess(URL) = False Then
      Log("Task not completed successfully.")
      Return b
   End If
   b = LoadBitmap(HttpUtilsService.TempFolder, SuccessfulUrls.Get(URL.Replace(" ", "%20")))
   Return b
End Sub

Sub GetInputStream(URL As String) As InputStream
   Dim in As InputStream
   If IsSuccess(URL) = False Then
      Log("Task not completed successfully.")
      Return in
   End If
   in = File.OpenInput(HttpUtilsService.TempFolder, SuccessfulUrls.Get(URL.Replace(" ", "%20")))
   Return in
End Sub

ProcessNextTask in HttpUtilsService

After

B4X:
Dim link As String
   link = HttpUtils.Tasks.Get(task)

Put

B4X:
link=link.Replace(" ", "%20")

This fixes the bug with URLs with spaces in them
 
Last edited:

NeoTechni

Well-Known Member
Licensed User
Longtime User
I'm having trouble queuing large amounts of files. It seems like it only lets you do 2 at a time and won't pull any ones after that off the stack
 

Cableguy

Expert
Licensed User
Longtime User
What would be the correct way to read a page's html sting (source-code)?

I tried to use directly the GetString, but it errors in the device (it compiles fine) complaining that a Map was not initialized...
After having a look at the httputils module, I think the map is never initialized when calling the GetString method!
What am I missing?
 

Cableguy

Expert
Licensed User
Longtime User
That is another problem I have...I cann LOG my phone (SGS)
 

ChrShe

Member
Licensed User
Longtime User
UttpUtils.GetInputStream woes...

1st off, lemme say that I'm super new to Android development and b4a is making my learning experience fantastic...and HttpUtils is making it easier yet!

However, all of the sudden, I'm having trouble with HttpUtils.GetInputStream.

It is failing with "Object should first be initialized (InputStream)". The strange thing is that yesterday, it worked fine. Today, all I did was change the size of some controls.

Here is a code snippet:
B4X:
Sub Globals
    ...
    Dim AnimalsPage As String
    ...
End Sub
Sub Activity_Create(FirstTime As Boolean)
   HttpUtils.CallbackActivity = "Main" 'Current activity name.
    HttpUtils.CallbackJobDoneSub = "ParsePetangoPage"
    ...
End Sub
Sub btnDog_Click
   ProgressDialogShow("Loading Currently Available Dogs...")
   AnimalsPage = "http://www.petango.com/webservices/adoptablesearch/wsAdoptableAnimals.aspx?species=Dog&sex=A&agegroup=All&onhold=A&orderby=ID&colnum=1&AuthKey=5refef2jv1piy179v350n58k5m1l4if77u466z4fy567wv622l&css=http://petango.com/WebServices/adoptablesearch/css/styles.css"
   HttpUtils.Download("ParsePetangoPage", AnimalsPage)
   ...
End Sub
Sub ParsePetangoPage
   ...
   Dim txtrdr As TextReader
   [B]txtrdr.Initialize(HttpUtils.GetInputStream(AnimalsPage))[/B] 'Fails here with exception ("Object should first be initialized (InputStream)") 
   ...
End Sub

Again...this worked yesterday, and if you post that giant url into a browser it works, so I'm at a total loss of what's happening.

Thanks in advance
Chris:BangHead:
 

ChrShe

Member
Licensed User
Longtime User
Never mind...I seem to have lost internet connectivity to my emulator.
Running on a real device works great!!!

Now on to adding in error handling...LOL
 

KashMalaga

Member
Licensed User
Longtime User
Im trying to do a post reply and after that read the source of webpage.

Httputils is really confuse with all those options. would be better create a simple project using every example?

Here is an example about what im talking, and isnt running
 

Attachments

  • kj.zip
    7.7 KB · Views: 435
Status
Not open for further replies.
Top