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.basic4ppc.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.basic4ppc.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: 2,906
  • HttpUtilsExample.zip
    7.8 KB · Views: 5,276
Last edited:

Erel

Administrator
Staff member
Licensed User
In which way it doesn't work?
The logs should be:
B4X:
** Activity (main) Create, isFirst = true **


Starting Job: POST Job1


** Activity (main) Resume **
** Service (httputilsservice) Create **


** Service (httputilsservice) Start **


http://www.basic4ppc.com/print.php done


POST variables:



array(2) {
  ["key1"]=>
  string(6) "value1"
  ["key2"]=>
  string(6) "value2"
}


GET variables:

array(0) {
}
Starting Job: GET Job1
** Service (httputilsservice) Start **


http://www.basic4ppc.com/android/images/logo2.png done


Starting Job: GET Job2


** Service (httputilsservice) Start **


http://www.bing.com done


http://www.facebook.com done


http://www.google.com done


http://www.cnn.com done


http://www.twitter.com done


http://www.google.com: success=true
http://www.bing.com: success=true
http://www.cnn.com: success=true
http://www.twitter.com: success=true
http://www.facebook.com: success=true
<!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=windows-1255"><title>Google</title><script>window.google={kEI:"tOuvToP9Gc2q-AaR7MmUDw",getEI:function(a){var b;while(a&&!(a.getAttribute&&(b=a.getAttribute("eid"))))a=a.parentNode;return b||google.kEI},https:function(){return window.location.protocol=="https:"},kEXPI:"28936,30316,32034,32409,32940,33076,33104,33373,33501,33735,33883,33907",kCSI:{e:"28936,30316,32034,32409,32940,33076,33104,33373,33501,33735,33883,33907",ei:"tOuvToP9Gc2q-AaR7MmUDw"},authuser:0,


ml:function(){},kHL:"iw",time:function(){return(new Date).getTime()},log:function(a,b,c,e){var d=new Image,f=google,h=f.lc,g=f.li,i="";d.onerror=(d.onload=(d.onabort=function(){delete h[g]}));h[g]=d;if(!c&&b.search("&ei=")==-1)i="&ei="+google.getEI(e);var j=c||"/gen_204?atyp=i&ct="+a+"&cad="+b+i+"&zx="+google.time();d.src=j;f.li=g+1},lc:[],li:0,Toolbelt:{},y:{},x:function(a,b){google.y[a.id]=[a,b];return false}};
window.google.sn="webhp";var i=window.google.timers={};window.google.startTick=function(a,b){i[a]={t:{start:(new Date).getTime()},bfr:!(!b)}};window.google.tick=function(a,b,c){if(!i[a])google.startTick(a);i[a].t[b]=c||(new Date).getTime()};google.startTick("load",true);try{}catch(v){}
var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&&
window._gjuc())&&setTimeout(_gjp,500)};
window._gjp && _gjp()</script><style>#gb{font:13px/27px Arial,sans-serif;height:30px}#gbz,#gbg{position:absolute;white-space:nowrap;top:0;height:30px;z-index:1000}#gbz{right:0;padding-right:4px}#gbg{left:0;padding-left:5px}#gbs{background:transparent;position:absolute;top:-999px;visibility:hidden;z-index:998}.gbto #gbs{background #fff}#gbx3,#gbx4{background-color:#2d2d2d;background-image:none;_background-image:none;background-position:0 -138px;background-repeat:repeat-x;border-bottom:1px solid #000;font-size:24px;height:29px;_height:30px;opacity:1;filter:alpha(opacity=100);position:absolute;top:0;width:100%;z-index:990}#gbx3{left:0}#gbx4{right:0}.gbtcb{position:absolute;visibility:hidden}#gbz .gbtcb{left:0}#gbg .gbtcb{right:0}.gbxx{display:none !important}.gbm{position:absolute;z-index:999;top:-999px;visibility:hidden;text-align:right;border:1px solid #bebebe;background:#fff;-moz-box-shadow:-1px 1px 1px #ccc;-webkit-box-shadow:0 1px 5px #ccc;box-shadow:0 1px 5px #ccc}.gbrtl .gbm{-moz-box-shadow:1px 1px 1px #ccc}.gbto .gbm,.gbto #gbs{top:29px;visibility:visible}#gbz .gbm,#gbz #gbs{right:0}#gbg .gbm,#gbg #gbs{left:0}.gbxms{background-color:#ccc;display:block;position:absolute;z-index:1;top:-1px;right:-2px;left:-2px;bottom:-2px;opacity:.4;-moz-border-radius:3px;filter:progid:DXImageTransform.Microsoft.Blur(pixelradius=5);*opacity:1;*top:-2px;*right:-5px;*left:5px;*bottom:4px;-ms-filter:"progid:DXImageTransform.Microsoft.Blur(pixelradius=5)";opacity:1\0/;top:-4px\0/;right:-6px\0/;left:5px\0/;bottom:4px\0/}.gbma{position:relative;top:-1px;border-style:solid dashed dashed;border-color:transparent;border-top-color:#c0c0c0;display:-moz-inline-box;display:inline-block;font-size:0;height:0;line-height:0;width:0;border-width:3px 3px 0;padding-top:1px;right:4px}#gbztms1,#gbi4m1,#gbi4s,#gbi4t{zoom:1}.gbtc,.gbmc,.gbmcc{display:block;list-style:none;margin:0;padding:0}.gbmc{background:#fff;padding:10px 0;position:relative;z-index:2;zoom:1}.gbt{position:relative;display:-moz-inline-box;display:inline-block;line-height:27px;padding:0;vertical-align:top}.gbt{*display:inline}.gbto{box-shadow:0 1px 5px #ccc;-moz-box-shadow:0 1px 5px #ccc;-webkit-box-shadow:0 1px 5px #ccc}.gbzt,.gbgt{cursor:pointer;display:block;text-decoration:none !important}.gbts
** Service (httputilsservice) Destroy **
 

