Android Question [Solved] Resumable Subs and Callbacks

Roger C

Active Member
Licensed User
Longtime User
Hi all,

I have a Servicemodule with HTTP-jobs, called HTTPservice.
All subs in there are resumable and they are sent two arguments in the call.

Sometimes I need to wait for the Service to complete, sometimes I don't need to wait.
Now I wonder which way is the best to solve this?

See below code.
In version 1, I call the Resumable Service and have a global xSender specifying where to return.
Either it is Activity1a or 1b. In the end of the service I check where to callback to.

In version 2, I call the service and then wait for the service to do everything with the Do While Not-loop.When the service is ready it sets the (Global) ServiceReady = True and then the code continues.

In version 3, I have tried to use Wait For but since the service is in another module it doesn't work as it is written here in the code. I've tried to add the "HTTPservice" but either it's SyntaxError or too many thing or...
Is there a way to use Wait For and call another module?
Should I use Wait For to call another Sub in the same module and that Sub uses CallSubDelayed3(... ?

In version 4, it's just a normal call without need to wait for it to complete.

So my question is this:
I want to use one Resumable Sub even if I need to wait or not.
It will be called from many different other Subs, from activities or services.
Sometimes I need to wait for it to complete and sometimes I don't.
What is the best solution to use?

(There might be errors in the code here and it might not be the best way to do things... But I'm asking since I want to learn)

B4X:
Sub Activity1a                ' Version1a. Call and wait for callback
    xSender = "Activity1a"
    CallSubDelayed3(HTTPservice,"Service1",Lat,Lon)
    Wait For Service_Ready
End Sub

Sub Activity1b                ' Version1b. Call and wait for callback
    xSender = "Activity1b"
    CallSubDelayed3(HTTPservice,"Service1",Lat,Lon)
    Wait For Service_Ready
End Sub

Sub Activity2                ' Version2. Call and wait for ServiceReady=True in a loop
    CallSubDelayed3(HTTPservice,"Service1",Lat,Lon)
    Do While Not(ServiceReady = True)
             Sleep(0)
        Loop
End Sub

Sub Activity3                ' Version3. Only Wait For, waiting for a return result
    Wait For(Service1(Lat, Lon)) Complete (Ready as String)
End Sub

Sub Activity4                ' Version4. Call resumable sub and don't wait
    CallSubDelayed3(HTTPservice,"Service1",Lat,Lon)
End Sub


'In Servicemodule HTTPservice
Public Sub Service1 (Latstring As String, Lonstring As String) As ResumableSub

    Dim SendString As String
    SendString = "https:xxxxxxxxx"
    job1.Download(SendString)
    Wait For (job1) JobDone(job1 As HttpJob)
    
    If job1.Success Then
        ' Code....
    End If

    Result = "OK"

    If xSender="Activity1a" Then                'Use a name of sub instead of Me beacause sender (Me) don't fit in call to sub
        xSender=""       
        CallSubDelayed("Activty1a", "Service_Ready")    'Activity1a will get the return
    Else
        If xSender="Activity1b" Then
            xSender=""       
            CallSubDelayed("Activty1b", "Service_Ready")    'Activity1b will get the return
        Else
            Return Result   
        End If
    End If
End Sub
 

Roger C

Active Member
Licensed User
Longtime User
Sorry,
Don't work.

B4X:
Sub btnNearby_Click
    Dim Lat As String
    Dim Lon As String
   
    Lat=Starter.GPSlat
    Lon=Starter.GPSlon

    Dim rs As ResumableSub = CallSub3(HTTPservice, "NearbyStops", Lat, Lon)   
    Wait For(rs) Complete (Ready As String)               ' Line 121
End Sub

' In module HTTPservice:
Public Sub NearbyStops (Latstring As String, Lonstring As String) As ResumableSub
    ....
    Return "Ready"
End Sub

Above code gives error:
B4X:
Error occurred on line: 121 (SetupTrip)
java.lang.RuntimeException: Object should first be initialized (ResumableSub).
    at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
    at anywheresoftware.b4a.keywords.Common.WaitFor(Common.java:1704)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:777)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:354)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:180)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:176)
    at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:80)
    at android.view.View.performClick(View.java:5280)
    at android.view.View$PerformClick.run(View.java:21239)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:234)
    at android.app.ActivityThread.main(ActivityThread.java:5526)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 
