Android Question Now - Android 6 ServiceStartAtExact not accurate

Discussion in 'Android Questions' started by wes58, Mar 27, 2016.

  1. wes58

    wes58 Active Member Licensed User

    With KitKat, Google changed AlarmManager such that if you wanted the Service to start at exactly set time you had to use AlarmManager new method setExact (int type, long triggerAtMillis, PendingIntent operation).
    Erel implemented this in B4A as StartServiceAtExact.
    Now I have upgraded to android 6.0.1 and (not) to my surprise this function is no longer accurate. I get sometimes couple of minutes (6, 7 min.) difference from the scheduled time.
    This of course is the result of Google introducing a "Doze" mode.

    I tried to use AlarmManager method setExactAndAllowWhileIdle (not tested yet), but searching the web I found that this doesn't fix the problems.
    On stackoverflow http://stackoverflow.com/questions/33432661/alarm-manager-for-background-services I found a Java example that uses WakefulBroadcastReceiver that supposed to work in Doze mode. But I am not sure yet how to implement it. Maybe someone with more Java knowledge (or Erel) could have a look at this - i.e. how to implement WakefulBroadcastReceiver. There is also a link there to the CommonsWare website with an example on Github https://github.com/commonsguy/cwac-wakeful

    The code is:
    Code:
    public class PollReceiver extends WakefulBroadcastReceiver{
        static final 
    String PERIOD = "period";
        @Override
        
    public void onReceive(Context context, Intent intent){
             startWakefulService(context,new 
    Intent(context,MyService.class));
             long 
    period = intent.getLongExtra(PERIOD,-1);
             
    if(period>0){
             scheduleExactAlarm(context,(AlarmManager)context.getSystemService(Context.ALARM_SERVICE),
    period)
             
    }
        }

        static void scheduleExactAlarm(Context context,AlarmManager alarms, long period){
        Intent i = new Intent(context,PollReceiver.class);
        PendingIntent pi = PendingIntent.getBroadcast(context,0,i,0);
        if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP_MR1){
            alarms.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
            SystemClock.elapsedRealtime() + period,pi);
        }
    }
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    WakefulBroadcastReceiver solves a different problem where the broadcast receiver is started and then the device goes to sleep again.

    Try it with this code:
    Code:
    Sub SetExactAndAllowWhileIdle (Time As Long, ServiceName As String)
       
    Dim p As Phone
       
    If p.SdkVersion < 23 Then
         StartServiceAtExact(ServiceName, Time, 
    True)
       
    Else
         
    Dim in As Intent
         
    in.Initialize("""")
         
    in.SetComponent(Application.PackageName & "/." &  ServiceName.ToLowerCase)
         
    Dim jin As JavaObject = in
         jin.RunMethod(
    "setAction"Array(Null))
         
    Dim ctxt As JavaObject
         ctxt.InitializeContext
         
    Dim am As JavaObject = ctxt.RunMethod("getSystemService"Array("alarm"))
         
    Dim pi As JavaObject
         pi = pi.InitializeStatic(
    "android.app.PendingIntent").RunMethod("getService", _
      
    Array(ctxt, 1in134217728))
         am.RunMethod(
    "setExactAndAllowWhileIdle"Array(0, Time, pi))
       
    End If
    End Sub
     
    Last edited: Oct 4, 2017
    ilan, Pencil3, Peter Simpson and 2 others like this.
  3. wes58

    wes58 Active Member Licensed User

    I did it similar way (the way I used to do it for setExact method prior to you implementing it. See below) but I didn't have time to test it. But reading comments on the stackoverflow I am not very optimistic. They say that this method doesn't work.

    Code:
    public void StartServiceAtExactWhileIdle(Long time, Boolean wakeUp){            
            AlarmManager alMan = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
            
    Intent intent = new Intent(this.getApplicationContext(), this.getClass());
            PendingIntent    penInt = PendingIntent.getService(this.getApplicationContext(), 
    1intent134217728);
            alMan.setExactAndAllowWhileIdle(wakeUp?AlarmManager.RTC_WAKEUP:AlarmManager.RTC, time, penInt);
        
    }
    I will see if it works tomorrow.
     
  4. wes58

    wes58 Active Member Licensed User

    I have tested it and on Android 6 when Alarm Manager Method setExactAndAllowWhileIdle is used, the scheduled service starts is at the set time.
     
    Erel likes this.
  5. NeoTechni

    NeoTechni Well-Known Member Licensed User

    I tried it, and it did not wake my device up
     
  6. wes58

    wes58 Active Member Licensed User

    I have been using it for over a week with my application that changes phone profile (i.e. switches data, wifi, bluetooth on/off and change other settings depending on the time of the day). The profiles are changed 3 times a day and the phone wakes up accurately at the set times.
    Which code are you using the one that Erel posted or mine? If you are using Erel's, did you compile with SDK ver. 23?

    Maybe you can post your code (or part of it).
     
  7. NeoTechni

    NeoTechni Well-Known Member Licensed User

    I used

    Code:
    Sub StartServiceAtExact2 (ServiceName As String, Time As Long, DuringSleep As Boolean)
      
    Dim p As Phone
      
    If p.SdkVersion < 23 Then
      StartServiceAtExact(ServiceName, Time, DuringSleep)
      
    Else
      
    Dim in As Intent, ctxt As JavaObject, pi As JavaObject
      
    in.Initialize("""")
      
    in.SetComponent(Application.PackageName & "/." &  ServiceName.ToLowerCase)
      ctxt.InitializeContext
      
    Dim am As JavaObject = ctxt.RunMethod("getSystemService"Array("alarm"))
      pi = pi.InitializeStatic(
    "android.app.PendingIntent").RunMethod("getService",  Array(ctxt, 1in134217728))
      am.RunMethod(
    "setExactAndAllowWhileIdle"Array(0, Time, pi))
      
    End If
    End Sub
    I used the name so I can just paste over my StartServiceAtExact calls.

    It didn't trigger till I turned my screen on
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    How do you see that it doesn't start? Are you checking the logs?
     
  9. NeoTechni

    NeoTechni Well-Known Member Licensed User

    I made an alarm clock that I used for over a year, until Google neutered it with Doze mode.
    My alarm clock is a lot more, aggressive, than Google's. As I have narcolepsy, and waking me up gets difficult some times

    So I set my alarm for 11 am, and it didn't go off till I turned my screen on at 11:02
     
    emexes likes this.
  10. wes58

    wes58 Active Member Licensed User

    I know that I asked before about what SDK you compiled your apk with, and I didn't mean what version of android.jar you are using.
    Why I ask again? Because I tried your code in my apk and it didn't work. Then I realised that in Manifest I had targetSdkVersion="20". That's why compiled apk used StartServiceAtExact and that's why it wasn't accurate.
    I don't use targetSdkVersion="23" because of problems with permissions that google changed in Android 6.
    So if you have targetSdkVersion les than 23 than remove the check from your code. That's the only explanation why it doesn't work that I can think of.

    I think, that better way would probably be, to check what OS version is apk used on rather than what target SDK version is.
     
  11. NeoTechni

    NeoTechni Well-Known Member Licensed User

    I didnt understand your instructions

    I have minsdk as 4
    Target is left out but it looks like im targeting 10 so I can keep the : button
     
  12. wes58

    wes58 Active Member Licensed User

    In my apk. I used my code and it worked without problems, so I did some tests with yours (Erel's) code and I found out that it doesn't work.

    My code is:
    Code:
    'declare variable for java object
    Dim srvAt As JavaObject
    'call the function
    srvAt.InitializeContext
    srvAt.RunMethod(
    "StartServiceAtExactWhileIdle"Array(timeToAlarm, True))


    #If JAVA
    //import android.app.Service;
    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.os.PowerManager;

    public void StartServiceAtExactWhileIdle(Long time, Boolean wakeUp){ 
       AlarmManager alMan = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
       Intent intent = new Intent(this.getApplicationContext(), this.getClass());
       PendingIntent penInt = PendingIntent.getService(this.getApplicationContext(), 1, intent, 134217728);
       alMan.setExactAndAllowWhileIdle(wakeUp?AlarmManager.RTC_WAKEUP:AlarmManager.RTC, time, penInt);
    }
    #End If
    Edit:
    I realised why yours/Erel's code didn't work for me. I used as a ServiceName Me instead of just service module name.
     
    Last edited: Apr 7, 2016
    Douglas Farias likes this.
  13. NeoTechni

    NeoTechni Well-Known Member Licensed User

    Now I'm even more confused. How do I use that with the existing code?
     
  14. wes58

    wes58 Active Member Licensed User

    Sorry to confuse you.
    So you have to options:
    1. Use your code. What did you use in the sub for ServiceName? Did you use your service module name? Then your code should work. You can't use Me as a ServiceName.
    In your code you have: in.SetComponent(Application.PackageName & "/." & ServiceName.ToLowerCase)
    If you use Me for ServiceName, than in above line, whatever is replaced be Me will be appended to Application.PackageName & "/." & Me
    Do the test if you like
    ServiceName = Your_Module_Service_Name
    Log("Service Name -" & Application.PackageName & "/." & ServiceName.ToLowerCase)
    ServiceName = Me
    Log("Service Name Me -" & Application.PackageName & "/." & ServiceName.ToLowerCase)

    2. Use my code:
    Declare java object variable
    Dim srvAt As JavaObject
    Add java code between #IfJava .... #End If, at the end of the Service module.

    Replace in your code, where you had a call: StartServiceAtExact2(...)
    with two lines of code:
    srvAt.InitializeContext
    srvAt.RunMethod("StartServiceAtExactWhileIdle", Array(timeToAlarm, True))

    To confuse you more (hopefully not). When you look at both, mine and Erels code (different variable names) they are similar so both should work. He is using javaObject only. I use JavaObject to call Java Fucntion only:
    Code:
    'Context
        ctxt.InitializeContext
    'my code
        srvAt.InitializeContext
       
    'Intent
        in.Initialize("""")
        
    in.SetComponent(Application.PackageName & "/." &  ServiceName.ToLowerCase)
    'my java code   
       Intent intent = new Intent(this.getApplicationContext(), this.getClass()); 

    'Pending Intent
        pi = pi.InitializeStatic("android.app.PendingIntent").RunMethod("getService",  Array(ctxt, 1in134217728))   
    'my java code
       PendingIntent penInt = PendingIntent.getService(this.getApplicationContext(), 1intent134217728);

    'Alarm Manager
        Dim am As JavaObject = ctxt.RunMethod("getSystemService"Array("alarm"))
        am.RunMethod(
    "setExactAndAllowWhileIdle"Array(0, Time, pi))
    'my java code 
        AlarmManager alMan = (AlarmManager)getSystemService(Context.ALARM_SERVICE);   
        alMan.setExactAndAllowWhileIdle(wakeUp?AlarmManager.RTC_WAKEUP:AlarmManager.RTC, time, penInt);
     
    DonManfred likes this.
  15. NeoTechni

    NeoTechni Well-Known Member Licensed User

    I mean, what SDK should I target, how does your code use a service name? (and I use the names of the service, not "me")

    Shouldn't the java code be encapsulated in something? That doesnt look right...

    Is this right?
    Code:
    Sub SetAlarm(timeToAlarm As Long)
       
    'declare variable for java object
       Dim srvAt As JavaObject
       
    'call the function
       srvAt.InitializeContext
       srvAt.RunMethod(
    "StartServiceAtExactWhileIdle"Array(timeToAlarm, True))
    End Sub

    #If JAVA
    //import android.app.Service;
    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.os.PowerManager;

    public void StartServiceAtExactWhileIdle(Long time, Boolean wakeUp){
      AlarmManager alMan = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
      Intent intent = new Intent(this.getApplicationContext(), this.getClass());
      PendingIntent penInt = PendingIntent.getService(this.getApplicationContext(), 1, intent, 134217728);
      alMan.setExactAndAllowWhileIdle(wakeUp?AlarmManager.RTC_WAKEUP:AlarmManager.RTC, time, penInt);
    }
    #End If
    Does this wake the whole device up? Meaning if I make an APK that just handles this alarm, (so I don't have to target 23 in my main program) will my program's alarm go off once this wakes up?
     
    Last edited: Apr 7, 2016
  16. NeoTechni

    NeoTechni Well-Known Member Licensed User

    I get

     
  17. Erel

    Erel Administrator Staff Member Licensed User

    You need to use a newer version of android.jar (Tools - Configure Paths). However you can use the code I posted as the result should be the same.
     
  18. DonManfred

    DonManfred Expert Licensed User

    Any Api below 23 will give you this error. setExactAndAllowWhileIdle is introduced in Api 23
     
  19. wes58

    wes58 Active Member Licensed User

    - Read about using Java in B4A - https://www.b4x.com/android/forum/threads/inline-java-code.50141/#content
    - The service name I get from "this.getClass()"
    - If you used in your application before StartServiceAtExact to handle alarm, function StartServiceAtExactWhileIdle (or from Erel's code SetExactAndAllowWhileIdle) will do exactly the same in Android 6.
    - As per Erel's and Don Manfred's comments use correct android.jar.
     
    DonManfred likes this.
  20. NeoTechni

    NeoTechni Well-Known Member Licensed User

    But I'm using Erel's code and it doesn't work. He seemed to imply it would work without needing a new jar file.

    Which jar should I use?
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice