Android Question Problem with "wait for": Activity_resume is called before Activity_create finishes

linuxfan

New Member
Hello all,

I am writing an app which uses MQTT, and I find a problem with "wait for".

In an activity "info" I must collect several data from MQTT: every datum is asked for by publishing a topic and waiting for the reply. I would like to write several lines like:

B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("ginfo")
    ...
    lAper.text = "Aperture " & ( readdata("a1") + 256*readdata("a2") )
    lPosit.text= "Position " & readdata("W18")
    ...

Problem is that, as executing "sleep" or "wait for" causes a subroutine to
return to caller, I can't find a way to make it work this way. Instead, I
had to write:

B4X:
    ...
    wait for (readdatai("a1")) complete (res As String)
    Dim ap1 As Int = res
    wait for (readdatai("a2")) complete (res As String)
    Dim ap2 As Int = res
    lAper.text = "Aperture " & ( ap1 + 256*ap2 )

This is bloated (5 lines instead of one) and, moreover, causes the sub
Activity_Resume to be called before Activity_Create is finished, disrupting the
program logic. This is a problem because the page is not fully loaded at that time.

Is there a way to simplify things? And how can I postpone the code in Activity_resume until Activity_Create is finished?
 

LucaMs

Expert
Licensed User
Longtime User
Assuming that it is always better to create B4XPages projects...

From within Activity_Create call a fill routine, using CallSubDelayed.

B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("ginfo")
    CallSubDelayed(Me, "FillViews")
End Sub

Private Sub FillViews
    lAper.text = "Aperture " & ( readdata("a1") + 256*readdata("a2") )
    lPosit.text= "Position " & readdata("W18")
End Sub
 
Last edited:
Upvote 0

toby

Well-Known Member
Licensed User
Longtime User
Move all Wait For code in Activity_create to other places including Activity_resume
 
Last edited:
Upvote 0

linuxfan

New Member
"Assuming that it is always better to create B4XPages projects..."
Problem with B4XPages is that rotation of the screen is not accounted for.

Anyway, using CallSubDelayed does exactly the same as before; this is the log with comments:

** Activity (gatemenu) Pause, UserClosed = false ** <- from a menu, call another activity
** Activity (gateinfo) Create, isFirst = true ** <- this activity "info"
ginfo create, firsttime=true <- this is my log("...") (inside activity_create - useless!)
** Activity (gateinfo) Resume ** <- Activity_resume called before (see later)
ginfo resume <- my log("...")
sending message to waiting queue (mqtt_disconnected)
****************** Finished fill. <- my log("...") signalling that the page is completed.

(how should I format these logs?)

Anyway I have kind of solved the issue. First of all, the bloated code has been reduced grouping together the requests via array+map:

B4X:
    wait for (readregs(Array As String("W18", "Ua1", "Ua2"))) complete (res As Object)
    gatelen = q90("Ua1") + 256*q90("Ua2")  <- q90() reads a global map

The readregs() subroutine reads the indicated registers, one after the other, and puts the answers in a global map which can then be read. The q90() routine reads the map: q90("a1") is cleaner than answers.get("a1"). To not lose control of the source I need short and clear code.
Unfortunately the code is still bloated because lAper.text = "Aperture " & ( readdata("Ua1") + 256*readdata("Ua2") ) would be more concise and clearer than the two lines above.

The fact that Activity_Resume is always called before Activity_Create finishes has been solved by using a global flag. Quite easy. But a little dirty.
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
The fact that Activity_Resume is always called before Activity_Create finishes has been solved by using a global flag. Quite easy. But a little dirty.
This can only happen when there is a Wait For or Sleep statement in Activity_Create. So, making a Sub execute those instructions by calling it from within Activity_Create and using CallSubDelayed - that is, only upon completion of the calling routine - solves the problem, without having to resort to module-level flags.


Problem with B4XPages is that rotation of the screen is not accounted for.
If most of the layouts in your app have a certain orientation and only one or two have different orientations, you can use the Activity Main to manage the B4XPages and create other Activities that have opposite orientation.

Not a great solution but it can be done.
 
Upvote 0

linuxfan

New Member
Thank you for your reply. Actually I've used CallSubDelayed and, as you say, I use "wait for" and "sleep":

B4X:
private Sub filldata
    mqttmod.mqtt.Close
    Do While mqttmod.mqtt.IsInitialized
       Sleep(100)
    Loop
    
    mqttmod.initmqtt
    wait for mqtt_connected
    Sleep(10)

    wait for (readregs(Array As String("W18", "Ua1", "Ua2"))) complete (res As Object)
    gatelen = q90("Ua1") + 256*q90("Ua2")
    ...
    wait for (readregs(Array As String("M220", "UU3", "W28"))) complete (res As Object)
    ...
    wait for (readregs(Array As String("M220", "UU3", "W28"))) complete (res As Object)
    
    Log("****************** Finished fill.")
    updfinished = True     ' global flag
End Sub

Sub Activity_Create(FirstTime As Boolean)

    Activity.LoadLayout("ginfo")
    updfinished = False        ' global flag'
    
    ...  ' rest of code without sleep or wait
    
    CallSubDelayed(Me, "filldata")

End Sub

Sub Activity_Resume
    mqttmod.infotimer.Initialize("infotimer", 1000)
    mqttmod.infotimer.Enabled=True
End Sub

but, as seen in the log of previous post, Activity_Resume is called before Activity_Create is finished. As Erel says, this is expected and, for my limited knowledge, I even understand why: behind the scenes there is a routine that invokes Activity_Create and Activity_Resume one after the other. The flux of code cannot be stopped: as soon as a "sleep" or "wait for" is invoked, the stack of called routines is rolled up to the routine behind the scenes.

I must start a timer only after the various data has been prepared, the timer interferes with the loading of data. I could start it from the Filldata routine, where the order of execution is guaranteed, but that routine is called only when the activity is created, not after a pause/resume sequence. Moreover, Activity_Resume is called just once, if one misses that opportunity of doing something, there are no ways to do something when the activity resumes. If the order of the events was respected it was easy.

It is not clear to me what happens to a running timer when the activity is paused. Is the timer paused too? And when the activity resumes, is the timer enabled again? If both answers are yes, then my problem is solved - I don't need Activity_Resume. In my tries I've got lost, sometimes the timer was stopped, some other times it was not, so I used a global flag that prevents the Timer_Tick handler to do its duties.

Regarding the orientation of the screen, the problem is when a user rotates the device - he can do it at will, in the middle of an activity, and the App should correctly manage this. That's all. It is not so important, but I started the project in this way and I am too far to change things, I suppose.

I think that the fact that "sleep" and "wait for" are equivalent to a return is sometimes a good thing, but sometimes is a pain. A way to "really block" the code, sometimes, could simplify. This is only an opinion from a programmer (me) coming from other languages, and without a full comprehension... I must study and experiment.
 
Upvote 0
Top