Android Tutorial [TUTORIAL] Inter-app Communication with Intents

thedesolatesoul

Expert
Licensed User
DISCLAIMER:
This tutorial is written without testing code. There may be bugs, but try to understand the process, not copy the code.


An intent is a way for different applications to communicate at run time. It can be used to pass messages between activities and make them do actions. Using it with startActivity targets the intent to an activity. Broadcast intents are broadcast to all activities, and any applications with an intent filter can listen for it and perform an action.

Explicit Intents: The target component (service or activity) is known. (For e.g. an intent to the browser)

Implicit Intents: The system will determine which activity is best to launch this intent. (For e.g. Share intents)

More information on intents:
Intents and Intent Filters | Android Developers


In this example we have two apps.
- The Provider, that contains the data we need
- The Requester, that will request the data via Intent


Step 1: Setup the Provider to be able to receive intents
This can be done in two ways:
1. Explicitly setting the component, and making it visible by setting exported=true
2. Defining your own intent filter

We will do the second way since it is more structured.

We set up an intent filter to trigger our activity in the Provider.
In the manifest editor add the following, for the Activity you want to trigger.
You can also use a service (use AddServiceText instead)

B4X:
AddActivityText(Main, <intent-filter>
   <action android:name="com.maximussoft.myprovider.REQUEST" />
   <category android:name="android.intent.category.DEFAULT" />
</intent-filter>)
Step 2: Code the Provider to return the data
When using an Activity,

B4X:
Sub Activity_Resume
 Dim in as Intent
 in = Activity.GetStartingIntent
 If in <> null Then
    If In.HasExtra("GetSettings") Then
       SendSettings(in)
       Activity.Finish
       Return
    End If
 End If
End Sub
...so far so good...
however do you see the problem here?
no? come on think harder
How does the Providor know which app to send the data back to? The Intent by default has no information about the Calling package.

Again, there are two (simpler) ways to deal with this
1. Add information about the Requestor in the intent extras so we can call it back
2. Use StartActivityForResult. This allows you to get the calling package name, but infact you do not even need it. You can just return the result as an intent back to the Requestor Activity.

However, this is the point where you pause and think about what your apps need to do. Are they working silently in the background? Or do they show a dialog at some point?

For now, we go with the first option, and assume that the Requestor sends us an intent action in the extras.
So in SendSettings we do,
B4X:
Sub SendSettings(in as Intent)
   Dim returnIntent as Intent
   returnIntent.Initialize(in.GetExtra("Callback"),"")
   returnIntent.AddCategory("android.intent.category.DEFAULT")
   returnIntent.PutExtra("Settings",someGlobalSettings)
   returnIntent.PutExtra("Successful","True")
   StartActivity(returnIntent) 'Or StartService(returnIntent)
End Sub
Make note of how the return intent is initialized with the Callback field from the original recevied intent.

3. Set up the Requester
As you may have noticed, the Requester needs to be set up in the same way except for some minor differences. First you set up the manifest, with a different intent filter:
B4X:
 AddActivityText(Main, <intent-filter>
   <action android:name="com.maximussoft.myrequestor.CALLBACK" />
   <category android:name="android.intent.category.DEFAULT" />
</intent-filter>)
4. Code the Requester to request the data/settings
This can be anywhere in your code, but if its settings you want to request, it will probably be in activity create or resume. The thing to note here is how we initialiaze the intent with the ACTION of the PROVIDER. And we send the CallBack i.e. the intent of the REQUESTER as the CALLBACK so the provider can send us back the data.

B4X:
Sub RequestSettings
   Dim in as Intent
   in.Initialize("com.maximussoft.myprovider.REQUEST","")
   in.AddCategory("android.intent.category.DEFAULT")
   in.PutExtra("Callback","com.maximussoft.myrequestor.CALLBACK")
   in.PutExtra("GetSettings","True")
   StartActivity(in) 'Or StartService(returnIntent)
End Sub
5. Code the Requester to accept the data
Again this code is very similar to the Provider except that it gets the data
B4X:
 Sub Activity_Resume
 Dim in as Intent
 in = Activity.GetStartingIntent
 If in <> null Then
    If In.HasExtra("Settings") Then
         AcceptSettings 'Do something here with your settings
    End If
 End If
End Sub
Points to note:
While I dont consider intents to be very advanced, it can be quite overwhelming for newbies, so do your research.

There are many ways to do things and combinations of services/activities can make things complicated.

You can add permissions on your intent filters, this way Plugin apps need to have your permission in their manifest. This helps in *some* access control, mainly you can list all apps that can access your app/settings/data.
To see this in operation you can look at my apps that plugin to each other Cloudpipes and PhoneBackup.
If I get time I may write a sample otherwise not.
 

RiverRaid

Active Member
Licensed User
Thank you very much...

... for this Tutorial!

I'm experimenting right now with this and I love it!! :sign0162:
I'm thinking of Add-Ons to my App (Daydream, Live Wallpaper,..) and all now possible :)
 

