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,801

Roger Garstang

Well-Known Member
Licensed User
Longtime User
I don't know how this works/is handled internally, but would it be possible to have 1-3 new subs of this that can specify a delay? 1-3 subs is just to match the current available and have a delay parameter. That way it waits the amount of time before trying to call it. I guess this would be similar to how a Service can be called on a timer, but really a sub.

I make use of this in the class I'm working on for views and it actually helps a call in one area- Normally when I call the Event sub for my Next button from the keyboard's Done Action button the keyboard doesn't go down before switching to my next activity. When returning from the 2nd activity it is like the animation was paused and the screen finishes shifting down and looks odd. I even had to add a line in Resume to reset my panel height because the IME_HeightChanged doesn't get called either on returning. With this delayed sub call it actually helps out 1 out of 3 times by delaying enough for the keyboard to finish. If it allowed a minimum delay time parameter I think I could delay it long enough for it to finish the sliding. Time in ms would be fine, no need to delay for hours or days or anything.
 

mjas

Member
Licensed User
Longtime User
Hi Erel,

congratulations for the great job you do here!!!
It's wonderfull how quickly the posted questions are answered by someone.

I'm a newbie with B4A, starting a new project and using your example to create several activities, and switching them bwith "CallSubDelayed".
Everything goes fine, but the big issue is:
Because I'l never close anyone of the activities, only switch from one to another, when I am in the Main activity and press the Back button, the Aplication does'n finish, only finishes the Main activity and goes to the next activity in the stack (the last one I opened) and pressing the Back button again do the same (close this one and goes for the next one) and only after pressing the Back button the same number of times like the activities i have finally it goes out for the Home screen.
Even if I trap the Back button event in Main activity and use "ExitApplication()" it does the same like before but throws an error in Log window:
"Got RemoteException sending setActive(false) notification to pid 557 uid 10036" because this pid is the Main activity just closed.
Any idea how this can be fixed?

BTW: I tried to do this in Manisfest.xml:
SetActivityAttribute(name of activity , android:noHistory, "true")
but this doesn't fix this behaviour, only resets each view to the initial state each time it is activated.

Thank you for any help.:sign0098:
 

Antti Mauranen

Member
Licensed User
Longtime User
Is it possible to make the message wait queue longer? When the main activity is paused and my service calls CallSubDelayed, it seems that I can add only 21 messages to wait queue and then I get warning that the queue is full. See below.

** Service (bleservice) Start **
sending message to waiting queue (CallSubDelayed - Add_Error_Line)
Ignoring event (too many queued events: CallSubDelayed - Add_Error_Line)
** Service (bleservice) Start **
sending message to waiting queue (CallSubDelayed - Add_Error_Line)
Ignoring event (too many queued events: CallSubDelayed - Add_Error_Line)
running waiting messages (21)
** Activity (main) Resume **
** Service (bleservice) Start **
 

Antti Mauranen

Member
Licensed User
Longtime User
I tried to collect messages into a list in the service and then send them in one burst when the activity gets out of pause state. That method loses messages also.

Then I tried to send the messages little slower (had 10 ms delay between messages every message), still loses messages.

Maybe I start to read the messages in the activity create, after the activity gets out of pause state (anyway, I have access to BleService.waitList). I will try that.

Lots of learn about this beast :).
 

Antti Mauranen

Member
Licensed User
Longtime User
Hello Erel,

yes, I will read the message in the service and do some actions in the service. But I also want to write an indication (one line per message) about every message into CustomListView in my main action. So my problem is how do I insert a new row into CLV. Now a) when main activity is active, I use CallSubDelayed and b) when main activity is in the background, I collect the (future) traffic between service and CLV into a list and when the main activity comes active, I read that list from Activity_Resume.

This seems to function. I do not know if that is a good way.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I collect the (future) traffic between service and CLV into a list and when the main activity comes active, I read that list from Activity_Resume.
This is a good solution. You should however understand that the process can be killed while it is in the background. If it is a problem than you need to persist this list (use KeyValueStore).
 

Ratna Fang

Member
Licensed User
Longtime User
hi erel,

i simply use callsubdelayed in this code, in the main activity to call another activity (Menu2 activity).
B4X:
Sub ImageView1_Click
    CallSubDelayed(Menu2,"Activity_Create")
End Sub


no error found when compiling, but in the device, i got an error message "activity_create does not match expected signature".

here is the code of activity_create in Menu2
B4X:
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Menu1")

    List1.Initialize("ListView1")

    List1.AddTwoLines ("1","one")
    List1.AddTwoLines ("2","two")
    List1.AddTwoLines ("3", "three")
End Sub

is there anything wrong? or i should use startactivity(Menu2) for this case?
 

Attachments

  • Screenshot_2013-12-21-11-28-56.png
    Screenshot_2013-12-21-11-28-56.png
    45.4 KB · Views: 1,487
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
You are missing the sub parameter in your call. You should use CallSubDelayed2 instead. However I don't think that you are using this method correctly.
If you just want to start an activity then you should call StartActivity. CallSubDelayed allows you to directly call a sub (and pass parameters) in another activity or service.
 

andrewj

Active Member
Licensed User
Longtime User
Hi,
Is there any way to pause after/at the end of processing the delayed sub? I'm loading images into a display which works well, in that now my screen shows the activity with the images empty, and then fills them through a series of delayed calls. However the screen still only shows the images when all delayed subs have completed, whereas I'd like it to refresh after each one.
Thanks
Andrew
 

andrewj

Active Member
Licensed User
Longtime User
From the file system. I am using the standard Bitmap.InitializeSample() followed by ImageView.Bitmap= load logic, but in a delayed sub rather than in line. I'd just like to force a refresh of the display after each load.
Thanks
Andrew
 
Top