Android Question [Unsolved] ResumableSub, custom type return and obfuscation

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
I have a resumable sub, akin to:
B4X:
Public Sub DeviceGetStatus As ResumableSub
    Dim Result as MyCustomType
    ' .... more code
    Log("Device status request complete.")
     Return Result
End Sub
In another block, I call it with:
B4X:
Wait For (DeviceGetStatus) Complete(Status As MyCustomType)
This works fine in debug and release. When I do Release(obfuscated), this call errors out with:
B4X:
Device status request complete.
java.lang.Exception: Sub complete signature does not match expected signature.
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:215)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:197)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.keywords.Common$14.run(Common.java:1773)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6523)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
java.lang.Exception: Sub complete signature does not match expected signature.
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:215)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:197)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.keywords.Common$14.run(Common.java:1773)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6523)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
I did not expierence this error prior to B4A 11.5. Was something changed, or was I doing it incorrectly all along?
 
Last edited:

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Odd, after cleaning the project and re-trying again it now never fires the complete event when in debug mode and throws the RTE in both release and release(obfuscated).

I haven't messed with this class in over a year, maybe two. I hadn't actually tested this part of the program since it was compiled with version 10-point-something and it worked fine. After upgrading to 11.5 and testing it again now it's behaving badly.

I tried changing the custom type in the Wait-For to an object and re-casting back to the custom type with the same result. It's not a terribly complex type:
B4X:
	Type cgDeviceStatus(Status As String, CurrentScreen As Int, ResponseMessage As String, SerialNumber As String, AppVer As String, _
						OSVer As String)

Suggestions?

--- Edit ---- Thank you, @LucaMs, for the test project and testing it. I'm not using B4X Pages if that matters at all, the activity I'm testing is two-or-three deep from the Main but the previous activities were finished so the one with this problem should be the only activity still running. Also, I don't know if it matters but the resumable sub has a wait for inside it from an HttpJob.
 
Last edited:
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
The code you posted in the first post should work.
I agree, and it was working up until yesterday when I tested it again after upgrading to version 11.5. I think the last time I tested that class it was running under version 10.9/7.

Currently, when running under debug the Wait For event never fires (I have a log statement right after it and I never see it). When running under release, it throws the signature doesn't match error. For clarity's sake, here is the entire procedure I am waiting for:
B4X:
Public Sub DeviceGetStatus As ResumableSub
    Dim poJob As HttpJob
    Dim poResult As cgDeviceStatus

    Reset 'this clears some global variables and resets some global flags
    poResult.Initialize
    poResult.CurrentScreen = DS_Unknown
    poResult.Status = "Error communicating with device"

    If msDeviceIP <> "" Then
        Dlog("Requesting device status...", Colors.Magenta)
        Dim psRequest As String =   $"http://${msDeviceIP}:8080/v2/pos?Action=Status&Format=XML"$
        ' issue request to device
        poJob.Initialize("", Me)
        Try
            ' send HTTP request
            poJob.GetRequest.Timeout = 15000
            poJob.Download(psRequest)
            ' wait for completion
            Wait For (poJob) JobDone(poJob As HttpJob)
            msRawDeviceResponse = poJob.GetString
            If poJob.Success Then
                Dim poTree As clsXmlTree = ParseXML(msRawDeviceResponse)
                If poTree.TreeIsEmpty  Then
                    msLastError = "Unknown response from device for status query: " & msRawDeviceResponse
                Else
                    poResult.Status = poTree.GetNodeTextFromRoot("Status")
                    Dim psVal As String = poTree.GetNodeTextFromRoot("CurrentScreen")
                    If IsNumber(psVal) Then
                        poResult.CurrentScreen = psVal
                    Else
                        poResult.CurrentScreen = DS_Unknown
                    End If
                    poResult.ResponseMessage = poTree.GetNodeTextFromRoot("ResponseMessage")
                    poResult.SerialNumber = poTree.GetNodeTextFromRoot("SerialNumber")
                    poResult.AppVer = poTree.GetNodeTextFromRoot("ApplicationVersion")
                    poResult.OSVer = poTree.GetNodeTextFromRoot("OSVersion")
                End If
            Else
                msLastError = poJob.ErrorMessage
            End If
        Catch
            msLastError = " Device Error: " & LastException.Message
        End Try
        poJob.Release
    End If
    If msLastError <> "" Then
        LogColor("DeviceGetStatus: " & msLastError, Colors.Red)
    Else
        Dlog("Device status request complete.", Colors.Magenta)
    End If
 
    Return poResult