nosaj66au

Member
Licensed User
Great explanation. I would like to understand if this method would suite my application it seems to have possibilities. I would like to setup a what would be best described an a local server and clients application running on a single device (at this stage).Each client would be a separate app but have access to the same data on the server service, but each client would uses the data in different way i.e. reporting, RT displaying, trending ,logging etc. Using this method would it be practical to send a data model of say 100 elements every 1 sec or should I just send 1 element when it changes. (a single larges exchange vs several small exchanges in the same time frame). I guess I'm looking to creating an Android based SOA bus.
Maybe I'm just asking to much!!!!!?????
 

thedesolatesoul

Expert
Licensed User
Great explanation. I would like to understand if this method would suite my application it seems to have possibilities. I would like to setup a what would be best described an a local server and clients application running on a single device (at this stage).Each client would be a separate app but have access to the same data on the server service, but each client would uses the data in different way i.e. reporting, RT displaying, trending ,logging etc. Using this method would it be practical to send a data model of say 100 elements every 1 sec or should I just send 1 element when it changes. (a single larges exchange vs several small exchanges in the same time frame). I guess I'm looking to creating an Android based SOA bus.
Maybe I'm just asking to much!!!!!?????
Its hard to say. Using Intents like this may have a fair bit of overhead and a little inflexible. It might still work for you.
The usual Android method for this would be to use Content Providers, but I dont know how to work with those yet. You can use a shared storage location to share the data?
Maybe you can go with Erel's suggestion.
 

Rick Harris

Well-Known Member
Licensed User
Nice idea Erol! It indeed sounds much more simple. Do you have any example code to make two apps exchange data?
 

Rick Harris

Well-Known Member
Licensed User
I found another solution, i.e. by letting App1 store a parameter in a file in the dirrootexternal directory, then start App2 using an intent after which App2 reads the contents of this (shared) file. It works like a dream!
 

cncncn

Member
Licensed User
hi,

I have got this error:
"An error has occurred in sub:main_sendsettings(java line 341)
android.content.ActivityNotFoundException:No Activity found to handle Intent { act=null cat =[android.intent.category.DEFAULT]flg=0x20000(has extras)}"

I have two applications.
The A application that opens a PDF with Adobe Reader and still running in the background.
Applying B prevents A when B is create.
Do I miss something?

thank you for your future response
 

thedesolatesoul

Expert
Licensed User
hi,

I have got this error:
"An error has occurred in sub:main_sendsettings(java line 341)
android.content.ActivityNotFoundException:No Activity found to handle Intent { act=null cat =[android.intent.category.DEFAULT]flg=0x20000(has extras)}"

I have two applications.
The A application that opens a PDF with Adobe Reader and still running in the background.
Applying B prevents A when B is create.
Do I miss something?

thank you for your future response
Can you post the code for sending the intent?
Also what you defined the action/activity in the manifest of B?
 

cncncn

Member
Licensed User
B4X:
Version=2.71
IconFile=
NumberOfModules=3
Module1=widget
ModuleVisible1=1
Module2=bibli
ModuleVisible2=1
Module3=SMBService
ModuleVisible3=1
Package=b4a.example
DoNotOverwriteManifest=True
ManifestCode='This code will be applied to the manifest file during compilation.~\n~'You do not need to modify it in most cases.~\n~'See this link for for more information: http://www.basic4ppc.com/forum/showthread.php?p=78136~\n~AddManifestText(~\n~<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="14"/>~\n~<supports-screens android:largeScreens="true" ~\n~    android:normalScreens="true" ~\n~    android:smallScreens="true" ~\n~    android:anyDensity="true"/>)~\n~SetApplicationAttribute(android:icon, "@drawable/icon")~\n~SetApplicationAttribute(android:label, "$LABEL$")~\n~'End of default text.~\n~AddActivityText(Main,<intent-filter>~\n~<action android:name="com.maximussoft.myprovider.REQUEST"/>~\n~<category android:name="android.intent.category.DEFAULT"/>~\n~</intent.filter>)
UserTypesHint=
NumberOfFiles=1
File1=main.bal
NumberOfLibraries=2
Library1=core
Library2=smb
@EndOfDesignText@
#Region  Project Attributes
    #ApplicationLabel: Modification
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region
'Version 1.0
'dernière mise à jour faite le 28.04.2014
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim fichier As Int '0=Plan de Q.F. 1=DT 2=N
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
   
   
    Activity.LoadLayout("main")
    Activity.SendToBack
'    fichier=1
'    If fichier =1 Then
'   
'        SMBService.Src        = "smb://192.168.1.60/GPAONGV/DOSSIERS/NGTS2222/CLANGDT012/"
'        SMBService.Fil        = "jeton.txt"
'        SMBService.Action    = 400
'       
'       
'    End If
'
'    StartService(SMBService)
'    If SMBService.DoneSuccessfully Then
'        ToastMessageShow("modification impossible",True)
'           
'    Else
'        ToastMessageShow("modification possible",True)
'    End If
'   
   
   