KashMalaga

Member
Licensed 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

In which way it doesn't work?
The logs should be:
B4X:
** Activity (main) Create, isFirst = true **


Starting Job: POST Job1


** Activity (main) Resume **
** Service (httputilsservice) Create **


** Service (httputilsservice) Start **


http://www.basic4ppc.com/print.php done


POST variables:



array(2) {
  ["key1"]=>
  string(6) "value1"
  ["key2"]=>
  string(6) "value2"
}


GET variables:

array(0) {
}
Starting Job: GET Job1
** Service (httputilsservice) Start **


http://www.basic4ppc.com/android/images/logo2.png done


Starting Job: GET Job2


** Service (httputilsservice) Start **


http://www.bing.com done


http://www.facebook.com done


http://www.google.com done


http://www.cnn.com done


http://www.twitter.com done


http://www.google.com: success=true
http://www.bing.com: success=true
http://www.cnn.com: success=true
http://www.twitter.com: success=true
http://www.facebook.com: success=true
<!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=windows-1255"><title>Google</title><script>window.google={kEI:"tOuvToP9Gc2q-AaR7MmUDw",getEI:function(a){var b;while(a&&!(a.getAttribute&&(b=a.getAttribute("eid"))))a=a.parentNode;return b||google.kEI},https:function(){return window.location.protocol=="https:"},kEXPI:"28936,30316,32034,32409,32940,33076,33104,33373,33501,33735,33883,33907",kCSI:{e:"28936,30316,32034,32409,32940,33076,33104,33373,33501,33735,33883,33907",ei:"tOuvToP9Gc2q-AaR7MmUDw"},authuser:0,


ml:function(){},kHL:"iw",time:function(){return(new Date).getTime()},log:function(a,b,c,e){var d=new Image,f=google,h=f.lc,g=f.li,i="";d.onerror=(d.onload=(d.onabort=function(){delete h[g]}));h[g]=d;if(!c&&b.search("&ei=")==-1)i="&ei="+google.getEI(e);var j=c||"/gen_204?atyp=i&ct="+a+"&cad="+b+i+"&zx="+google.time();d.src=j;f.li=g+1},lc:[],li:0,Toolbelt:{},y:{},x:function(a,b){google.y[a.id]=[a,b];return false}};
window.google.sn="webhp";var i=window.google.timers={};window.google.startTick=function(a,b){i[a]={t:{start:(new Date).getTime()},bfr:!(!b)}};window.google.tick=function(a,b,c){if(!i[a])google.startTick(a);i[a].t[b]=c||(new Date).getTime()};google.startTick("load",true);try{}catch(v){}
var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&&
window._gjuc())&&setTimeout(_gjp,500)};
window._gjp && _gjp()</script><style>#gb{font:13px/27px Arial,sans-serif;height:30px}#gbz,#gbg{position:absolute;white-space:nowrap;top:0;height:30px;z-index:1000}#gbz{right:0;padding-right:4px}#gbg{left:0;padding-left:5px}#gbs{background:transparent;position:absolute;top:-999px;visibility:hidden;z-index:998}.gbto #gbs{background #fff}#gbx3,#gbx4{background-color:#2d2d2d;background-image:none;_background-image:none;background-position:0 -138px;background-repeat:repeat-x;border-bottom:1px solid #000;font-size:24px;height:29px;_height:30px;opacity:1;filter:alpha(opacity=100);position:absolute;top:0;width:100%;z-index:990}#gbx3{left:0}#gbx4{right:0}.gbtcb{position:absolute;visibility:hidden}#gbz .gbtcb{left:0}#gbg .gbtcb{right:0}.gbxx{display:none !important}.gbm{position:absolute;z-index:999;top:-999px;visibility:hidden;text-align:right;border:1px solid #bebebe;background:#fff;-moz-box-shadow:-1px 1px 1px #ccc;-webkit-box-shadow:0 1px 5px #ccc;box-shadow:0 1px 5px #ccc}.gbrtl .gbm{-moz-box-shadow:1px 1px 1px #ccc}.gbto .gbm,.gbto #gbs{top:29px;visibility:visible}#gbz .gbm,#gbz #gbs{right:0}#gbg .gbm,#gbg #gbs{left:0}.gbxms{background-color:#ccc;display:block;position:absolute;z-index:1;top:-1px;right:-2px;left:-2px;bottom:-2px;opacity:.4;-moz-border-radius:3px;filter:progid:DXImageTransform.Microsoft.Blur(pixelradius=5);*opacity:1;*top:-2px;*right:-5px;*left:5px;*bottom:4px;-ms-filter:"progid:DXImageTransform.Microsoft.Blur(pixelradius=5)";opacity:1\0/;top:-4px\0/;right:-6px\0/;left:5px\0/;bottom:4px\0/}.gbma{position:relative;top:-1px;border-style:solid dashed dashed;border-color:transparent;border-top-color:#c0c0c0;display:-moz-inline-box;display:inline-block;font-size:0;height:0;line-height:0;width:0;border-width:3px 3px 0;padding-top:1px;right:4px}#gbztms1,#gbi4m1,#gbi4s,#gbi4t{zoom:1}.gbtc,.gbmc,.gbmcc{display:block;list-style:none;margin:0;padding:0}.gbmc{background:#fff;padding:10px 0;position:relative;z-index:2;zoom:1}.gbt{position:relative;display:-moz-inline-box;display:inline-block;line-height:27px;padding:0;vertical-align:top}.gbt{*display:inline}.gbto{box-shadow:0 1px 5px #ccc;-moz-box-shadow:0 1px 5px #ccc;-webkit-box-shadow:0 1px 5px #ccc}.gbzt,.gbgt{cursor:pointer;display:block;text-decoration:none !important}.gbts
** Service (httputilsservice) Destroy **

Erel using the complete source of your fully example works fine.
Cleaning your example to use what i need. Dont work.
I know that maybe comes for clean all unneed source but i would like to do it in less code can be done.
 

magarcan

Active Member
Licensed User
Erel using the complete source of your fully example works fine.
Cleaning your example to use what i need. Dont work.
I know that maybe comes for clean all unneed source but i would like to do it in less code can be done.
Why don't you upload your code? I think this could help in order we to help you ;)
I'm also working over the example code, because I need to download an image and save it.
 

x-mint

New Member
Licensed User
I'm having a problem every time i run my app for the first time after i turn off and on my phone. Anyone having the same problem?

errornew.jpg
 
Last edited:

x-mint

New Member
Licensed User
Already figured it out, it only shows when "Include Debug Information" and "Attach Debugger" is enabled. Thanks everyone
 

NeoTechni

Well-Known Member
Licensed User
Can we get a progress/status event? That way we can provide data to the user of how fast the download is going?
 

Erel

Administrator
Staff member
Licensed User
You can achieve it by using CountingOutputStream from the RandomAccessFile library.

Add the following declarations to HttpUtilsServce.Process_Globals:
B4X:
   Dim cout As CountingOutputStream
   Dim currentTaskLength As Long
Change hc_ResponseSuccess to:
B4X:
Sub hc_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   cout.Initialize(File.OpenOutput(TempFolder, TaskId, False))
   currentTaskLength = Response.ContentLength
   Response.GetAsynchronously("response", cout, _
      True, TaskId)
