B4A Class [B4X] CallSubPlus - CallSub with explicit delay

Discussion in 'Additional libraries, classes and official updates' started by Erel, Nov 30, 2015.

  1. Erel

    Erel Administrator Staff Member Licensed User

    In many cases you need to run a task in a few seconds. The solution for such cases is to create a timer and then execute the task in the timer's Tick event. This is good for a single task. However if you need to run multiple tasks then it becomes difficult to maintain.

    This small class makes it much simpler. You just need to call CallSubPlus with the target sub and the delay.
    Internally it uses a (one shot) timer together with CallSubDelayed or CallSub.

    There are four methods:
    CallSubPlus, CallSubPlus2, CallSubDelayedPlus and CallSubDelayedPlus2.
    CallSubPlus and CallSubDelayedPlus should be used with subs with no parameters. The others should be used with subs with a single parameter. The parameter type must be an array of objects.

    For example:
    Code:
    Sub Activity_Click
       
    Log($"Click: $Time{DateTime.Now}"$)
       Starter.csu.CallSubPlus(Me, 
    "Sub_1"2000'run in two seconds
       Starter.csu.CallSubPlus2(Me, "Sub_2"1000Array("Value 1""Value 2")) 'run in one second
    End Sub

    Sub Sub_1
       
    Log($"Sub_1: $Time{DateTime.Now}"$)
    End Sub

    Sub Sub_2(args() As Object)
       
    Log($"Sub_2: $Time{DateTime.Now}, arg(0)=${args(0)}"$)
    End Sub
    csu is declared in the Starter service:
    Code:
    Sub Process_Globals
       
    Public csu As CallSubUtils
    End Sub

    Sub Service_Create
       csu.Initialize
    End Sub
    Notes

    - Make sure to include an underscore in the target subs names if you intend to compile with obfuscation.
    - CallSubDelayed will start the target service or activity if needed (see this tutorial for more information: http://www.b4x.com/android/forum/th...etween-activities-and-services.18691/#content).
    - You can safely call multiple subs. This can be useful to break a long task into smaller tasks.
    - This class can also be used with B4i and B4J.
    - Add this class to your project with Project - Add Existing Module.
     

    Attached Files:

  2. LucaMs

    LucaMs Expert Licensed User

    As you know, you can get a warning if you declare a Timer elsewhere than in Process_Globals.
    Here you declare a Timer in a sub; I suppose that the warning is related to activities only, right?


    [P.S. Nice: you use timers as keys of a map :D]
     
    Erel likes this.
  3. Erel

    Erel Administrator Staff Member Licensed User

    That's true. If you declare a global timer then it should be declared in Process_Globals instead of Globals. Otherwise, new timers will be created each time that the activity is recreated.
     
    lemonisdead and LucaMs like this.
  4. LucaMs

    LucaMs Expert Licensed User

    Thank you, Erel
     
  5. EvgenyB4A

    EvgenyB4A Active Member Licensed User

    Hi
    How to pass the parameter in ...Plus2 if the parameter is integer and not string?
     
  6. LucaMs

    LucaMs Expert Licensed User

    I think Arg array should be changed to single object (in each part of module).
     

    Attached Files:

    lemonisdead and EvgenyB4A like this.
  7. DonManfred

    DonManfred Expert Licensed User

    Code:
    Starter.csu.CallSubPlus2(Me, "Sub_2"1000Array("Value 1"0815)) 'run in one second
    CallSubPlus2 is sending an Array of Object. It i not an Array as String.
     
    Erel and Filippo like this.
  8. LucaMs

    LucaMs Expert Licensed User

    If the signature of "Sub_2" is (as I suppose @EvgenyB4A wants):

    Sub Sub_2(Value As Int)

    your code cannot work.
     
  9. Erel

    Erel Administrator Staff Member Licensed User

    I don't understand the problem.

    @DonManfred code should work.
     
  10. LucaMs

    LucaMs Expert Licensed User


    @EvgenyB4A wants to call a sub like:
    Code:
    Sub2(Param As Int)
    DonManfred's code is not the solution, it can call this sub:
    Code:
    Sub2(Param As Object)
        
    Dim Data() As Object = Param
        
    Log(Data(0) & tab & Data(1))
    End Sub

    The solution could be in my post #6.
     
    Last edited: Apr 26, 2016
  11. Filippo

    Filippo Expert Licensed User

    Thank Erel! :)
    The class is so good, it could have been from me.:D
     
    Erel and JakeBullet70 like this.
  12. LucaMs

    LucaMs Expert Licensed User


    Better explanation:

    If you want to use this class to call EXISTING routines, which must receive a single parameter (Int in this case),
    you should use "my solution" (the change I have suggested) #6.
     
    Last edited: Apr 26, 2016
  13. Erel

    Erel Administrator Staff Member Licensed User

    You are correct.
     
  14. LucaMs

    LucaMs Expert Licensed User


    Yes, I know :D <--- upload_2016-4-27_17-7-23.png
     
  15. Filippo

    Filippo Expert Licensed User

    Hi,

    Here a small change.
    I've added this code "If Not(IsPaused(rdd.Module)) Then " so as to avoid that if the activity is not active, no error occurs.

    Code:
    'Class module
    Sub Class_Globals
        
    Private RunDelayed As Map
        
    Type RunDelayedData (Module As Object, SubName As String, Arg() As Object, Delayed As Boolean)
    End Sub

    Public Sub Initialize

    End Sub

    'Similar to CallSubDelayed. This method allows you to set the delay (in milliseconds).
    'Note that the sub name must include an underscore if compiled with obfuscation enabled.
    Public Sub CallSubDelayedPlus(Module As Object, SubName As String, Delay As Int)
        CallSubDelayedPlus2(Module, SubName, Delay, 
    Null
    End Sub

    'Similar to CallSubDelayed. This method allows you to set the delay (in milliseconds).
    'Note that the sub name must include an underscore if compiled with obfuscation enabled.
    'The target sub should have one parameter with a type of Object().
    Public Sub CallSubDelayedPlus2(Module As Object, SubName As String, Delay As Int, Arg() As Object)
        PlusImpl(Module, SubName, Delay, Arg, 
    True)
    End Sub

    'Similar to CallSub. This method allows you to set the delay (in milliseconds).
    'Note that the sub name must include an underscore if compiled with obfuscation enabled.
    Public Sub CallSubPlus(Module As Object, SubName As String, Delay As Int)
        CallSubPlus2(Module, SubName, Delay, 
    Null
    End Sub

    'Similar to CallSub. This method allows you to set the delay (in milliseconds).
    'Note that the sub name must include an underscore if compiled with obfuscation enabled.
    'The target sub should have one parameter with a type of Object().
    Public Sub CallSubPlus2(Module As Object, SubName As String, Delay As Int, Arg() As Object)
        PlusImpl(Module, SubName, Delay, Arg, 
    False)
    End Sub

    Private Sub PlusImpl(Module As Object, SubName As String, Delay As Int, Arg() As Object, delayed As Boolean)
        
    If RunDelayed.IsInitialized = False Then RunDelayed.Initialize
        
    Dim tmr As Timer
        tmr.Initialize(
    "tmr", Delay)
        
    Dim rdd As RunDelayedData
        rdd.Module = Module
        rdd.SubName = SubName
        rdd.Arg = Arg
        rdd.delayed = delayed
        RunDelayed.Put(tmr, rdd)
        tmr.Enabled = 
    True
    End Sub

    Private Sub tmr_Tick
       
    Dim t As Timer = Sender
       
    Dim rdd As RunDelayedData = RunDelayed.Get(t)
       
    If Not(IsPaused(rdd.Module)) Then
         RunDelayed.Remove(t)
         t.Enabled = 
    False
         
    If rdd.Delayed Then
           
    If rdd.Arg = Null Then
             CallSubDelayed(rdd.Module, rdd.SubName)
           
    Else
             CallSubDelayed2(rdd.Module, rdd.SubName, rdd.Arg)
           
    End If
         
    Else
           
    If rdd.Arg = Null Then
             
    CallSub(rdd.Module, rdd.SubName)
           
    Else
             
    CallSub2(rdd.Module, rdd.SubName, rdd.Arg)
           
    End If
         
    End If     
       
    End If
    End Sub
     
    Last edited: May 1, 2016
  16. LucaMs

    LucaMs Expert Licensed User

    If the Activity is not active, you might want to start it, using CallSubDelayed. I think you should add that check only to CallSub calls.
     
    Last edited: May 1, 2016
  17. Filippo

    Filippo Expert Licensed User

    Did you test it?
    I have tested it so and it works. ;)
     
  18. LucaMs

    LucaMs Expert Licensed User

    It should work for sure but...

    Beh scrivo in italiano, poi FORSE traduco una sintesi, mi riesce meglio :)

    Funziona di sicuro, la tua modifica; però, metti che tu, dall'activity Main, vuoi chiamare una routine dell'activity actTwo, devi chiamarla tramite CallSubDelayed, in modo che actTwo, che non è attiva, venga caricata.
    Se usi la CallSubDelayed direttamente, tutto funziona. Se usi questa classe, dato che la actTwo non è attiva, la chiamata sarà ignorata, per via del

    Sorry, in short:
    It should work for sure but...
    If you want, from Main, call a sub of another activitiy (actTwo), you can use CallSubDelayed and the call will have success (actTwo will be started).
    If you add your change to this class, the call will be ignored, because actTwo is not in foreground.
     
  19. LucaMs

    LucaMs Expert Licensed User

    Anyway, I think Erel can change his class, or not? I'm almost sure, if he wants :D
     
  20. LucaMs

    LucaMs Expert Licensed User

    I think the class should be so, with your change, @Filippo, and mine.
     

    Attached Files:

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