Android Question DoEvents needed?

advansis

Active Member
Licensed User
Longtime User
Hi guys,
I have an Activity (called "DashBoard") and a Class (called "appClass").
In appClass there is resumable sub called "FileImport": it processes a CSV file.
During the file processing I call (by Callsub statement) the "ShowStatus" sub that displays a string in a label (lblStatus).
If don't put the DOEVENTS command inside the "ShowStatus", the label is not updated.
Here's the relevant code, the process starts clicking a button (BTN_Click):

DashBoard:
Sub BTN_Click
    Dim AC As App_Class: AC.Initialize
    wait for (AC.FileImport(Me)) complete (success As Boolean)
End Sub


Sub ShowStatus(Msg As String,RowNum As Int)
    If RowNum>0 Then
        lblStatus.Text="Processing row: " & RowNum
    else if Msg.Length>0 Then
        lblStatus.Text=Msg
    Else
        lblStatus.Text=""
    End If
    ' lblStatus.Invalidate ' <- NOT WORKING
    ' Sleep(0) ' <- NOT WORKING
    DoEvents ' WORKS !!!
End Sub

AppClass:
Sub Class_Globals
    Dim DummyAct As Activity
End Sub

...

Sub DataImport(Caller As Object) As ResumableSub
    ...
    Dim NRows as int=0
    Do While (...)
        ...
        NRows=NRows+1
        if NRows mod 100=0 Then ' Update the Status only each 100 rows
            If SubExists(Caller,"ShowStatus") Then CallSub3(Caller,"ShowStatus","",NRows)
        End If
    Loop
    If SubExists(Caller,"ShowStatus") Then CallSub3(Caller,"ShowStatus","",0) ' Clear Status
    Return true
End Sub

During debugging, if I put a breakpoint in ShowStatus sub, the label is updated...
I know DoEvents should not be used, but how can I achieve what I need ? Thanks in advance

PS. Tried also CallSubDelayed3 (I don't think is relevant), with no luck...
 

OliverA

Expert
Licensed User
Longtime User
Sub is called. If is put a breakpoint inside ShowStatus, it is fired and ... label is updated!
That's because in Debug mode, you are pausing the loop and giving your app a chance to process the events (in your case the event to update the label). That's why often blocking code will work "correctly" in Debug mode (since Debug mode will in a sense "sleep" you routine, giving the rest of the system time to process events). This may be a horrible explanation, but that is the gist of it.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
This should give a chance to update the label, but ...
Ok, I may display my ignorance here, but CallSubDelayed creates an event, CallSub is a direct call and would therefore not give your app a chance to process other events. Plus if it were an Event generator, the sub would never get called (and here it is) since the loop has nothing in it to allow the app to process events.
 
Upvote 0

peacemaker

Expert
Licensed User
Longtime User
Looks like it needs ShowStatus with Sleep(0) AND CallSubDelayed(Caller, "ShowStatus"....
 
Upvote 0

advansis

Active Member
Licensed User
Longtime User
The issue is still that your loops are 100% blocking! Nothing in those loops gives the system a breather to update your label. Both SQL calls made in those loops block. Let's say you import 1000 statements. The loop will process 1000 times, creating 2000 blocking SQL statements and 1000 blocking CallSub's. In your sub to update the label, setting the text on the label is not like calling CallSub to update the text, but it creates an Event (sorta like CallSubDelayed). The problems is that your loop (and the sub that displays the text) does not give you app any chance to process any events. Therefore, your label is never updated. That is why DoEvents works (but don't use it). It stops your loop and gives the system a chance to process events, including the event that updates your label. Do not use ExecQuerySingleResult and do not use ExecNonQuery2 in the loop. Replace them with async calls! Plus since you call TransactionSuccessful outside the loop anyways, it makes more sense to use AddNonQueryToBatch inside the loop and ExecNonQueryBatch outside of the loop (it will then handle the transaction for you). The ExecQuerySingleResult needs to be replaced with ExecQueryAsync. Even then you may need to add a Sleep(with some value) to let the system catch up with the events.
Thank you for the reply... I will try it
 
Upvote 0

advansis

Active Member
Licensed User
Longtime User
You should also add an index on the code column in all the relevant tables. Otherwise a full table scan is needed each and every query.
@Erel, at line 18 of the creation code, I remarked index creation: I just removed the statements (and other not relevant) to have cleaner code...
 
Upvote 0

advansis

Active Member
Licensed User
Longtime User
Ok, I may display my ignorance here, but CallSubDelayed creates an event, CallSub is a direct call and would therefore not give your app a chance to process other events. Plus if it were an Event generator, the sub would never get called (and here it is) since the loop has nothing in it to allow the app to process events.
I do not know the inside-difference, but I suppose you are right.
I will try to use async methods and some more sleep.
CallSubDelayed(s) probably will be called at the end of my process... all at once !
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
CallSubDelayed(s) probably will be called at the end of my process... all at once !
The queue is not unlimited. You (most)probably loose events.
Make your code run async using sleep and wait for
 
Upvote 0

advansis

Active Member
Licensed User
Longtime User
SOLVED (I think)!!!
Adding, by now, some sleep(0) seems to work, the next step is to change sql to async.
The strange thing is that in my original code, the statement at line 20, even if is followed by a wait for (line 23), does not update the status label...
Thanks to everybody, you made my day happier!
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
the statement at line 20, even if is followed by a wait for (line 23), does not update the status label...
That Wait For is placed before the loop that processes your data and updates your label. Therefore that Wait For has nothing to do, nor does it impact the updating of your label.
but consider that the task itself (DataImport) is called asyncronously
Why? Wait For <> Threading. If you declare a Sub Resumable, but you don't use a Sleep() or Wait For within that Sub, than its just 100% blocking. It runs as if you had not declared it Resumable. The Resumable declaration was added to the B4X language to allow returning values from resumable subs.
the main thread DOESN'T HANG and works correctly
If your sub "DOESN'T HANG", then it just means that it processes the data sequentially (blocking) fast enough that it is not noticeable. If that is so, then the label updating is mute. But, if later on you process even more data, it would become (with the original code) more noticeable and there would be a point where Android thinks your application is stuck and decides to kill it.
 
Upvote 0

advansis

Active Member
Licensed User
Longtime User
Thanks! The real problem was I tought resumablesubs could be considered likes threads...
 
Upvote 0
Top