B4A Library FirebaseNotifications - Push messages / Firebase Cloud Messaging (FCM)

Status
Not open for further replies.
Updated tutorial: https://www.b4x.com/android/forum/threads/b4x-firebase-push-notifications-2023.148715/


Clarification: The nice thing about FCM is that your app doesn't need to run in order to receive messages. The FirebaseMessaging receiver will be started by the OS when a new message arrives.
It is not a real issue, but if the user closed your app with a "force close" from the device settings then messages will not arrive, until the app is started again.
On some misbehaving devices, killing the app with a swipe will also cause messages not to arrive, until the app is started again.

Firebase Cloud Messaging service is a layer above Google Cloud Messaging service.
It makes it simple to add support for push messages.

Sending messages is done with a simple HTTP request. It is also possible to send message from the Firebase console, though it is not very useful and is actually more complicated than using the REST api.

1. The first step is to follow the Firebase integration tutorial:
https://www.b4x.com/android/forum/threads/integrating-firebase-services.67692/

Make sure to add the Notifications snippet.
You should also reference FirebaseAnalytics

2. Add a Receiver named FirebaseMessaging to your app (must be this name):
B4X:
Sub Process_Globals
    Private fm As FirebaseMessaging
End Sub

Private Sub Receiver_Receive (FirstTime As Boolean, StartingIntent As Intent)
    If FirstTime Then
        fm.Initialize("fm")
    End If
    fm.HandleIntent(StartingIntent)
End Sub

Public Sub SubscribeToTopics
    fm.SubscribeToTopic("general")
End Sub

Sub fm_MessageArrived (Message As RemoteMessage)
    Log("Message arrived")
    Log($"Message data: ${Message.GetData}"$)
    Dim n2 As Notification
    n2.Initialize2(n2.IMPORTANCE_DEFAULT)
    n2.Icon = "icon"
    n2.SetInfo(Message.GetData.Get("title"), Message.GetData.Get("body"), Main)
    n2.Notify(1)  
End Sub

Sub fm_TokenRefresh (Token As String)
    Log("TokenRefresh: " & Token)
End Sub

fm_MessageArrived will be raised whenever a message is received. In this case we show a notification. You can do whatever you need.

We call SubscribeToTopics from the starter service to make sure that the app will be subscribed when it starts:
B4X:
'Starter service
Sub Process_Globals

End Sub

Sub Service_Create
   CallSubDelayed(FirebaseMessaging, "SubscribeToTopics")
End Sub

Now we can send messages to a "topic" and all the subscribed devices will receive it.

See the code in the attached B4J tool. Note that the API_KEY should be set in the B4J code. It shouldn't be distributed in your app.

API_KEY is the server key from:

SS-2017-04-07_08.10.47.png


A simple non-ui B4J program is attached.

Note that messages sent from Firebase Console will not arrive in some cases. Use the B4J code to test it.
 

Attachments

  • B4J-SendingTool.zip
    1.1 KB · Views: 1,123
Last edited:

luke2012

Well-Known Member
Licensed User
Longtime User
based on this i would try something like

B4X:
Private Sub SendMessage(id As String, Title As String, Body As String)
   Dim Job As HttpJob
   Job.Initialize("fcm", Me)
   Dim m As Map = CreateMap("to": $"${id}"$)
   Dim data As Map = CreateMap("title": Title, "body": Body)
   m.Put("data": data)
   Dim jg As JSONGenerator
   jg.Initialize(m)
   Job.PostString("https://fcm.googleapis.com/fcm/send", jg.ToString)
   Job.GetRequest.SetContentType("application/json;charset=UTF-8")
   Job.GetRequest.SetHeader("Authorization", "key=" & API_KEY)
End Sub

Hi @DonManfred :) What is the Id? Device ID?
 

Alpandino

Member
Licensed User
Hi, I'm using FCM for a chat-app, it works great. However, often when my app is in background the OS kill my process. From this moment the app isn't able to receive other messages (correctly, because the FirebaseMessaging service is destroyed). In order to be able to receive messages again, I must re-open the app and send a message from the app. Only at this point I see in the log that the FirebaseMessaging service is restared, in fact now I'm able to receive messages.
I tried to make the FirebaseMessaging service sticky, but when the service automatically restart I get this error:
** Service (starter) Create **~i:** Service (starter) Start **
** Service (starter) Start **
** Service (firebasemessaging) Create **
** Service (firebasemessaging) Start **

