Android Question Timer in Starter

Dianzoa

Active Member
Licensed User
Hello Friends!
I have a Timer in the Starter Service, to watch for changes in a remote database, even if the app is minimized o in background the starter must check for the database changes. It works for a short period of time, then it crush. Heres is the image of the log error.
EDIT: Actually the title has to be: " Calling httpjobs in Starter Service"
B4X:
Sub Service_Start (StartingIntent As Intent)
 
    If gpsClient.GPSEnabled=True Then
     
    '        CallSub(Me,"StartGps")
        timer2.Initialize("timer2",20000)
        timer2.Enabled = True
'        gpsClient.Start(0, 0)
'        'timer_gps.Initialize(timer_gps_tick, 35000)
'        'timer_gps.Enabled = True
'        'ProgressDialogShow("Waiting for GPS location")
    End If
End Sub

And I check the database in the timertick event
B4X:
Dim ServerUrl As String
    ServerUrl = "someurl.aspx?id=" & Main.idrepartidor
    Log(ServerUrl)
    Dim job As HttpJob
    'Msgbox(ServerUrl,"")
   
    job.Initialize("", Me)
    job.PostString(ServerUrl,"")
    Wait For (job) JobDone(j As HttpJob)
    If job.Success Then
        Dim parser As JSONParser
        Dim response As String = job.GetString
        'Msgbox(response,"")
        parser.Initialize(response)
        Dim rows As List
        rows = parser.NextArray
        If rows.Size > 0 Then
        'Msgbox("trae filas","")
            For i = 0 To rows.Size - 1
                'SendMessage(str(Main.idrepartidor), "Nuevo Pedido", Main.nombre_cliente As String)
                'Log("Rows #" & i)
                Dim m As Map
                m = rows.Get(i)
                'Msgbox(Main.idrepartidor,"repartidor")
                'Main.idrepartidor = m.Get("idrepartidor")
                nombre_cliente = m.Get("nombre_solicitante")
                           
                Select m.Get("estado")
                    Case "P "
                        estado = "Pendiente"
                                   
                        If ya_notifique = 0 Then
                                       
                            Dim p As Phone
                            'Msgbox(p.SdkVersion,"")
                            If p.SdkVersion >= 21 Then
                                Dim nb As NotificationBuilder
                                nb.Initialize
                                           
                                nb.ContentTitle = "Nuevo Pedido"
                                nb.ContentText = "Nuevo Pedido de " & nombre_cliente
                                nb.AutoCancel = True
                                If Main.idrepartidor > 0 Then
                                    nb.setActivity(interfaz_inicio)
                                Else
                                    nb.setActivity(Main)
                                End If
                                nb.SmallIcon = "ducati"
                                ya_notifique = 1
                                Main.notifico = 1
                                'nb.Notify(1)
                                If p.SdkVersion >= 26 Then
                                    Dim ctxt As JavaObject
                                    ctxt.InitializeContext
                                    Dim manager As JavaObject
                                    manager.InitializeStatic("android.app.NotificationManager")
                                    Dim Channel As JavaObject
                                    Dim importance As String
                                    importance = "IMPORTANCE_HIGH"
                                    Dim ChannelVisibleName As String = Application.LabelName
                                    Channel.InitializeNewInstance("android.app.NotificationChannel", _
                                                   Array("MyChannelId1", ChannelVisibleName,manager.GetField(importance)))
                                    manager = ctxt.RunMethod("getSystemService", Array("notification"))
                                    manager.RunMethod("createNotificationChannel", Array(Channel))
                                    Dim jo As JavaObject = nb
                                    jo.RunMethod("setChannelId", Array("MyChannelId1"))
                                    ya_notifique = 1
                                    nb.Notify(1)
                                    Return
                                Else
                                    nb.Notify(1)
                                End If
                                   
                            Else
                                Dim n As Notification
                                n.Initialize
                                n.AutoCancel = True
                                n.Icon = "ducati"
                                If Main.idrepartidor > 0 Then
                                    n.SetInfo("EnviaPy","Nuevo pedido de " & nombre_cliente, interfaz_inicio)
                                Else
                                    n.SetInfo("EnviaPy","Nuevo pedido de " & nombre_cliente, Main)
                                End If
                                   
                                ya_notifique = 1
                                Main.notifico = 1
                                n.Notify(1)
                            End If
                       
                        End If
                        'End If
                    Case "R "
                               
                    Case "T "
                               
                End Select
                       
                           
            Next
        End If
    End If
