Android Tutorial DoEvents deprecated and async dialogs (msgbox)

Starting from B4A v7.0 the following warning will appear for DoEvents calls:
DoEvents is deprecated. It can lead to stability issues. Use Sleep(0) instead (if really needed).

The purpose of DoEvents was to allow the UI to be updated while the main thread is busy. DoEvents which shares the same implementation as the modal dialogs implementation, is a low level implementation. It accesses the process message queue and runs some of the waiting messages.

As Android evolved, the handling of the message queue became more sophisticated and fragile.
The reasons for deprecating DoEvents are:

1. It is a major source for instability issues. It can lead to hard to debug crashes or ANR (application not responding) dialogs. Note that this is also true for the modal dialogs (such as Msgbox and InputList).
2. There are better ways to keep the main thread free. For example use the asynchronous SQL methods instead of the synchronous methods.
3. It doesn't do what many developers expect it to do. As it only handles UI related messages, most events could not be raised from a DoEvents call.
4. It is now possible to call Sleep to pause the current sub and resume it after the waiting messages are processed. Sleep implementation is completely different than DoEvents. It doesn't hold the thread. It instead releases it while preserving the sub state.
Unlike DoEvents which only processed UI related messages, with Sleep all messages will be processed and other events will be raised.
(Note that using Wait For to wait for an event is better than calling Sleep in a loop.)

With that said, DoEvents is still there and existing applications will work exactly as before.

Dialogs

Modal dialogs = dialogs that hold the main thread until the dialog is dismissed.

As written above, modal dialogs share the same implementation as DoEvents. It is therefore recommended to switch to the new async dialogs instead. Using Wait For it is really a simple change:

Instead of:
B4X:
Dim res As Int = Msgbox2("Delete?", "Title", "Yes", "Cancel", "No", Null)
If res = DialogResponse.POSITIVE Then
  
End If
You should use:
B4X:
Msgbox2Async("Delete?", "Title", "Yes", "Cancel", "No", Null, False)
Wait For Msgbox_Result (Result As Int)
If Result = DialogResponse.POSITIVE Then
   '...
End If

Wait For doesn't hold the main thread. It instead saves the current sub state and releases it. The code will resume when the user clicks on one of the dialog buttons.
The other similar new methods are: MsgboxAsync, InputListAsync and InputMapAsync.

With the exception of MsgboxAsync, the new methods also add a new cancelable parameter. If it is true then the dialog can be dismissed by clicking on the back key or outside the dialog. This is the default behavior of the older methods.

As other code can run while the async dialog is visible, it is possible that multiple dialogs will appear at the same time.
If this case is relevant for your app then you should set the sender filter parameter in the Wait For call:
B4X:
Dim sf As Object = Msgbox2Async("Delete?", "Title", "Yes", "Cancel", "No", Null, False)
Wait For (sf) Msgbox_Result (Result As Int)
If Result = DialogResponse.POSITIVE Then
   '...
End If
This allows multiple messages to be displayed and the result events will be handled correctly.

Bug in B4A v7.00 / 7.01 - Async dialogs should not be used in classes as the Result event will be raised in the Activity instead of the class module. This is fixed for the next update.
 
Last edited:

luciano deri

Active Member
Licensed User
I had use Doevents to show the rotate of Progressdialog.
B4X:
strquery = "SELECT * FROM tabella "
    dbcursor = Main.dbSql.ExecQuery(strquery)
    Dim documento As String
    For i = 0 To dbcursor.RowCount - 1
        dbcursor.position = i
        ProgressDialogShow("Lettura tabella in corso")
        DoEvents
    next
What instruction can i use instead DoEvents?
 

Star-Dust

Expert
Licensed User
When Erel said he could replace DoEvents, he did not say in the general sense but for some specific uses. In fact, for some uses, WaitFor and Sleep are optimized and save you code and design time.
It is not usable for all uses because they are not exactly matching. Continue to use DoEvents where necessary
 

Computersmith64

Well-Known Member
Licensed User
I had use Doevents to show the rotate of Progressdialog.
B4X:
strquery = "SELECT * FROM tabella "
    dbcursor = Main.dbSql.ExecQuery(strquery)
    Dim documento As String
    For i = 0 To dbcursor.RowCount - 1
        dbcursor.position = i
        ProgressDialogShow("Lettura tabella in corso")
        DoEvents
    next
What instruction can i use instead DoEvents?
You could use Sleep(0) but why are you calling ProgressDialogShow in a loop?

You should call it before you start the loop.

- Colin
 

Computersmith64

Well-Known Member
Licensed User
When Erel said he could replace DoEvents, he did not say in the general sense but for some specific uses. In fact, for some uses, WaitFor and Sleep are optimized and save you code and design time.
It is not usable for all uses because they are not exactly matching. Continue to use DoEvents where necessary
I took it to mean that using DoEvents should be avoided wherever possible, so have replaced all DoEvents in my code with Sleep(0). Actually, I took the opportunity to think about whether I really needed each of them & as a result was able to remove a few as well.

In cases where you have a sub returning a value & you want to use Sleep, you can generally refactor the code so that it works. I haven't come across a situation where I couldn't replace a DoEvents with a Sleep(0).

- Colin.
 
Last edited:

luciano deri

Active Member
Licensed User
You could use Sleep(0) but why are you calling ProgressDialogShow in a loop?

You should call it before you start the loop.

- Colin
n the ProgressDialogShow show a value read in db, but if i call before cicle is same problem, without Doevent not rotate.
I see that Sleep(0), some time do return end exit cicle.
 

Filippo

Expert
Licensed User
I would change your code like this.
B4X:
    strquery = "SELECT * FROM tabella "
    dbcursor = Main.dbSql.ExecQuery(strquery)
    Dim documento As String

    ProgressDialogShow("Lettura tabella in corso")
    Sleep(1000)

    For i = 0 To dbcursor.RowCount - 1
        dbcursor.position = i
       
        Do something ...
    Next
   
    ProgressDialogHide
 

Filippo

Expert
Licensed User
Why 1000 ?
You can also write 5000, it does not matter, it's just for it to be shown. After the SQL routine, the progress dialog will end anyway.
 

Erel

Administrator
Staff member
Licensed User
Ok, but i should rewrite all code to convert with this new method for access at db
It depends on which part is the bottleneck.
The async SQL methods are good when the query itself is the slow step.
If it is the for loop then you should write it like this:
B4X:
    ProgressDialogShow("Lettura tabella in corso")
    For i = 0 To dbcursor.RowCount - 1
        dbcursor.position = i
        If i Mod 500 = 0 Then Sleep(0)
        Do something ...
    Next
    ProgressDialogHide
 
Last edited:
Top