End Sub
Now in your activity you can use a Timer to periodically check HttpUtilsService.cout.Count.
Make sure to first check that it is initialized with cout.IsInitialized.
The total length is stored in HttpUtilsService.currentTaskLength.
 

pluton

Active Member
Licensed User
Hi Erel and other B4A guru people :)

I have problem with my app (see screenshot)
Erel can you give me your email that I send to you my project because I don't want source in public.
You will find error because I'm hitting the wall :BangHead:

This error happens sometimes when the app starts and sometimes doesn't.
Usually when it start for the second time. It is something with HttpUtils and HttpService

Screen_Shot.png
 

NeoTechni

Well-Known Member
Licensed User
You can achieve it by using CountingOutputStream from the RandomAccessFile library.

Add the following declarations to HttpUtilsServce.Process_Globals:
B4X:
   Dim cout As CountingOutputStream
   Dim currentTaskLength As Long
Change hc_ResponseSuccess to:
B4X:
Sub hc_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   cout.Initialize(File.OpenOutput(TempFolder, TaskId, False))
   currentTaskLength = Response.ContentLength
   Response.GetAsynchronously("response", cout, _
      True, TaskId)
End Sub
Now in your activity you can use a Timer to periodically check HttpUtilsService.cout.Count.
Make sure to first check that it is initialized with cout.IsInitialized.
The total length is stored in HttpUtilsService.currentTaskLength.

Perfect. Ty. I recommend merging it (and my %20/space handling stuff) that way I/we don't have to remerge it everytime you make an update. The space handling stuff id say is neccesary
 

pluton

Active Member
Licensed User
Please post the error stack trace that appears in the logs.

The problem is that error is only showing when I start the app for the second time on phone so everytime it compile correct and start ok
I dont know where to look for error.

Sent from my LG-P500
 

pluton

Active Member
Licensed User
Can you connect to your phone with B4A-Bridge or USB debugging? If yes then you should go to the Logs tab in the right side of the IDE and press connect. The error message will appear in the logs.

Yes I can
Ok. I will post it ASAP

Sent from my LG-P500
 

pluton

Active Member
Licensed User
It is OK now

You are right Erel. In Logs tab I saw the error and now is corrected and working fine. The error was here:

B4X:
Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
      parser.Initialize
   End If

On second time my app has crashes. Here is also log where I saw that.

B4X:
LogCat connected to: 80A354043047052354
--------- beginning of /dev/log/system
--------- beginning of /dev/log/main


** Activity (main) Create, isFirst = true ** [B]' First time start is TRUE[/B]
Starting Job: Provjera
** Activity (main) Resume **
** Service (httputilsservice) Create **
** Service (httputilsservice) Start **
http://xxxx.com/xxxxx.xml done [B]' this is link to my xml file on my server[/B]
** Service (httputilsservice) Destroy **
received

[B]' This is second start of app[/B]

** Activity (main) Pause, UserClosed = true **
** Activity (main) Create, isFirst = false ** [B]' Now is first time FALSE and error happens[/B]
Starting Job: Provjera
** Activity (main) Resume **
** Service (httputilsservice) Create **
** Service (httputilsservice) Start **
http://xxxxx.com/xxxxxx.xml done
main_jobdone (java line: 662) [B]' Error because of parser[/B]

java.lang.NullPointerException
   at anywheresoftware.b4a.objects.SaxParser.parse(SaxParser.java:76)
   at anywheresoftware.b4a.objects.SaxParser.Parse(SaxParser.java:71)
   at fantastic._______xXxxx.main._jobdone(main.java:662)
   at java.lang.reflect.Method.invokeNative(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:521)
   at anywheresoftware.b4a.BA.raiseEvent2(BA.java:105)
   at anywheresoftware.b4a.BA.raiseEvent(BA.java:89)
   at anywheresoftware.b4a.keywords.Common.CallSub4(Common.java:760)
   at anywheresoftware.b4a.keywords.Common.CallSub2(Common.java:747)
   at fantastic.xxxx______.httputilsservice._processnexttask(httputilsservice.java:180)
   at fantastic.xxxx______.httputilsservice._response_streamfinish(httputilsservice.java:244)
   at java.lang.reflect.Method.invokeNative(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:521)
   at anywheresoftware.b4a.BA.raiseEvent2(BA.java:105)
 
Status
Not open for further replies.
Top