End If
End Sub

Any tips?
Regards, Diego
 

Attachments

  • timer_starter_error.png
    timer_starter_error.png
    11.8 KB · Views: 312
Last edited:

Dianzoa

Active Member
Licensed User
I think it fails because the httputils service was destroy, and the error is in the poststring line.
** Service (httputils2service) Destroy **
 
Upvote 0

Dianzoa

Active Member
Licensed User
Please post the error message as text (right click to copy).

Which version of B4A are you using?
I am using VERSION 8.50!

Registo conectado a: HUAWEI DRA-LX3
--------- beginning of crash
--------- beginning of main
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=0
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=0
** Activity (main) Pause, UserClosed = true **
** Activity (interfaz_inicio) Create, isFirst = true **
** Activity (interfaz_inicio) Resume **
** Activity (interfaz_inicio) Pause, UserClosed = false **
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
** Service (starter) Destroy (ignored)**
** Service (httputils2service) Destroy **
http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=4
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=b4a.example/anywheresoftware.b4a.samples.httputils2.httputils2service }: app is in background uid UidRecord{ffc21a7 u0a91 SVC bg:+1m9s202ms idle change:idle procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1525)
at android.app.ContextImpl.startService(ContextImpl.java:1481)
at android.content.ContextWrapper.startService(ContextWrapper.java:650)
at anywheresoftware.b4a.keywords.Common$9.run(Common.java:870)
at android.os.Handler.handleCallback(Handler.java:795)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:6857)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:450)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
 
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
Useful articles
https://developer.android.com/about/versions/oreo/android-8.0-changes#back-all
https://developer.android.com/about/versions/oreo/background

Android 8.0 (API level 26) also includes the following changes to specific methods:

  • The startService() method now throws an IllegalStateException if an app targeting Android 8.0 tries to use that method in a situation when it isn't permitted to create background services.
  • The new Context.startForegroundService() method starts a foreground service. The system allows apps to call Context.startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.

Briefly: when app is background, background services can't start another background services. Meanwhile exactly this happens: Activity is paused, Starter service (if to change nothing) is background.
 
Upvote 0

Dianzoa