End Sub
And here is the relevant portion of the code that is doing the waiting (and I have verified all of the loop variables prior to executing the Wait-For):
B4X:
        Do While Not(mbAbort) And (DateTime.Now - plStart) < 120000 And Not(pbOnScreen)
            LogColor("Checking device status, before wait-for.", Colors.Magenta)
            Wait For (DeviceGetStatus) Complete(Status As cgDeviceStatus)
            Dlog("keyed status wait loop, device screen code is "& Status.CurrentScreen, Colors.Magenta) 'I Never see this log statement
            If Status.CurrentScreen = DS_MainPaymentCollection Then
                pbOnScreen = True
                Exit
            Else
                Dlog("Not swipe screen - " & Status.CurrentScreen, Colors.Magenta)
                If Status.CurrentScreen = DS_Unknown Then
                    Exit  'some error
                Else
                    Sleep(250)
                End If
            End If
        Loop
And here is the relevant portion of the log with the RTE information:
B4X:
waiting for device swipe screen
Checking device status, before wait-for.
Requesting device status...
Device status request complete.
java.lang.Exception: Sub complete signature does not match expected signature.
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:215)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:197)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.keywords.Common$14.run(Common.java:1773)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6523)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
java.lang.Exception: Sub complete signature does not match expected signature.
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:215)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:197)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.keywords.Common$14.run(Common.java:1773)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6523)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)


I would like to solve this if I can, as I use this basic methodology frequently and I am concerned that other Wait-For's might fail unexpectedly.
 
Last edited:
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
There are no known issues related to resumable subs
I'm not saying there is, I'm asking for help understanding how "Return CustomVariableType" can cause a method signature mismatch when the wait statement variable type is correct?
 
Last edited:
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Reasonably, I have debug statements everywhere. I see the last line from the get status block, and I never see the debug line after the wait for. When I attempt to test in debug mode via B4A-Bridge the Wait For event never fires, it just hangs.

--- EDIT ---
Ok, this just gets weirder and weirder. If run in debug mode, it hangs, the last log line I see is the "Device status complete" one. If I then put a break point at the
"Dlog('Keyed status wait loop...'" line, the breakpoint will fire, and the sub starts working properly if I keep hitting run after the break point fires.

I then remove the break point and try the routine again, I'm back to having it hang. All of this is without restarting the run, by the way. I couldn't make this stuff up if I tried. 🙄
 
Last edited:
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Not easily. The project is rather large with 20 activities, 40+ classes and the device that I'm "talking to" is a proprietary hand-held unit that you send HTTP Post requests to that it responds to... so rather hard for anyone else to test this.

I could try to mock up a sample that just goes to a random HTTP site and mimics the code-flow to see if I can reproduce this, but that seems overkill. The fact that if I add a break point to the log statement after the wait for makes it start working, well that has me completely baffled.

This class, including this procedure, was working fine back in Sept. of 2020, which would have been V10.2 of B4A near as I can tell? I haven't touched this project, or that class, since then other than recompiling it under the new versions of B4A/JDK/ASDK as they were released.
 
Last edited:
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Precisely for this reason perhaps the exception concerns a different point of the project.
There are a lot of moving parts to the project (e.g., MQTT, UDP monitoring, etc.) so I can't argue that.

I've created a test project as slimmed down as I can get it (main, starter and a couple of classes). The actual program has a wrapper for this class that handles the UI and timeout monitoring, cancel options, etc.

The test for this example program has the same results in debug (hang or works with breakpoint). In both release modes, however, I no longer get the runtime error, it just hangs as it does in debug mode sans breakpoint.

This tends to indicate it is not the wait for line throwing the error as @LucasMs has suggested, but it does not explain why the hang is occurring. Since I doubt anyone else has the required device testing will be unlikely, but I've attached the project in case you're interested.
 

Attachments

  • WaitForTest.zip
    22 KB · Views: 88
Upvote 0

swChef

Active Member
Licensed User
Longtime User
There are a lot of moving parts to the project (e.g., MQTT, UDP monitoring, etc.) so I can't argue that.

I've created a test project as slimmed down as I can get it (main, starter and a couple of classes). The actual program has a wrapper for this class that handles the UI and timeout monitoring, cancel options, etc.

The test for this example program has the same results in debug (hang or works with breakpoint). In both release modes, however, I no longer get the runtime error, it just hangs as it does in debug mode sans breakpoint.

This tends to indicate it is not the wait for line throwing the error as @LucasMs has suggested, but it does not explain why the hang is occurring. Since I doubt anyone else has the required device testing will be unlikely, but I've attached the project in case you're interested.
Install a separate b4a ide environment back at v10.2 (assuming you saved the installer), retest there. Then upgrade to any other intermediate version you have and retest. Then
Odd, after cleaning the project and re-trying again it now never fires the complete event when in debug mode and throws the RTE in both release and release(obfuscated).

I haven't messed with this class in over a year, maybe two. I hadn't actually tested this part of the program since it was compiled with version 10-point-something and it worked fine. After upgrading to 11.5 and testing it again now it's behaving badly.

