Android Question Place HttpJob inside Class Module

Marc Harbeson

New Member
Licensed User
Is there a way to place a HttpJob into a Class - I would really like to make my .NET web service API fully contained in the class similar to how .NET would do it (as best I can) which means ideally I would set properties, invoke the Call method which would build the XML request, submit it via the HttpJob, and parse the SOAP / JSON (depending on the class) and set the properties of the class based on the response (you know, how .NET would do it)

This is what the class example looks with the ValidateUser method embedded in the class

B4X:
Sub Class_Globals
   '
   ' Attributes
   '
   Public ClientBuild As Int
   Public IndLaborCode As String
   Public EmployeeNumber As String
   Public BarcodePrinterPath As String
   Public BarcodeSystemType As Int
   Public AutoIndirectAfterTA As Boolean
   Public WarnErrorNegativeInventory As String
   Public PasswordMinLength As Int
   Public PasswordRepeatingCharLimit As Int
   Public PasswordHistoryLimit As Int
   Public PasswordResetRequired As Boolean
   Public PasswordDays As Int
   Public PasswordExpireDate As String
   Public EmployeeClearConstant As Int
   Public CurrentPrinter As String
   Public DefaultPrinter As String
   Public HomeFacility As String
   Public TimeZoneAddSubtract As String
   Public TimeZoneOffset As Int
   Public DefaultMenu As String
   Public Password As String
   Public UserName As String
   Public UserID As String
   Public IsSystemAdmin As Boolean
   Public ForceInvTransactions As Boolean
   Public NumericMenus As Boolean
   Public ClientIdent As String
   Public ServerLoginsDisabled As Boolean
   Public ServerLoginDMessage As String
   Public ServerSystemVersion As Int
   Public AuthFlags As String
   '
   ' Controls
   '
   Public ParseError As Boolean
   Public ParseErrorMsg As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   ParseError = False
   ParseErrorMsg = ""
End Sub


public Sub RequestXML As String
   Return $"<?xml version="1.0" encoding="utf-8"?>
   <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
   <ValidateUser xmlns="http://tempuri.org/">
   <Environment>
       <ClientBuild>${ClientBuild}</ClientBuild>
       <Password>${Password}</Password>
       <UserID>${UserID}</UserID>
   </Environment>
   </ValidateUser>
   </soap:Body>
   </soap:Envelope>"$
End Sub

'public Sub getRequestJob As HttpJob
'   
'   Dim j As HttpJob
'
'   j.Initialize("",Main)
'   j.PostString(Globals.WebServerPath & "server.asmx", RequestXML )
'   j.GetRequest.SetContentType("text/xml; charset=utf-8")
'   j.GetRequest.SetHeader("SOAPAction",$""http://tempuri.org/ValidateUser""$)
'   
'   Return j
'
'End Sub