Active Member
Licensed User
Ok, I got that, so, I can't use jobs to check for DB in starter anymore, since the httputils2 service does not survive in the background.
So what can I do so instead with this new version?
EDIT: If I create another service as foreground would do nothing because I need to call jobs and htpputils service will be destroy anyway from inside the new service. I am out of ideas :(
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
I have approximatelly the same situation - my "background" service must send files to website.

I do following. In Activity_Resume (when my app is foreground) I check status of service and, if not running, start service again.
The service is foreground (Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS).

In my case there is a job for service time to time only. So, if there is no work, I increase delay intervals.
To check status of service I use ordinary variables (in Service_Start IsRunning = True, in Service_Destroy - IsRunning = False)

Of course, Android is able to kill my service. Not a problem (for me). When user will activate my app, the service will be created again.
 
Upvote 0

Dianzoa

Active Member
Licensed User
I have approximatelly the same situation - my "background" service must send files to website.

I do following. In Activity_Resume (when my app is foreground) I check status of service and, if not running, start service again.
The service is foreground (Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS).

In my case there is a job for service time to time only. So, if there is no work, I increase delay intervals.
To check status of service I use ordinary variables (in Service_Start IsRunning = True, in Service_Destroy - IsRunning = False)

Of course, Android is able to kill my service. Not a problem (for me). When user will activate my app, the service will be created again.

:( Well, I need exactly to check database in the background, I mean the app is almost never really "active" in the foreground. Just to confirm the task after a notification arrives. Of course I need to check the database until the user explicitly logout from the app. With this I mean that the user not need to activate the app in order to receive these database changes .
 
Upvote 0

Dianzoa

Active Member
Licensed User
Maybe the solution would be that we can make the httputils service to be like the starter service and never be destroyed? Because httputils service would not start even if it is inside a foreground service that never dies, because at the end the httpservice is dead. Or maybe another way to look to database without calling aspx scripts?
 
Upvote 0

Dianzoa

Active Member
Licensed User
Ok, I think I solved with the help of the docs Semen give me and the tutorial on foreground services. I create a foregroundalways service and call the httpjobs from there, and it works, the httputils service was never destroyed. At the end it was more simple than I thought.

Thanks.
This is the code that I change in case some need.

B4X:
Sub timer2_tick
'    'If ya_notifique = 0 Then
'       
'    Dim ServerUrl As String
'    ServerUrl = "http://enviapy.gear.host/traer_pedido_solicitado.aspx?id=" & Main.idrepartidor
'    Log(ServerUrl)
'    Dim job As HttpJob
'    'Msgbox(ServerUrl,"")
'       
'    job.Initialize("buscar_pedido", Me)
'           
'    job.PostString(ServerUrl,"")
'        'Wait For (job) JobDone(j As HttpJob)
'            'If (IsPaused(HttpUtils2Service)) Then StartService(HttpUtils2Service)
'    'End If
'CallSubDelayed(interfaz_inicio,"timer1_tick")
If IsPaused(notificacion_minimizada) = True Then
        StartServiceAt(notificacion_minimizada, DateTime.Now -30, True)
        timer2.Enabled = False
End If
Instead of calling the httpjobs in the starter timer, I just start the new service immediately, and that service manage the jobs.
 
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
About your previous idea.
An attached app successfully processes StopService, but I am absolutelly not sure that Android kills services exactly so.
To imitate a situation, when Android will kill a service, is enough hard. I started 20-30 apps, but Test service continious to work.
 

Attachments

  • test.zip
    8.5 KB · Views: 224
Upvote 0

Dianzoa

Active Member
Licensed User
About your previous idea.
An attached app successfully processes StopService, but I am absolutelly not sure that Android kills services exactly so.
To imitate a situation, when Android will kill a service, is enough hard. I started 20-30 apps, but Test service continious to work.
I used your technique, it works ok, basically I change all the logic from starter to the new service foreground. I have to test it on android 6 now.
 
Upvote 0

Dianzoa

Active Member
Licensed User
I have a new issue. In android 6 and 7, these code that works ok in android 8, keeps doing noise and vibrating every time the timer executes, because I think of the foreground service. How can I get rid of that?
EDIT: Using the AUTOMATIC_FOREGROUND_WHEN_NEEDED option did the trick on android 6 and 7, well, again, I need to test this option in android 8.1 hehehe. Fun & annoying at the same time, but for sure I am learning cool stuff.
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
Under Android 6, 7 the service can be ordinary. So you can replace
B4X:
Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS
to
B4X:
Dim phoneInstance As Phone
If phoneInstance.SdkVersion >= 26 Then Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS

Вut I am very doubt that "foreground service" as it is generates noise and vibrating
Do you here sound when you run my test also ? (BTW, do you test in smartphones or emulators ?)
 
Upvote 0

Dianzoa

Active Member
Licensed User
Under Android 6, 7 the service can be ordinary. So you can replace
B4X:
Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS
to
B4X:
Dim phoneInstance As Phone
If phoneInstance.SdkVersion >= 26 Then Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS

Вut I am very doubt that "foreground service" as it is generates noise and vibrating
Do you here sound when you run my test also ? (BTW, do you test in smartphones or emulators ?)
I test it on real phones, huawei with android 6,7 and 8.1. I did not test your test with android 6, only with android 8.1, and still your test left an notification icon, but I can live with that
 
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
At first I tested on my phone (Android 8.1) and in emulator (API 26). No sound.

Then I run in API 24/25 emulator. 2 sounds as notification, then silence. If to remove Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS then no sound at all.

I agree Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS under Android 6/7 is responsible for sound.

I looked core.jar. And see, for example, this.service.startForeground(Id, Notification);
 
Last edited:
Upvote 0

Dianzoa

Active Member
Licensed User
At first I tested on my phone (Android 8.1) and in emulator (API 26). No sound.

Then I run in API 24/25 emulator. 2 sounds as notification, then silence. If to remove Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS then no sound at all.

I agree Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS under Android 6/7 is responsible for sound.

I looked core.jar. And see, for example, this.service.startForeground(Id, Notification);
Yes! I took your advice of the if phone sdk version etc, and works like a charm. Thank You! The only thing that I still can not search about it is the notification icon, doesnt not work in android 8.1, just a white square, but I think I read about it on some post here.
 
Upvote 0

Dianzoa

Active Member
Licensed User
What I have learned with this is that httputils service won't live in starter, but will live forever(almost) if used in a foreground service(android 8+). I was assuming that httputils service would also live like the starter service if it was used in starter service.
 
Upvote 0
Top