Upvote 0

Roger C

Active Member
Licensed User
Longtime User
I've tried different versions now and
Wait For(rs) Complete (Ready As String)
Works only if the Resumable Sub is in the same module.

If it's in a Service or another Activity it throws above error.

(Supplied are the Test-project as zip.)
The two top buttons work (Button1 and 4)
The two buttons in bottom don't work (Button2 and 3)
B4X:
Sub Button1_Click       'Works
    Wait For(Sum(1, 2)) Complete (Result As Int)
   
    Log("result: " & Result)
    Log("after sum")
End Sub

Sub Button2_Click      'Don't work
    Dim rs As ResumableSub = CallSub3(HTTPservice, "Sum", 3, 4)
    Wait For(rs) Complete (Result As Int)
   
    Log("result: " & Result)
    Log("after sum")
End Sub

Sub Button3_Click      'Don't work
    Dim rs As ResumableSub = CallSub3(Testactivity, "Sum", 3, 4)
    Wait For(rs) Complete (Result As Int)
   
    Log("result: " & Result)
    Log("after sum")
End Sub

Sub Button4_Click      'Works
   
    Dim rs As ResumableSub = CallSub3(Me,"Sum", 5, 8)
    Wait For(rs) Complete (Result As Int)
   
    Log("result: " & Result)
    Log("after sum")
End Sub



Sub Sum(a As Int, b As Int) As ResumableSub
    Sleep(100)
    Log(a + b)
    Return a + b
End Sub

Sub Sum2 As ResumableSub
    Sleep(100)
    Log(3 + 4)
    Return 3 + 4
End Sub


'In Service:
Sub Sum(a As Int, b As Int) As ResumableSub
    Sleep(100)
    Log(a + b)
    Return a + b
End Sub

' In activity:
Sub Sum(a As Int, b As Int) As ResumableSub
    Sleep(100)
    Log(a + b)
    Return a + b
End Sub
 

Attachments

  • ResumableSubs.zip
    10.4 KB · Views: 145
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
CallSub will not start the service if it isn't currently running. If the service was not started then CallSub will return an uninitialized ResumableSub. You can check it with rs.IsInitialized.

The best option is to create a class instance with the code and initialize it in Service_Create of the starter service. This way you will not need to use CallSub at all.
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
The best option is to create a class instance with the code and initialize it in Service_Create of the starter service. This way you will not need to use CallSub at all.
CallSub works well within the current activity. It won't start another activity or service (which might be killed - or ever started).
CallSubDelayed will start another activity or service, yet it can't be used with Wait For. (boo... doesn't return a result)

Creating a class of your (activity) object works well. Converting an activity to a class was a difficult concept at first, until I wrapped my head around it (OOP).
Wait For (ResumableSub) now behaves as expected.
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
Note that you can use the starter service as the CallSub target. It is never paused so you don't need to worry about such cases.
Pardon? Can you provide an ETP on this subject (or point to existing) on this concept?
We struggle with this concept.
Thanks
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
Hummm, I tried recently and did not achieve any success with CallSub (other than frustration with Wait For). Likely due to my lack of knowledge of how to implement.
I shall revisit the issue and post a new thread on the issue. Thanks.
 
Upvote 0

Roger C

Active Member
Licensed User
Longtime User
So... Classes.
They gave me a headace in VB. :)
I've read the CustomVievs and a lot of threads in the forum.

So is this the way to go?
How do I get a return from the class-sub?
I have three different versions in the Main-module below... Is any of them correct?

Class-module NearbyStops

