Android Tutorial Using CallSubDelayed to interact between activities and services

Until Basic4android v2.00 the way to pass information between activities and services was through process global variables.

CallSubDelayed makes it much simpler. It allows you to call a sub in a different service or activity. If the target module is not active, then it will be started automatically. When the target module is ready, the sub will be called.

CallSubDelayed doesn't immediately call the target sub. It sends a message to the message queue. The internal framework manages this message and passes it to the target module when it is ready.

CallSubDelayed can also be used to call subs in the current module. It is useful in cases where you want to run some code "right after" the execution of some UI event.

Rules
- If the target module is already running then the sub will be called.
- If the target module is a service and it is not already running then it will first be started (Service_Create and Service_Start will first be executed).
The sub will be called after Service_Start.
- If the target module is an activity:
- If the application is visible (one of its activities is visible) then the target module will be started if needed and the sub will be called.
- If the application is in the background (can happen when a service calls an activity) then the message will be stored in a special message queue. In this case the sub will be called when the target activity becomes visible. The sub will be called before Activity_Resume.​

Just to make it clear, you do not need to call StartActivity or StartService when you use CallSubDelayed.
CallSubDelayed is the recommended method for interaction between activities and services.

Note that you cannot use CallSubDelayed (or CallSub) with code modules.
CallSubDelayed can be used with class instances. However the containing module will not be started if it is not already running.

An improved version of the two activities example:

B4X:
'Main Activity
Sub Process_Globals

End Sub

Sub Globals
   Dim Label1 As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
End Sub

Sub Button1_Click
   'This call will bring Activity2 to front and will then execute ShowList
   CallSubDelayed2(Activity2, "ShowList", "This is the title")
End Sub
Sub GetResult(Result As String)
   Label1.Text = "You have chosen: " & Result
End Sub

'*****************************
'Activity2

Sub Process_Globals
   
End Sub

Sub Globals
   Dim ListView1 As ListView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("2")
   For i = 1 To 100
      ListView1.AddSingleLine("Item #" & i)
   Next
End Sub

Sub ShowList(Title As String)
   Activity.Title = Title
End Sub

Sub ListView1_ItemClick (Position As Int, Value As Object)
   'this call will bring Main to front and call GetResult
   CallSubDelayed2(Main, "GetResult", Value)
End Sub
 

Attachments

  • TwoActivities.zip
    7.2 KB · Views: 4,610

CapReed

Member
Licensed User
Longtime User
That's All Right!

B4X:
CallSubDelayed2("","VolumeUp",ModoAntiguo)

B4X:
Sub VolumeUp(tipoVolume As Int)
        Dim P As Phone
        P.SetRingerMode(tipoVolume) ' Vuelve a poner el telefono en modo normal
        DoEvents
        DoEvents
End Sub

The only question I have is that I have reason to put two DoEvents. I've already spent on other occasions, with one nothing happens, but if I put two in a row if it occurs than expected.

Thank you Erel.
 

CidTek

Active Member
Licensed User
Longtime User
I can see how CallSubDelayed can be very useful for me but how do I reference the calling Activity in the Service I'm calling?

Services don't seem to like when I declare an Activity object and passing in that value as a parameter.
 

lemonisdead

Well-Known Member
Licensed User
Longtime User
how do I reference the calling Activity in the Service I'm calling
You'll find the information using StartingIntent available from the Service_Start sub. It has some extras

B4X:
Sub Service_Start (StartingIntent As Intent)
 

CidTek

Active Member
Licensed User
Longtime User
You'll find the information using StartingIntent available from the Service_Start sub. It has some extras

B4X:
Sub Service_Start (StartingIntent As Intent)

Thanks I'll check this out. I also figured out a workaround by passing in a string as a CallSubDelayed parameter referencing an activity name and using a Select statement to handle the different things I would do based on the calling activity. So rather than name the sub as a parameter I just use hardwired logic in my Select statement since there is only one sub per Activity I need to deal with.

Naturally if there were more than one sub within an Avtivity expecting to be called then I would have to use the Sub name as the parameter and use StatingIntent to know the calling Activity. It's good to have options.
 

mkvidyashankar

Active Member
Licensed User
Longtime User
First time I am using Services module
I am trying pass one id to start a sub in activity module, but it is not working

B4X:
Sub Service_Destroy
StartServiceAt("", DateTime.Now + 10000 * DateTime.TicksPerSecond, True)
CallSub2(Main,"getquotes",quoteid)

End Sub

where am i am doing error?
 

mkvidyashankar

Active Member
Licensed User
Longtime User
no the activity is not visible at that time.
can i open the application by clicking on the notification and passing some parameters.
 

pesquera

Active Member
Licensed User
Longtime User
I don't know if is possible to do.. but, I'm trying :)
I want to pass a panel as parameter between two activities, so I don't need to duplicate all the views from the panel into the new activity
I want to "clone" the panel (including all its child views)
B4X:
'on the base Activity
CallSubDelayed2(NewActivity,"TakePanel",pnl_tmp)
'on the New Activity
Sub TakePanel (p_Panel As Panel)
    pnl_Header.Initialize("")
    pnl_Header = p_Panel
    Activity.AddView(pnl_Header,p_Panel.Left,p_Panel.Height,p_Panel.Width,p_Panel.Height)
End Sub
The error is: The specified child already has a parent.. You must call removeView() on the child's parent first..
 

LucaMs

Expert
Licensed User
Longtime User
I think you should remove the panel from you base Activity

B4X:
'on the base Activity
pnl_tmp.RemoveView
CallSubDelayed2(NewActivity,"TakePanel",pnl_tmp)

'on the New Activity
Sub TakePanel (p_Panel As Panel)
    pnl_Header.Initialize("") ' <------
    pnl_Header = p_Panel
    Activity.AddView(pnl_Header,p_Panel.Left,p_Panel.Height,p_Panel.Width,p_Panel.Height)
End Sub

and probably you don't need the initialization of the panel.
 

pesquera

Active Member
Licensed User
Longtime User
Thanks LucasMs.. that was what I've needed.. because I don't need to raise events from these views, but just show them
It's like a "move" of the view from an activity to another, instead of "clone" it
Now, I'm working on how to restore the panel again on the base activity when returning to it (left,top,width,height,etc)
thanks again
 

Derek Jee

Active Member
Licensed User
Longtime User
Hi there

If I wanted to open an activity but only pass a variable, what would I put in the sub? I do not know if the activity is already created and sitting behind my current activity or if it is going to open for the first time. Can I do this CallSubDelayed2(Activity, "", MyVariable) and will it run the activity create if it needs to?

Thanks,


Derek.
 

Derek Jee

Active Member
Licensed User
Longtime User
Yes. The activity will be created if needed. Note that the activity will become visible.
If I don't reference a sub, how do I pick up the variable value?
 

Derek Jee

Active Member
Licensed User
Longtime User
Ok, cool.. Will it call the custom sub before or after activity_resume?
 

Derek Jee

Active Member
Licensed User
Longtime User
Perfect, thank you..
 
Top