I tried changing the custom type in the Wait-For to an object and re-casting back to the custom type with the same result. It's not a terribly complex type:
B4X:
    Type cgDeviceStatus(Status As String, CurrentScreen As Int, ResponseMessage As String, SerialNumber As String, AppVer As String, _
                        OSVer As String)

Suggestions?

--- Edit ---- Thank you, @LucaMs, for the test project and testing it. I'm not using B4X Pages if that matters at all, the activity I'm testing is two-or-three deep from the Main but the previous activities were finished so the one with this problem should be the only activity still running. Also, I don't know if it matters but the resumable sub has a wait for inside it from an HttpJob.
Try installing a completely separate ide/java/Android tools? From scratch.
 
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
I appreciate the thought, @swChef, but my goal here is to solve this with 11.5 and the most recent tools, not revert back to a previous version. I only mention it works under a previous version to help diagnose the problem (this is not a new piece of code that I'm trying to get to work).

The line that is failing is a Wait For statement, I'm not sure how I can screw that up, programmatically speaking. The resumable sub it is calling clearly completes and has ResumableSub as the return type.

The only "odd" bit to this entire function is that there is a previous HTTP job that is still running in the background started by the sub that hangs. The methodology is not my design, I have to work with what I'm given, I would have designed the device a bit differently and fired the programmer that suggested this design methodology.

I've already posted the complete code if you're curious, but the methodology is fairly straightforward:
  • Main Routine
    • Setup some variables
    • Call another resumable sub (no return type specified) that starts an HTTP job with 3 min. timeout and wait for http jobdone
    • Loop checking device status, this loop calls another resumable sub (with ResumableSub return type that has its own HTTP job with wait for jobdone) and waits for that sub to complete (this hangs)
    • Once status is met, exit loop
    • Loop checking for global var set by the earlier HTTP sub call (waiting for user input on device basically)
    • Return final result
Today I'll spend time modifying the Wait For to look for a specific event that I'll fire in the status sub. If that still fails, I'll try coding a loop that looks for a global var set by the status sub. I'll let you know what I find.
 
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Ok, the results from the test:
B4X:
' Changing sub to no return signature and raising an event ...
    ' ...
    CallSubDelayed2(Me, "Status_Complete", poResult)
End Sub
'...
DeviceGetStatus
Wait For Status_Complete(Result As cgDeviceStatus)
'...
Gives the same results, the program never comes back from the Wait For.

Modifying the sub to set a global result variable and a global completion flag, then a manual loop to wait for that flag:
B4X:
    '....
    moDeviceStatus = poResult
    mbStatusComplete = True
End Sub

'...
        Do While Not(mbAbort) And (DateTime.Now - plStart) < 120000 And Not(pbOnScreen)
            LogColor("Checking device status, before wait-for.", Colors.Magenta)
            DeviceGetStatus
            Do While Not(mbAbort) And (DateTime.Now - plStart) < 120000 And mbStatusComplete = False
                Sleep(250)
            Loop
            Dlog("keyed status wait loop, device screen code is "& moDeviceStatus.CurrentScreen, Colors.Magenta)
            If moDeviceStatus.CurrentScreen = DS_MainPaymentCollection Then
                pbOnScreen = True
                Exit
            Else
                Dlog("Not swipe screen - " & moDeviceStatus.CurrentScreen, Colors.Magenta)
                If moDeviceStatus.CurrentScreen = DS_Unknown Then
                    Exit  'some error
                Else
                    Sleep(250)
                End If
            End If
        Loop
This methodology works as expected.

I would still like an answer as to why the in-line Wait For never fires, because at this point I am worried about other Wait For blocks randomly failing elsewhere in the project.
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
With reference to your first code fragment in post #17. The timings look like they might be a bit odd although without the full code I can't say. In one Sub you call CallSubDelayed2 then exit the Sub. If that Sub return gets back to the message loop then the CallSubDelayed2 might execute before you have called the Wait For, which I assume is in another Sub, but as there is no context for the location of the code in the fragment I can't see the actual order of events. If the CallSubDelayed2 does execute before the Wait For then the Wait For will hang as the event has already gone by.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
If that Sub return gets back to the message loop then the CallSubDelayed2 might execute before you have called the Wait For,
In that example, I changed the sub signature to no return value (void) and I'm not waiting for that sub's return value. I just call the status check directly, it _should_ return at the "Wait For JobDone.." part in the status sub and then execute the wait for the Status_Comlete event in the calling block. The Status_Complete is fired before exiting the get status block, after waiting for the HTTP job.

I'm not sure what you mean by "timing," are you suggesting adding a sleep immediately before the callsubdelayed2?
 
Upvote 0
Top