End Sub

Sub Activity_Resume
    Dim In As Intent
    In=Activity.GetStartingIntent
    If In <> Null Then
        Log("envoie 1")
        If In.HasExtra("GetSettings") Then
            SendSettings(In)
            Log("envoie 2")
            Return
        End If
    End If   
   
    Activity.Finish
   
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
'fonction delay
Sub Delay(nMilliSecond As Long)As Boolean
Dim nBeginTime As Long
Dim nEndTime As Long
nEndTime = DateTime.Now + nMilliSecond
nBeginTime = DateTime.Now
Do While nBeginTime < nEndTime
nBeginTime = DateTime.Now
'Log(nBeginTime)
If nEndTime < nBeginTime Then
Return True
End If
DoEvents
Loop
End Sub

Sub SendSettings(In As Intent)
    Dim returnIntent As Intent
    returnIntent.Initialize(In.GetExtra("CallBack"),"")
    returnIntent.AddCategory("android.intent.category.DEFAULT")
    returnIntent.PutExtra("Settings","true")
    returnIntent.PutExtra("Successful","true")
    StartService(returnIntent)

End Sub



And yes i add this code in my manifest of B :
"
AddActivityText(Main, <intent-filter>
<action android:name="com.maximussoft.myprovider.REQUEST" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>)"
 

incendio

Well-Known Member
Licensed User
How to set Provider to point to a service?

I made this code on manifest text
B4X:
AddServiceText (myService, <intent-filter>
   <action android:name="com.myAppthatHasService.REQUEST" />
   <category android:name="android.intent.category.DEFAULT" />
</intent-filter>)
Is that the right manifest?

In your example above, codes to respond from Requester, put below Sub Activity_Resume, where to put respond codes if it is point to Service?

Thanks.
 

tdocs2

Well-Known Member
Licensed User
I found another solution, i.e. by letting App1 store a parameter in a file in the dirrootexternal directory, then start App2 using an intent after which App2 reads the contents of this (shared) file. It works like a dream!
Hello, Rick.

Sounds clever.

Can you post your code? Requester: Manifest code and sub to issue intent. Provider: Manifest code and sub where you receive the intent and call sub that reads external file?

Thank you in advance.

Sandy
 

tdocs2

Well-Known Member
Licensed User
Greetings, all.
Thank you in advance for your help.

I have no experience doing this. Obviously, I am missing a few building blocks... I have created two apps: IntentRequester and IntentProvider. This is as far as I got… IntentRequester sends the intent to IntentProvider and it works. The IntentProvider app is “killed” and restarted. Why is
StartActivity in IntentRequester killing the activity and not just bringing it to the front?

Log
B4X:
Killing previous instance (main).
Code in IntentRequester
B4X:
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("1")
    Activity.Title="IntentRequester"


End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
Sub Button1_Click
    Dim Intent1 As Intent
    Intent1.Initialize("swi.IntentProvider.REQUEST", "")
    Intent1.PutExtra("B4A","")
    'Intent1.SetComponent("swi.IntentProvider/.Main")
    StartActivity(Intent1)
 
End Sub
Code in IntentProvider
B4X:
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("1")
    Activity.Title="IntentProvider"
    Label1.Text=DateTime.Time(DateTime.now)

End Sub

Sub Activity_Resume
    Dim In As Intent
     In = Activity.GetStartingIntent
     If In <> Null Then
       If In.HasExtra("B4A") Then
        Log("GetStartingIntent")
        Dim Notification1 As Notification
        Notification1.Initialize
        Notification1.Icon="icon"
        'Notification1.Insistent=True
        Notification1.SetInfo("IntentProvider", "Intent received", "")
        Notification1.Notify(1)
      End If
     End If

End Sub
Best regards.

Sandy
 
Last edited:

Rick Harris

Well-Known Member
Licensed User
All you have to do is add the following code to App-1 to save a parameter:
B4X:
'Add this to your App number-1
    Dim Text1 As String
    Dim TW As TextWriter
    Text1 = "Hello"
    TW.Initialize(File.OpenOutput(File.DirRootExternal, "data1.ini", False))
    TW.Write(Text1)
    TW.Close
and read it from within App-2 with:
B4X:
            'Add this to your App number-2
    Dim Text1 As String
    Text1 = File.ReadString(File.DirRootExternal, "data1.ini")
    ToastMessageShow(Text1,True)
I assume you are aware that both apps cannot be active at the same time because Android has no multi-tasking and one app will go into pause state as soon as the other app is activated. You could create a service that holds a timer which controls which app should be active (e.g. flip flop every minute). Or you could write code that automatically reactivates your other app as soon as the active app is paused (and vise versa).
 
Last edited:

tdocs2

Well-Known Member
Licensed User
Thank you, Rick.

Simple and effective solution. I did manage to pass data directly with Intents. Powerful Tool!

Best regards.

Sandy
 
Top