Android Question Broadcast Receiver on Android 8

FrankBerra

Active Member
Licensed User
Longtime User
Hello Forum!
I am implementing a broadcast receiver in my app for detecting when connection is available on the device.

I use this code in my app:
B4X:
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   Dim BroadCast As BroadCastReceiver
End Sub

Sub Service_Create
   BroadCast.Initialize("BroadcastReceiver")
   BroadCast.addAction("android.net.conn.CONNECTIVITY_CHANGE")
   BroadCast.SetPriority(2147483647)
   BroadCast.registerReceiver("")
End Sub

Sub BroadcastReceiver_OnReceive (Action As String, i As Object) 'https://www.b4x.com/android/forum/threads/broadcastreceiver.12493/
   Dim retIn As Intent
   retIn = i
   Dim IsConnected As String
   'Dim TheType As String
 
   If Action = "android.net.conn.CONNECTIVITY_CHANGE" Then
       Dim jo As JavaObject = retIn
       Dim NetworkInfo As JavaObject = jo.RunMethod("getParcelableExtra", Array("networkInfo"))
       IsConnected = NetworkInfo.RunMethod("getState", Null)

       If IsConnected.EqualsIgnoreCase("CONNECTED") Then
          'Do this...
       Else
          'Do that...
       End If
   
   End If
End Sub

Sub Service_Start (StartingIntent As Intent) 
   Service.StopAutomaticForeground 'Call this when the background task completes (if there is one)
End Sub

Sub Service_Destroy

End Sub

If i use this code in Starter service it seems to work on some devices but on some others it seesm it crashes with something like:
B4X:
java.lang.RuntimeException:
at anywheresoftware.b4a.keywords.Common$11.run (Common.java:1167)
at android.os.Handler.handleCallback (Handler.java:789)
at android.os.Handler.dispatchMessage (Handler.java:98)
at android.os.Looper.loop (Looper.java:164)
at android.app.ActivityThread.main (ActivityThread.java:6944)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:327)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1374)
Caused by: java.lang.IllegalStateException:
at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1538)
at android.app.ContextImpl.startService (ContextImpl.java:1484)
at android.content.ContextWrapper.startService (ContextWrapper.java:663)
at anywheresoftware.b4a.keywords.Common.StartService (Common.java:894)
at anywheresoftware.b4a.keywords.Common$11.run (Common.java:1137)