B4X:
Sub Class_Globals
    Private SendString As String
    Private Latstring, Lonstring As String
    Private ResultMap As Map
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
    ' Does it need to be something here or can it just be empty?
End Sub

Public Sub GetStops(Lat As Double, Lon As Double) As ResumableSub
    
    Dim job1 As HttpJob
    job1.Initialize("Part1",Me)
    job1.Tag = "Part1tag"
    
    Latstring = Lat
    Lonstring = Lon
    
    Dim SendString As String
    
    SendString = "xxxxxxxxx" & Latstring & "xxxxxxx" & Lonstring

    job1.Download(SendString)
    Wait For (job1) JobDone(job1 As HttpJob)
    
    Log("Job1 done")
    
    If job1.Success Then
        ' Deal with successful job
        ' Result is stored in ResultMap
    End If
    job1.Release

    Return ResultMap
    
End Sub

Starter-module
B4X:
Sub Service_Start (StartingIntent As Intent)
    Stops.Initialize
End Sub

Main-module
B4X:
Sub GetData
    Starter.Stops.GetStops(59.123456,18.123456)
    'or
    Dim rs As ResumableSub = CallSub3(NearbyStops, "GetStops", Lat, Lon)
    Wait For(rs) Complete (Return As Map)
    'or
    Dim rs As ResumableSub = Starter.Stops.GetStops(59.123456,18.123456)
    Wait For(rs) Complete (Return As Map)
End Sub
 
Upvote 0

Roger C

Active Member
Licensed User
Longtime User
Haha Erel, you wan't me to think myself? :)
OK, below is what worked.

But is it correct way to do it?
The correct way to call a Class and keeping the Class alive as long as Starter is alive?

Class TestClass
B4X:
Sub Class_Globals
    Private SendString As String
    Private Latstring, Lonstring As String
    Private ResultMap As Map
    Private Latstring As String
    Private Lonstring As String
    Private ResultMap As Map
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
    ' Does it need to be something here or can it just be empty?
End Sub

Public Sub GetStops(Lat As Double, Lon As Double) As ResumableSub
 
    Log("TestClass.GetStops Start")
    
    Latstring = Lat
    Lonstring = Lon
 
    Log("TestClass GetStops: " & Latstring)
 
    Log("Sleep 5 sec")
    Sleep(5000)
    Log("Wakeup")
    
    ResultMap.Initialize
    ResultMap.Put("Row1","Bottle")
    ResultMap.Put("Row2","Hat")
 
    Log("TestClass.GetStops Before Return")
 
    Return ResultMap
 
    Log("TestClass.GetStops After Return")
End Sub


In Starter-service
B4X:
Sub Service_Start (StartingIntent As Intent)
    Tester.Initialize
End Sub

Main-module

B4X:
Sub Activity_Create(FirstTime As Boolean)
    GetData
End Sub

Sub GetData
   Log("Main.GetData Before call to class.")
 
   Dim rs As ResumableSub = CallSub3(Starter.Tester, "GetStops", 58.123456, 18.123456)
   Wait For(rs) Complete (Ready As Map)
 
   Log(Ready)
 
   Log("Main.GetData After call to class")
   
End Sub

And the Log:
B4X:
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Main.GetData Before call to class.
TestClass.GetStops Start
TestClass GetStops: 58.123456
Sleep 5 sec
** Activity (main) Resume **
Wakeup
TestClass.GetStops Before Return
(MyMap) {Row1=Bottle, Row2=Hat}
Main.GetData After call to class
 

Attachments

  • TestClass.zip
    9.1 KB · Views: 181
Upvote 0

Roger C

Active Member
Licensed User
Longtime User
Ok Thanks.
I tried without the CallSub and it worked, but I can't remember to have seen an example with 'Wait For' without the CallSub so I used that.

But I will remove the CallSub, looks better.

Thank you for your work here! Looking forward to install v9 this evening!!!!!
 
Upvote 0
Top