firebasemessaging_service_start (java line: 476)
java.lang.RuntimeException: Object should first be initialized (Intent).
at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
at b4a.inPlace.firebasemessaging._service_start(firebasemessaging.java:476)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:153)
at b4a.inPlace.firebasemessaging.handleStart(firebasemessaging.java:95)
at b4a.inPlace.firebasemessaging.access$000(firebasemessaging.java:8)
at b4a.inPlace.firebasemessaging$1.run(firebasemessaging.java:76)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5298)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:911)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:706)

What does it mean?
I don't know what is the argument "StartingIntent" of Service_Create Sub, and I ignore the usefulness of the line code "If fm.HandleIntent(StartingIntent) Then Return" inside the Service-Start Sub
Is possible to eliminate this line? I think is this line that cause the error.

B4X:
Sub Service_Start (StartingIntent As Intent)
    If fm.HandleIntent(StartingIntent) Then Return
End Sub

Thank you for your support
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
From this moment the app isn't able to receive other messages (correctly, because the FirebaseMessaging service is destroyed).
That's not how it works. It uses a static intent to intercept the incoming message. This means that the OS will create a new process if your app is not currently running. You don't need to set it to be a sticky service nor to start the service yourself.

It should catch messages in the background. Make sure to test it in release mode and don't kill the app yourself.
 

luke2012

Well-Known Member
Licensed User
Longtime User
Is there any framework / tutorial that allow FCM notifications on Android Wear?
 

Alpandino

Member
Licensed User
That's not how it works. It uses a static intent to intercept the incoming message. This means that the OS will create a new process if your app is not currently running. You don't need to set it to be a sticky service nor to start the service yourself.

It should catch messages in the background. Make sure to test it in release mode and don't kill the app yourself.

Erel, you're right.
If Android kills the FirebaseMessaging service and another device sends me a message, I receive the message. Before I wasn't able to receive messages because I was sending the message from my device to my self (clearly with a topic I have subscribed): in this case I can confirm that the device don't receives the message. It's strange...... There is someone that can explain this?

Thanks a lot!
 

freedom2000

Well-Known Member
Licensed User
Longtime User
Before I wasn't able to receive messages because I was sending the message from my device to my self (clearly with a topic I have subscribed): in this case I can confirm that the device don't receives the message. It's strange...... There is someone that can explain this?

Thanks a lot!

I do the same (sending message to myself) and I do receive them ...
 

Alpandino

Member
Licensed User
It should work.
Well, I did other tests. Maybe I understood the problem. I noticed that if I connect the phone to PC with the USB I often (almost ever) don't receive messages, also If the app is installed in release mode. Maybe the problem is that in my device I have activated the developer mode, and in some way this can give some trouble.
Is it possible?
 

Alpandino

Member
Licensed User
Are you connected with B4A-Bridge? If so then it will only display the logs in debug mode.

If you are connected in USB debug mode then it should show all logs.
I'm connected in USB, but in release mode, and I can see all logs. Once I have installed the app (always with Developer Mode enabled on my device) I tried also to plug the USB in a power supply, with same behavior: when Android kills the process I can't receive message anymore. I see in the log that FirebaseMessaging service has started when the message arrive, but it seems that the fm_MessageArrived Sub is not raised (I have configured a ToastMessage to check this). In this situation I can resolve the problem deleting the cache. I tried also to disable the Developer Mode, and in this case when Android kills the process I can receive message again.
I did another test: I swiped out my app from the app list. In this case (also without developer mode) I can't receive the message anymore (or to say better, I receive the message, but the fm_MessageArrived Sub is not raised). To be able to receive new message again I must delete cache. So I'm looking for a way to delete the cache by code each time the starter service starts (I made it sticky). I'm reading about DirInternalCache. If I delete all files inside DirInternalCache I'm deleting the cache as if I was click on Settings --> Applications Management --> MyApp --> Delete Cache?

Thanks
 

Alpandino

Member
Licensed User
When the message arrive I'm able to show notifications, no problem.
This is the log when alla works fine. I deleted non-relevant info.
"Message Arrived" is a log I put when fm_MessageArrived is raised.

B4X:
** Service (firebasemessaging) Create **
** Service (firebasemessaging) Start **
Mi sono registrato a alpandino

** Activity (listautenti) Create, isFirst = true **~i:** Activity (listautenti) Resume **
Lista utenti ricevuta, sto elaborando...
mappa di partenza: (MyMap) {error=false, users_list=[{uid=xtj2Aa8W9DMTekFybpPL9gKLlEl1, nickname=Armando , gender=M,  fmtoken=ftlj5SXTFbk:APA91bF5kfjvSkBwmJ4WoSnJNcDLxBicpYxWXZz8ig341gZ7vaObgfpqzidwS1p7A36lFEBnQxSkmOQtKmZzHOjzXQIXZ5EVlrWxbNKNaDjVM3pno1kkuHMr2fWianM01VsAYBb-Hyr8}]}
JobName = fcm, Success = true~i:
** Service (firebasemessaging) Start **
******Message arrived******
** Activity (listautenti) Pause, UserClosed = false **
TOPIC: alpandino
** Activity (configprofile) Pause, UserClosed = true **
** Activity (listautenti) Resume **JobName = fcm, Success = true
** Service (firebasemessaging) Start **
******Message arrived******

I waited that OS kills the process. Then I send a message to my device.
This is the log that I usually can see:
B4X:
** Service (firebasemessaging) Create **
** Service (firebasemessaging) Start **
Mi sono registrato a alpandino

** Activity (listautenti) Create, isFirst = true **~i:** Activity (listautenti) Resume **
Lista utenti ricevuta, sto elaborando...
mappa di partenza: (MyMap) {error=false, users_list=[{uid=xtj2Aa8W9DMTekFybpPL9gKLlEl1, nickname=Armando , gender=M,  fmtoken=ftlj5SXTFbk:APA91bF5kfjvSkBwmJ4WoSnJNcDLxBicpYxWXZz8ig341gZ7vaObgfpqzidwS1p7A36lFEBnQxSkmOQtKmZzHOjzXQIXZ5EVlrWxbNKNaDjVM3pno1kkuHMr2fWianM01VsAYBb-Hyr8}]}
JobName = fcm, Success = true~i:
** Service (firebasemessaging) Start **

After the last line I can't see the "******Message arrived******" line. And the message in fact don't arrive.
 

Alpandino

Member
Licensed User
I already posted the full logs.
I deleted only non relevant informations (e.g. Checkpoints to control some variable of my app unrelated with FirebaseMessaging)

After the OS kills the app, I obtain (almost always) the second log: after the line

** Service (firebasemessaging) Start **

there is nothing, I can't read the line
"******Message arrived******"
and correctly the ToastMessage don't popups on the screen, and consequently the DB is not updated with the message that someone has sent to me.
Attention: this happens (almost always) when the device has the developer mode enabled and it is connected to an USB cable (also connected to a power supply). If I disable the developer mode all works fine. The only problem there is if I swype out my app from the app list.

I will do other tests....
 

Alpandino

Member
Licensed User
I already posted the full logs.
I deleted only non relevant informations (e.g. Checkpoints to control some variable of my app unrelated with FirebaseMessaging)

After the OS kills the app, I obtain (almost always) the second log: after the line

** Service (firebasemessaging) Start **

there is nothing, I can't read the line
"******Message arrived******"
and correctly the ToastMessage don't popups on the screen, and consequently the DB is not updated with the message that someone has sent to me.
Attention: this happens (almost always) when the device has the developer mode enabled and it is connected to an USB cable (also connected to a power supply). If I disable the developer mode all works fine. The only problem there is if I swype out my app from the app list.

I will do other tests....
I did a lot of test. I disabled the developer mode, I disconnected the usb cable, but occasionally my device becomes unable to receive message. I put a toastmessage inside FirebaseMessaging service, as first line of Service_Create and Service_Start:

B4X:
Sub Service_Create
   
    fm.Initialize("fm")
    ToastMessageShow("Ho appena inizializzato FM", False)
    OtherUser.Initialize
    mapNewusers.Initialize
End Sub

Public Sub SubscribeToTopics(topic As String)
   'fm.SubscribeToTopic("general") 'you can subscribe to more topics
   fm.SubscribeToTopic(topic)
   Log("Mi sono registrato a "&topic)
End Sub

Public Sub UnsubscribeToTopics(topic As String)
    fm.UnsubscribeFromTopic(topic)
End Sub

Sub Service_Start (StartingIntent As Intent)
    'Log("StartingIntent FM: "&StartingIntent)
    ToastMessageShow("FM is started",False)
      If fm.HandleIntent(StartingIntent) Then Return
End Sub

When I'm unable to receive a message, after someone sends me a message, I see that the FirebaseMessaging service starts looking into the application management of android, but I don't see any toastmessage.
There is no a timing, sometimes it happens in few minutes, sometimes after 2 hours.
This behavior seems don't appears in other devices. I tested other 2 devices and all is working fine, for the moment.
Please, help me, I don't have other ideas.... :-(
 
Status
Not open for further replies.
Top