And also someone else said that broadcast receivers should not be put in starter service (https://www.b4x.com/android/forum/threads/broadcast-receiver-not-working.95679/)

So i put the broadcast receiver in another service but now i am experiencing another problem: it seems that Android 8 kills background services so when the connectivity changes the receiver doesn't receive nothing.

Any suggestion about detecting connection available?

(I need this because i created a chat app and when i send a messge while there is no connection it is put in a queue and the app waits until network connection is detected to retry sending the message)
 

DonManfred

Expert
Licensed User
Longtime User
The solution is posted in this thread too. https://www.b4x.com/android/forum/threads/broadcast-receiver-not-working.95679/#post-604449
it seems that Android 8 kills background services so when the connectivity changes the receiver doesn't receive nothing.
A BCR has nothing to do with a long running task. They are two pair of shoes.
You do not need to have your app running all the time. Just wait for a BCR Message come in into your broadcastservice and then check if you need to do anything.

and the app waits until network connection is detected
Do not block the app. Put them in the queue if there is no connection. Wait for the BCR getting a Message. If the connection is there then resend the Data (should be done in your starter service) and stop the bcrservice.
B4X:
Sub Service_Start (StartingIntent As Intent)
  ' do your quick check here. If any data to send call a sub in the starter to send it and then immediatly
   ' you can stop the AutomaticForeground from the bcr-service
   Service.StopAutomaticForeground 'Call this when the background task completes (if there is one)
End Sub
 
Upvote 0

FrankBerra

Active Member
Licensed User
Longtime User
Evidently i suffer from a lack of knolege about this subject.
The solution suggested in that thread uses manifest to register broadcast but this is what i learned by searching around the forum and internet:

- On official Android documentation they say that: "Apps that target Android 8.0 or higher can no longer register broadcast receivers for implicit broadcasts in their manifest. An implicit broadcast is a broadcast that does not target that app specifically." (https://developer.android.com/about/versions/oreo/background)
So i suppose checking for a connection available it is not related specifically to my app.

- But the last lines of this post says that "Update 2018: Static intent filters will cause the service to start while the app is not in the foreground. This means that on newer devices it will only work with B4A v8+ and that we need to call Service.StopAutomaticForeground once the task has completed." (https://www.b4x.com/android/forum/bookmarks/?type=post&id=116193)

So due to that post am i allowed to continue to use manifest to register Broadcasts even if Android official documentation doesn't allow this?
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
This is not relevant here. You are not creating a static intent filter.

The only way to prevent the OS from killing the service is by making the service a foreground service. You can stop it from being a foreground service when the user exits your app.
 
Upvote 0

FrankBerra

Active Member
Licensed User
Longtime User
I feel more confused now.
I followed the example and added to manifest the following code:
B4X:
AddPermission("android.permission.CHANGE_NETWORK_STATE") 'I am not sure if it is needed
AddPermission("android.permission.ACCESS_WIFI_STATE") 'I am not sure if it is needed
AddPermission("android.permission.ACCESS_NETWORK_STATE") 'I am not sure if it is needed
AddReceiverText(BroadcastReceiverService,
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>)

And then i modified the service as follows:
B4X:
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 Service_Create

End Sub


Sub Service_Start (StartingIntent As Intent)
Log("Intent: " & StartingIntent.Action)

Dim IsConnected As String
If StartingIntent.IsInitialized And StartingIntent.Action <> Null And StartingIntent.Action == "android.net.conn.CONNECTIVITY_CHANGE" Then
       Dim jo As JavaObject = StartingIntent
       Dim NetworkInfo As JavaObject = jo.RunMethod("getParcelableExtra", Array("networkInfo"))
       IsConnected = NetworkInfo.RunMethod("getState", Null)

       If IsConnected.EqualsIgnoreCase("CONNECTED") Then
          'Do this...
       Else
          'Do that...
       End If
 
End If

   Service.StopAutomaticForeground 'Call this when the background task completes (if there is one)
End Sub

Sub Service_Destroy

End Sub

Now an a empty Intent.Action is logged only when the app starts for the first time but if i put device in airplane mode or i power off wifi nothing happens. No logs and no crashes.
Anyone found a solution to obtain this behaviour on Android 8+: "Static intent filters will cause the service to start while the app is not in the foreground" ?
(I don't need the service to be continously running but i need to catch the conectivity change)

Thanks in advance for your help!

Edit: I found this: https://www.b4x.com/android/forum/threads/which-is-the-best-way-to-monitor-changes-if-api-23.91066/
Is this the final answer: "Schedule your service to run every hour (non-exact) and try to synchronize." ? Or are there any better approaches?
 
Last edited:
Upvote 0

FrankBerra

Active Member
Licensed User
Longtime User
It will not work on Android 8+. You must use a dynamic intent filter here (BroadcastReceiver library) with a foreground service.
As you can see in the first post i use a dynamic intent filter with BroadcastReceiver library.

So at this point the problem should be with the "foreground service"....
In this tutorial (https://www.b4x.com/android/forum/threads/automatic-foreground-mode.90546/) is said that:
"The only way for services to start when the app is not in the foreground is with a new API that ensures that the service will start and immediately switch to foreground mode.
This is now handled automatically in the B4A framework. When needed services automatically start in foreground mode."

If the service is started in foreground mode automatically i suppose it should work....Where i am wrong?
Should i move "Service.StopAutomaticForeground" in the sub "BroadcastReceiver_OnReceive" ?
 
Upvote 0

FrankBerra

Active Member
Licensed User
Longtime User
I attach a "working" project with full code that uses the Broadcast Library and "Service.StartForeground", but something is not working as expected.

How to reproduce the problem:
1) Open the app so the Starter service starts also the "BroadcastReceiverService"
2) A toastmessage says "CONNECTED"
3) If i exit from the app and i open, for example, other apps and i power off the WIFI the toastmessages appears and says "DISCONNECTED".
4) If i power on the wifi again it says "CONNECTED"
5) After a while (let's say one or two minutes) if i power off/on again the wifi no toast messages appears anymore.

What's wrong?
Is there anyone here on the forum that faced this same problem that has found the solution and could point me in the right direction step-by-step?
Thank you very much in advance!
 

Attachments

  • ConnectivityChange.zip
    9.1 KB · Views: 299
Upvote 0
Cookies are required to use this site. You must accept them to continue using the site. Learn more…