public Sub ValidateUser
   Dim j As HttpJob

   j.Initialize("",Me)
   j.PostString(Globals.WebServerPath & "server.asmx", RequestXML )
   j.GetRequest.SetContentType("text/xml; charset=utf-8")
   j.GetRequest.SetHeader("SOAPAction",$""http://tempuri.org/ValidateUser""$)
   
   wait for (j) jobdone(j As HttpJob)
   
   If j.Success Then
       ParseXml(j.GetString)
   Else
       ParseErrorMsg = "Error Communicating with Web Server: " & j.ErrorMessage
       ParseError = True
   End If
   j.Release
End Sub

public Sub ParseXml(XmlString As String)
   
   Dim x2m As Xml2Map
   x2m.Initialize
   '
   ' extract base document root
   '
   Dim root As Map
   If x2m.Parse(XmlString) Is Map Then
       root = x2m.Parse(XmlString)
   Else
       ParseError = True
       ParseErrorMsg = "Missing Document Root"
       Return
   End If
   '
   ' extract base document root
   '
   'Log(root)
   Dim envelope As Map
   If root.Get("Envelope") Is Map Then
       envelope = root.Get("Envelope")
   Else
       ParseError = True
       ParseErrorMsg = "Soap Envelope Missing"
       Return
   End If
   '
   ' extract soap body
   '
   Dim body As Map
   If envelope.Get("Body") Is Map Then
       body = envelope.Get("Body")
   Else
       ParseError = True
       ParseErrorMsg = "Soap Body Missing"
       Return
   End If
   '
   ' extract Environment from Response
   '
   Dim EnvironmentResponse As Map
   If  body.Get("ValidateUserResponse") Is Map Then
       EnvironmentResponse = body.Get("ValidateUserResponse")
   Else
       ParseError = True
       ParseErrorMsg = "Environment Response is missing from Soap Envelope"
       Return
   End If
   '
   ' extract environment from response
   '
   Dim Environment As Map
   If EnvironmentResponse.Get("Environment") Is Map Then
       Environment = EnvironmentResponse.Get("Environment")
   Else
       ParseError = True
       ParseErrorMsg = "Environment is missing from Environment Response"
       Return
   End If
   '
   ' Extract Individual Attributes
   '
   ClientBuild = Environment.Get("ClientBuild")
   IndLaborCode = Environment.Get("IndLaborCode")
   EmployeeNumber = Environment.Get("EmployeeNumber")
   BarcodePrinterPath = Environment.Get("BarcodePrinterPath")
   BarcodeSystemType = Environment.Get("BarcodeSystemType")
   AutoIndirectAfterTA = Environment.Get("AutoIndirectAfterTA")
   WarnErrorNegativeInventory = Environment.Get("WarnErrorNegativeInventory")
   PasswordMinLength = Environment.Get("PasswordMinLength")
   PasswordRepeatingCharLimit = Environment.Get("PasswordRepeatingCharLimit")
   PasswordHistoryLimit = Environment.Get("PasswordHistoryLimit")
   PasswordResetRequired = Environment.Get("PasswordResetRequired")
   PasswordDays = Environment.Get("PasswordDays")
   PasswordExpireDate = Environment.Get("PasswordExpireDate")
   EmployeeClearConstant = Environment.Get("EmployeeClearConstant")
   CurrentPrinter = Environment.Get("CurrentPrinter")
   DefaultPrinter = Environment.Get("DefaultPrinter")
   HomeFacility = Environment.Get("HomeFacility")
   TimeZoneAddSubtract = Environment.Get("TimeZoneAddSubtract")
   TimeZoneOffset = Environment.Get("TimeZoneOffset")
   DefaultMenu = Environment.Get("DefaultMenu")
   Password = Environment.Get("Password")
   UserName = Environment.Get("UserName")
   UserID = Environment.Get("UserID")
   IsSystemAdmin = Environment.Get("IsSystemAdmin")
   ForceInvTransactions = Environment.Get("ForceInvTransactions")
   NumericMenus = Environment.Get("NumericMenus")
   ClientIdent = Environment.Get("ClientIdent")
   ServerLoginsDisabled = Environment.Get("ServerLoginsDisabled")
   ServerLoginDMessage = Environment.Get("ServerLoginDMessage")
   ServerSystemVersion = Environment.Get("ServerSystemVersion")
   AuthFlags = Environment.Get("AuthFlags")
End Sub

It feels like the problem is that control returns back to my activity before the class fully processes the HttpJob and therefore the response is not fully parsed in time for what happens next in the activity after the call to ValidateUser.

Is there a way to execute the HttpJob blocking thread executing and not return from the class call until the httpjob is finished?
 

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Blocking in Android is frowned upon, and difficult to implement at best. I've done something similar in the past by creating the HTTP class to handle the communications, but they are all done asyncroniously, the class raises events as jobs are completed.

I create the class in my starter service and receive the events there. The event handlers in the starter service evaluate the call back, and if the requesting activity is not paused, will callsub the appropriate method in the necessary activity.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Is there a way to place a HttpJob into a Class
Yes, sure.
Is there a way to execute the HttpJob blocking thread executing and not return from the class call until the httpjob is finished?
You need to make your class using resumeable subs. Watch the Video from the ErelTeachesProgramming Video series.
 
Upvote 0

Marc Harbeson

New Member
Licensed User
Blocking in Android is frowned upon, and difficult to implement at best. I've done something similar in the past by creating the HTTP class to handle the communications, but they are all done asyncroniously, the class raises events as jobs are completed.

I create the class in my starter service and receive the events there. The event handlers in the starter service evaluate the call back, and if the requesting activity is not paused, will callsub the appropriate method in the necessary activity.

It works fine if I leave the HttpJob in the activity event (such as button click) - I am just being picky in wanting that event to contain 1 line of code instead of 4 or so...

Placing call backs in the service feels like it would be harder to follow from a readability point of view - which is partially the point of what I am wanting. :) (even thou it may be unreasonable demand)

The absolute perfect solution would be the ability to add WebReferences which builds a class with all the wrappers for me... I would pay $ for that since I have several more web service methods to "port to android" and then maintain... Its amazing how super easy Visual Studio made this part of the process.

I am tempted to build a class builder on the .NET side to speed it up now that I know basically how to wire them up... (once I make a final decision on if I have to live with the HttpJob outside the class and inside the activity(s))
 
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
HttpJob outside the class and inside the activity(s)
This has always created a headache for me at times, due to the quasi-stateless nature of the Android OS; Once you have it in the activity, try rotating the device (or clicking home) just as soon as you click your "start the HTTP request" button.

Ultimately, it's personal preference as is most programming, it was just easier for me to centralize the actual HTTP job code in it's own class and centralize the handling of that classes events in the starter service. That way, each activity could track its own state regardless of any pending network I/O.

Each to his/her own ;)
 
Upvote 0

Marc Harbeson

New Member
Licensed User
ok, I think I found a solution I liked...

I added a property to the class to indicate if the call is complete, then wait for that to happen...

B4X:
    Globals.UserEnv.UserID = txtLogin.Text
   Globals.UserEnv.Password = txtPassword.Text
   Globals.UserEnv.ClientBuild = "3364"
   
   Globals.UserEnv.ValidateUser
   
   Do While Not(Globals.UserEnv.ParseComplete )
       Sleep(100)
   Loop
 
Upvote 0
Top