iOS Question [solved] How to create asynchronous methods? (callSubDelayed)

Discussion in 'iOS Questions' started by yonson, Jan 26, 2015.

  1. yonson

    yonson Active Member Licensed User

    Hi Erel

    in using the iHud library, I wish to show a busy message before starting on a long task, then remove it once its done.

    In B4A, this is very straightforward - I just show the message, call 'DoEvents' and then call the method. Then 'DoEvents' again and then that works fine.

    However, in B4i I'm aware this isn't possible. And so this simple operation doesn't work in sequence. Erel mentions in the iHud thread that in order to achieve this you must call an async method, but I was wondering if you could clarify how to create a async method?

    Here is a very simple example. 'DoBusyEvent' is an example method which simply loops to represent a method which takes a long time to complete:-

    Code:
    Dim hd As HUD
    hd.ProgressDialogShow(
    "busy")
    DoBusyEvent
    hd.ProgressDialogHide

    Private Sub DoBusyEvent

    For i=0 To 10000
    Log(i)
    Next
    End Sub
    how could I make 'DoBusyEvent' an async method, i.e. so that the busy message is shown whilst the method is running, and only closed once it is finished?
    Many thanks!
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    Can you describe the real "slow" code?
     
  3. yonson

    yonson Active Member Licensed User

    I use a range of different functions in my projects which I could consider slow, but typically these relate to processing database queries and rendering the results (which I do in code and not in the designer as they are dynamic).

    The code above is simply an example, it loops from 1 to a high number outputting to the log. It serves to function but merely helps to illustrates the issue, which is where I want to:-

    1. Show a busy message
    2. run the 'slow' code
    3. hide the busy message

    Could you clarify how to make a method async, I assume the solution is to only call the 'hide message' by making the slow code call a job done or similar but I'm not sure how to implement this?
     
  4. Erel

    Erel Administrator Staff Member Licensed User

    There is no simple / general way to make a method asynchronous. There is no method equivalent to DoEvents in iOS.

    This is why I'm asking about the actual code that you are running to see whether there are way to optimize it or use different methods.
     
  5. yonson

    yonson Active Member Licensed User

    Hi Erel,

    to be honest it would be outside the scope of the forum to post all the methods I've done, its a huge app and has thousands of lines of code, as I say in essence though it is running database calls (so using a wrapper class on DBUtils) and I render all the screens in code, not in the designer as they are dynamic.

    I suppose what I need is a design pattern or something I could implement throughout.

    If you run my simple example at the top of the page you'll see the issue. Could I ask - if you wanted to re-write this example so as to make it run properly, how would you go about it. i.e.

    Code:
    Dim hd As HUD
    ' 1. show the busy dialog
    hd.ProgressDialogShow("busy")
    ' 2. do something time consuming
    DoBusyEvent
    ' 3. ONLY hide the busy dialog when complete
    hd.ProgressDialogHide

    Private Sub DoBusyEvent
    For i=0 To 100000
    Log(i)
    Next
    End Sub
     
  6. tucano2000

    tucano2000 Active Member Licensed User

    Try this code. You can change TimerInterval and allow your application to perform other tasks.

    Code:
    'Code module
    #Region  Project Attributes
        
    #ApplicationLabel: B4i Example
        
    #Version: 1.0.0
        
    'Orientation possible values: Portrait, LandscapeLeft, LandscapeRight and PortraitUpsideDown
        #iPhoneOrientations: Portrait, LandscapeLeft, LandscapeRight
        
    #iPadOrientations: Portrait, LandscapeLeft, LandscapeRight, PortraitUpsideDown
    #End Region

    Sub Process_Globals
        
    'These global variables will be declared once when the application starts.
        'Public variables can be accessed from all modules.
        Public App As Application
        
    Public NavControl As NavigationController
        
    Private Page1 As Page
        
    Private TimerDoBusyEvent  As Timer
        
    Private i As Int = 0
        
    Private hd As HUD
    End Sub

    Private Sub Application_Start (Nav As NavigationController)
        NavControl = Nav
        Page1.Initialize(
    "Page1")
        Page1.Title = 
    "Page 1"
        Page1.RootPanel.Color = 
    Colors.White
        NavControl.ShowPage(Page1)

        
    Dim hd As HUD
        
    ' 1. show the busy dialog
        hd.ProgressDialogShow("busy")
        
    ' 2. do something time consuming
        'DoBusyEvent
        TimerDoBusyEvent.Initialize("DoBusyEvent",10)  'TimerInterval=10
        TimerDoBusyEvent.Enabled=True

        
    ' 3. ONLY hide the busy dialog when complete
        'hd.ProgressDialogHide
    End Sub

    Private Sub DoBusyEvent_Tick
        TimerDoBusyEvent.Enabled=
    False
        
    Log(i)
        i=i+
    1
        
    If i<=100000 Then TimerDoBusyEvent.Enabled=True Else hd.ProgressDialogHide
    End Sub
     
    Last edited: Feb 2, 2015
  7. Erel

    Erel Administrator Staff Member Licensed User

    It will not work. The timer event will only fire after the main thread exists the sub and returns to handle the internal message queue.

    As I previously wrote there is no keyword equivalent to DoEvents in iOS. Also remember that you can only access the UI layer from the main thread, so running this method in a background thread will not help.

    There are all kinds of possible solutions. For example you can split the large job into smaller jobs and use CallSubDelayed to execute the next sub-task each time.
     
  8. yonson

    yonson Active Member Licensed User

    thanks for your help Tucano - yes indeed as Erel says it does not work.

    Essentially all I want to do is show the user that the app is busy 'doing something' whilst its carrying it out, essentially its a user feedback notification to announce that their input has been received, so its essential that its shown first.

    Erel I've pasted in a real example from the code, with the show / hide messages embedded.

    The important lines are

    RenderUtils.showBusyMessage (MUST be shown first, to tell the user that we're busy doing something)

    and

    RenderUtils.hideMessage (MUST ONLY be called once everything is complete)

    Any suggestions for how to modify for the callsubdelayed?

    Code:
    'Class module
    Sub Class_Globals
        
    Public thisPage As Page
        
    Dim header As HeaderBar
        
    Private scr_content As ScrollView
        
    Dim allGroups,allSubgroups,allFeatures As List

        
    Dim act_groups,act_subgroups,act_features As ActionSheet
        
    Dim pnl_groups,pnl_subgroups,pnl_features As Panel 
            
    Dim nearbys As List
         
    End Sub

    'Initializes the object. You can add parameters to this method if needed.
    Public Sub Initialize(Eventname As String)
       RenderUtils.showBusyMessage

    thisPage.Initialize(
    "pageNearby")
        thisPage.RootPanel.Color=
    Colors.Black

        allGroups = DatabaseInterface.getLutGroups
        allSubgroups = DatabaseInterface.getLutSubgroups
        allFeatures = DatabaseInterface.getFeaturesList
        renderHeaderPanel 
        refreshContent
     
    End Sub

    Public Sub closePage
     
        Main.NavControl.RemoveCurrentPage

    End Sub


    Private Sub pageSearch_Resize(Width As Int, Height As Int)
        refreshContent
    End Sub

    Private Sub refreshContent
        
    If scr_content.IsInitialized Then
            scr_content.RemoveViewFromParent
        
    End If
     
        scr_content.Initialize(
    "",Main.screenWidth,Main.screenHeight-Main.headerBarHeight) ' set height of scrollview later
     
        
    ' *********************************
        ' ADD STUFF TO scr_content, then set height accordingly
        ' *********************************
     
        
    Dim pnl As Panel
      pnl = scr_content.Panel
     
     
        nearbys = DatabaseInterface.getNearbys
        
    Dim totalHeight As Int
        totalHeight=
    0
        
    '1. get all the favourites from the settings table
        nearbys = DatabaseInterface.getNearbys
        
    ' now add in others     
        For i = 0 To nearbys.Size-1
            
    Dim thisResult As Map
            thisResult = nearbys.Get(i)
            
    ' add in a panel for each with
            ' header (title and modifier button for increment, decrement & remove), and 'show all' button to do a search
            ' maximum 3 entries, or 1 entry of 'none' if not found
         
         
            
    Dim cp As Map
            cp = getNearbyPanelMap(thisResult)
    '        Log("THIS CP HAS HEIHGT "&cp.Get("height"))
         
            pnl.AddView(cp.Get(
    "panel"),0,totalHeight,Main.screenWidth,cp.Get("height"))

            totalHeight=totalHeight+cp.Get(
    "height")
        
    Next 
     
        
    ' add on 3 dropdowns which will add them to the others
            pnl_groups.Initialize("pnl_groups")
            pnl_subgroups.Initialize(
    "pnl_subgroups")
            pnl_features.Initialize(
    "pnl_features")     
            pnl_groups=RenderUtils.renderShadowPanelWithClick(pnl_groups,
    "Add Nearest Group",Main.colour_white,"#000000",Main.colour_purple,Main.colour_lightgrey,Main.screenWidth,Main.screenWidth*0.15,Me)
            pnl_subgroups=RenderUtils.renderShadowPanelWithClick(pnl_subgroups,
    "Add Nearest Sub-Group",Main.colour_white,"#000000",Main.colour_purple,Main.colour_lightgrey,Main.screenWidth,Main.screenWidth*0.15,Me)
            pnl_features=RenderUtils.renderShadowPanelWithClick(pnl_features,
    "Add Nearest Feature",Main.colour_white,"#000000",Main.colour_purple,Main.colour_lightgrey,Main.screenWidth,Main.screenWidth*0.15,Me)     
            pnl.AddView(pnl_groups,
    0,totalHeight,Main.screenWidth,Main.screenWidth*0.15)
            totalHeight=totalHeight+(Main.screenWidth*
    0.15)
            pnl.AddView(pnl_subgroups,
    0,totalHeight,Main.screenWidth,Main.screenWidth*0.15)
            totalHeight=totalHeight+(Main.screenWidth*
    0.15)
            pnl.AddView(pnl_features,
    0,totalHeight,Main.screenWidth,Main.screenWidth*0.15)
            totalHeight=totalHeight+(Main.screenWidth*
    0.15)
        scr_content.ContentHeight=totalHeight
        thisPage.RootPanel.AddView(scr_content,
    0,Main.headerBarHeight,Main.screenWidth,Main.screenHeight-Main.headerBarHeight)


                RenderUtils.hideMessage
     

    End Sub

    Sub shadowPanel_Click
        act_groups.Initialize(
    "act_filter","","Cancel","",MiscUtils.MapToList(allGroups,"full_value"))

        act_groups.Show(thisPage.RootPanel)
     
    End Sub


    Sub btn_settings_Click
        
    Dim p As Button
        p = 
    Sender
        
    'Dim thisResult As Map
        'thisResult = p.Tag
            Log("trying to delete "&p.Tag)
            DatabaseInterface.nearbyDelete(p.Tag)
            refreshContent
     
    End Sub

    Sub btn_search_Click
       
    Dim p As Button
       p = 
    Sender
        
    Dim thisResult As Map
        thisResult = p.Tag
     
        
    Dim restrictions As List
        restrictions.Initialize


        
    'Msgbox("locate id : "&refId,"title")
        Select Case thisResult.Get("type")
                
    Case "G"
                    restrictions.Add(
    "e.id=eg.establishment_id")
                    restrictions.Add(
    "e.id=et.establishment_id")
                    restrictions.Add(
    "et.type_id=t.id")         
                    restrictions.Add(
    "eg.group_id="&thisResult.Get("ref_id"))
                    restrictions.Add(
    "e.address_street=s.id"
                    SearchUtils.clearSearch
                    SearchUtils.AddSearchElement(
    "EG",thisResult.Get("ref_id"),"establishment e,establishment_type et, establishment_group eg,lut_type t,lut_street s",restrictions,"R")
                    
    Dim ps As pageSearch
                    ps.Initialize(
    "")
                    Main.NavControl.ShowPage(ps.thisPage)
                
    Case "S"
                    restrictions.Add(
    "e.id=eg.establishment_id")
                    restrictions.Add(
    "e.id=et.establishment_id")
                    restrictions.Add(
    "e.id=es.establishment_id")
                    restrictions.Add(
    "et.type_id=t.id")         
                    restrictions.Add(
    "es.subgroup_id="&thisResult.Get("ref_id"))
                    restrictions.Add(
    "e.address_street=s.id"
                    SearchUtils.clearSearch
                    SearchUtils.AddSearchElement(
    "ES",thisResult.Get("ref_id"),"establishment e,establishment_type et, establishment_group eg,lut_type t,lut_street s,establishment_subgroup es",restrictions,"R")
                    
    Dim ps As pageSearch
                    ps.Initialize(
    "")
                    Main.NavControl.ShowPage(ps.thisPage) 
                
    Case "F"
                    restrictions.Add(
    "e.id=eg.establishment_id")
                    restrictions.Add(
    "e.id=et.establishment_id")
                    restrictions.Add(
    "et.type_id=t.id")         
                    restrictions.Add(
    "ef.establishment_id=e.id")
                    restrictions.Add(
    "ef.feature_id="&thisResult.Get("ref_id"))
                    restrictions.Add(
    "e.address_street=s.id"
                    SearchUtils.clearSearch
                    SearchUtils.AddSearchElement(
    "EF",thisResult.Get("ref_id"),"establishment e,establishment_type et, establishment_group eg,lut_type t,lut_street s,establishment_feature ef",restrictions,"R")
                    
    Dim ps As pageSearch
                    ps.Initialize(
    "")
                    Main.NavControl.ShowPage(ps.thisPage)     
        
    End Select 
    End Sub

    Private Sub renderHeaderPanel 

        header.Initialize(Me,
    "Nearby",MiscUtils.HexToColor(Main.colour_purple,255),True,True,False,"","",Null,"","")
        thisPage.RootPanel.AddView(header.getPanelView, 
    00, Main.screenWidth, Main.headerBarHeight)

    End Sub


    Private Sub act_groups_Click(item As String)
        
    For Each v As Map In allGroups
            
    If v.Get("full_value")=item Then
                
    Log("match for group, adding "&item)
                DatabaseInterface.nearbyAdd(
    "G",v.Get("id"))
                refreshContent         
                
    Return
            
    End If
         
        
    Next
    End Sub


    Private Sub act_subgroups_Click(item As String)
        
    For Each v As Map In allSubgroups
            
    If v.Get("full_value")=item Then
                
    Log("match for subgroup, adding "&item)
                DatabaseInterface.nearbyAdd(
    "S",v.Get("id"))
                refreshContent         
                
    Return
            
    End If
        
    Next
    End Sub


    Private Sub act_features_Click(item As String)
        
    Dim id As String
        
    id=DBUtils.GetTableValue(Main.SQL,"lut_feature","id","value='"&item&"'")
     
        
    If (IsNumber(id)) Then
            
    ' add
            DatabaseInterface.nearbyAdd("F",id)
            refreshContent 
        
    End If
     

    End Sub


    Sub pnl_groups_Click
        act_groups.Initialize(
    "act_groups","","Cancel","",MiscUtils.MapToList(allGroups,"full_value"))

        act_groups.Show(thisPage.RootPanel)
    End Sub

    Sub pnl_subgroups_Click
        act_subgroups.Initialize(
    "act_subgroups","","Cancel","",MiscUtils.MapToList(allSubgroups,"full_value"))

        act_subgroups.Show(thisPage.RootPanel)
    End Sub

    Sub pnl_features_Click
        act_features.Initialize(
    "act_features","","Cancel","",MiscUtils.MapToList(allFeatures,"value"))

        act_features.Show(thisPage.RootPanel)
    End Sub

    Sub pnlButton_Click(callType As String,refId As String)
        
    ' add this favourite to search history, and open search activity
    End Sub

    Sub pnlButton_LongClick(p As PanelButton)
        
    ' might be one of the top row panels, if so ignore them (can't delete them)
     
        
    If (p=NullThen Return
     

    End Sub

    ' returnas a map, one contains the panel with all the results, other for the panel height
    Private Sub getNearbyPanelMap(thisNearby As MapAs Map
        
    Dim l As Map
        l.Initialize
        
    Dim p As Panel
        p.Initialize(
    "")
     
     
        
    Dim title As String
        title=DatabaseInterface.getNearbyTitle(thisNearby.Get(
    "type"),thisNearby.Get("ref_id"))
        
    Dim pnl_title As Panel
        pnl_title=RenderUtils.renderSectionTitlePanel(title,Main.screenWidth*
    0.7,Main.screenWidth*0.15)
             
        
    Dim btn_settings,btn_search As Button 
     
        btn_settings.Initialize(
    "btn_settings",btn_settings.STYLE_SYSTEM)
        
    Dim bitmapSettings As Bitmap
        bitmapSettings.Initialize(
    File.DirAssets,"btn_delete.png")
        RenderUtils.SetBackgroundImage(btn_settings,bitmapSettings,
    0)
        btn_settings.Tag=thisNearby.Get(
    "id")

        btn_search.Initialize(
    "btn_search",btn_search.STYLE_SYSTEM)
        
    Dim bitmapSearch As Bitmap
        bitmapSearch.Initialize(
    File.DirAssets,"btn_search.png")
            RenderUtils.SetBackgroundImage(btn_search,bitmapSearch,
    0)
        btn_search.Tag=thisNearby
     
        pnl_title.AddView(btn_settings,Main.screenWidth*
    0.7,0,Main.screenWidth*0.15,Main.screenWidth*0.15)
        pnl_title.AddView(btn_search,Main.screenWidth*
    0.85,0,Main.screenWidth*0.15,Main.screenWidth*0.15)

        
    ' put in splitter index
        Dim lbl_splitter1a,lbl_splitter1b,lbl_splitter2a,lbl_splitter2b As Label
        lbl_splitter1a.Initialize(
    "")
        lbl_splitter1a.Color=MiscUtils.HexToColor(
    "#f07fea",70)
        lbl_splitter2a.Initialize(
    "")
        lbl_splitter2a.Color=MiscUtils.HexToColor(
    "#f07fea",70)

        lbl_splitter1b.Initialize(
    "")
        lbl_splitter1b.Color=MiscUtils.HexToColor(
    "#460543",255)
        lbl_splitter2b.Initialize(
    "")
        lbl_splitter2b.Color=MiscUtils.HexToColor(
    "#460543",255)
     
        pnl_title.AddView(lbl_splitter1a,Main.screenWidth*
    0.7,3%x,1dip,(Main.screenWidth*0.15)-6%x
        pnl_title.AddView(lbl_splitter1b,(Main.screenWidth*
    0.7)-1dip,3%x,1dip,(Main.screenWidth*0.15)-6%x
        pnl_title.AddView(lbl_splitter2a,Main.screenWidth*
    0.85,3%x,1dip,(Main.screenWidth*0.15)-6%x
        pnl_title.AddView(lbl_splitter2b,(Main.screenWidth*
    0.85)-1dip,3%x,1dip,(Main.screenWidth*0.15)-6%x)     
     
        p.AddView(pnl_title,
    0,0,100%x,Main.screenWidth*0.15)
        p.AddView(btn_settings,Main.screenWidth*
    0.7,0,Main.screenWidth*0.15,Main.screenWidth*0.15)
        p.AddView(btn_search,Main.screenWidth*
    0.85,0,Main.screenWidth*0.15,Main.screenWidth*0.15)


     
        
    Dim height As Int
        height=Main.screenWidth*
    0.15
     
        
    Dim rowHeight As Int
        rowHeight=Main.screenWidth*
    0.2
     
        
    ' now add in results
        ' now need to be a bit cute here. I want to show closest 3 places, but the closest might be shut, so limit to first 10 only
        ' and that'll do it I reckon

        
    Dim resultsPanelButtons As List
        resultsPanelButtons.Initialize
        resultsPanelButtons=runSearch(thisNearby.Get(
    "type"),thisNearby.Get("ref_id"),Main.nearby_limit)

        
    If (resultsPanelButtons.Size<1Then
                
    Dim lbl_noresults As Label
                lbl_noresults=RenderUtils.renderLabel(
    "Nothing Nearby")         
                lbl_noresults.Color=
    Colors.Black
                p.AddView(lbl_noresults,
    0,height,Main.screenWidth,rowHeight)
                height = height+rowHeight
        
    Else
            
    'Dim total As Int
            'total=resultsPanelButtons.Size
            For Each pb As PanelButton In resultsPanelButtons
                p.AddView(pb.Render,
    0,height,Main.screenWidth,rowHeight)
                height = height+rowHeight
                
    'height=15%x+(total*rowHeight)
             
            
    Next
         
            
    'p.AddView(renderUltimateListView(resultsInfo),0,15%x,100%x,height-15%x)
        End If
         
            l.Put(
    "panel",p) 
            l.Put(
    "height",height)
         
        
    Return l
    End Sub

    ' return panels in 'panel' map, and height in 'height' value
    ' returns LIST OF DATA
    Private Sub runSearch(nearbyType As String,Search_id As String, number As Int) As List 
        
    Dim centreLocation As Location
        centreLocation.Initialize2(Main.currentLocation.Latitude,Main.currentLocation.Longitude)


        
    Dim restrictions As List
        restrictions.Initialize
        
    Dim q As String

        
    Dim tables,search_type As String

        
    ' if establishment_id is set, then use that as the starting lat/lon,
        ' else if traveller_point_id is set, use that
        ' sle Else use current Location
        If (Main.establishment_id<>Null AND IsNumber(Main.establishment_id) AND Main.establishment_id>0Then
            centreLocation=EstablishmentInterface.getEstablishmentLocation(Main.establishment_id)     
        
    Else If (Main.traveller_point_id<>Null  AND IsNumber(Main.traveller_point_id) AND Main.traveller_point_id>0Then
            centreLocation.Initialize2(Main.mapCentre_lat,Main.mapCentre_lon)
        
    Else
            centreLocation=Main.currentLocation
        
    End If

        
    'Log("NEARBY SEARCHING FOR "&nearbyType)

        
    If (nearbyType="G"Then
            restrictions.Add(
    "e.id=eg.establishment_id")
            restrictions.Add(
    "e.id=et.establishment_id")
            restrictions.Add(
    "et.type_id=t.id")         
            restrictions.Add(
    "eg.group_id="&Search_id)
            restrictions.Add(
    "e.address_street=s.id"
            tables=
    "establishment e,establishment_type et, establishment_group eg,lut_type t,lut_street s"
            search_type=
    "EG"

        
    Else If (nearbyType="S"Then
                restrictions.Add(
    "e.id=et.establishment_id")
                restrictions.Add(
    "e.id=eg.establishment_id")
                restrictions.Add(
    "e.id=es.establishment_id")
                restrictions.Add(
    "et.type_id=t.id")         
                restrictions.Add(
    "es.subgroup_id="&Search_id)
                restrictions.Add(
    "e.address_street=s.id"
                tables=
    "establishment e,establishment_type et, establishment_group eg,lut_type t,lut_street s,establishment_subgroup es"
                search_type=
    "ES"
        
    Else If (nearbyType="F"Then
                restrictions.Add(
    "e.id=et.establishment_id")
                restrictions.Add(
    "e.id=eg.establishment_id")
                restrictions.Add(
    "et.type_id=t.id")         
                restrictions.Add(
    "ef.establishment_id=e.id")
                restrictions.Add(
    "ef.feature_id="&Search_id)
                restrictions.Add(
    "e.address_street=s.id")     
                tables=
    "establishment e,establishment_type et, establishment_group eg,lut_type t,lut_street s,establishment_feature ef"
                search_type=
    "EF"
        
    End If
        
    Dim s As Search
        s.Initialize(Me,search_type,Search_id,tables,restrictions,
    "R",centreLocation,number)
        
    'Return s.RowInfos
        Dim result_panels As List
        result_panels=s.getResultPanels 
    'return panelButtons for each result 
        Return result_panels
    End Sub
     
  9. yonson

    yonson Active Member Licensed User

    Hi Erel,

    I think that will work perfectly, thank you, so in my initial code:-

    Code:
    CallSubDelayed(Me,"renderHeaderPanel")  
        CallSubDelayed(Me,
    "refreshContent")
    instead of directly calling the subs. I'll let you know if I run into anymore issues
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice