Android Service

NatannielNatanniel BRMember
edited November 2016 in Xamarin.Android

Good morning person,

I'm trying to create a service that runs every 5 seconds without binding the application, but I'm starting with basic routines that shows the moment the service goes into operation and the moment it shuts off, I can see that the service is not restarting, Can anyone give me a hand ?

Main Activity

protected override void OnCreate(Bundle bundle) {
    base.OnCreate(bundle);

        global::Xamarin.Forms.Forms.Init(this, bundle);
        LoadApplication(new App());
    this.StartService(MyService );
}

Service Code

[Service(Exported=true)]
public class MyService : Service{

    public override IBinder OnBind(Intent intent){
        throw new NotImplementedException();            
        }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId){
            Toast.MakeText(this, "Service Start", ToastLength.Long).Show();
        StopSelf();
                return StartCommandResult.Sticky;
    }   

     public override void OnDestroy()
        {

            Toast.MakeText(this, "Service Destroy", ToastLength.Long).Show();
            base.OnDestroy();
        }
}

Answers

  • AlbertKAlbertK MYMember ✭✭✭

    Hi,

    You will need something like a remote service where it will run when the application is not in focus (if that is what you want) The process cannot be running forever every 5 seconds as it will drain the battery in a few hours. For Marshmallow and above your process will start get frozen (doze mode) in half an hours then progressively more restriction as time goes by when the phone is no in motion (not moving). The Alarm that I am setting in my code is to wake up the phone to run my code but in Marshmallow you can only set it to run once every 9 minutes (you can set less time but nothing will happen).

    Another thing, my code will run without the Alarm wake up if you have the device plug in to a power source. Well that defeats the purpose of having a smartphone. Anyway, until someone comes up with a battery that last a month you can have process running constantly in the background.

    [Service(Name = "myservice", Enabled = true, Process = ":bgservice") ]
        public partial class MyService : Service
        {
            private const string TAG = "bgservice";
    
    
            private int wakelockcount = 0;
            private System.Timers.Timer _timer= null;
            private PowerManager.WakeLock wakelock = null;
    
    
            public override void OnCreate()
            {
                base.OnCreate();
    
                PowerManager pmanager = (PowerManager)this.GetSystemService("power");
                wakelock = pmanager.NewWakeLock(WakeLockFlags.Partial, "servicewakelock");
                wakelock.SetReferenceCounted(false);
    
            }
    
    
            public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
            {
    
                Log.Info(TAG, "OnstartCommand ");
                //SetWakeLock();
    
    
    
                AlarmManager manager = (AlarmManager)GetSystemService(AlarmService);
                long triggerAtTime = SystemClock.ElapsedRealtime() + (10 * 60 * 1000);
                Intent alarmintent = new Intent(this, typeof(AlarmReceiver));
    
                PendingIntent pendingintent = PendingIntent.GetBroadcast(this, 0, alarmintent, 0);
                if (Android.OS.Build.VERSION.SdkInt >= BuildVersionCodes.M)
                {
                    manager.Cancel(pendingintent);
                    manager.SetAndAllowWhileIdle(AlarmType.ElapsedRealtimeWakeup, triggerAtTime, pendingintent);
                    Log.Info(TAG, "Alarm SetAndAllowWhileIdle Set");
    
                }
                else if(Android.OS.Build.VERSION.SdkInt == BuildVersionCodes.KitKat || Android.OS.Build.VERSION.SdkInt == BuildVersionCodes.Lollopop)
                {
                    manager.Cancel(pendingintent);
                    manager.SetExact(AlarmType.ElapsedRealtimeWakeup, triggerAtTime, pendingintent);
                }
    
    
                    timer(this, null);
    
                return StartCommandResult.Sticky;
            }
    
        public void SetWakeLock()
            {
                if( ! wakelock.IsHeld)
                {
                    wakelock.Acquire();
                    wakelockcount += 1;
                    Log.Info(TAG, "WakeLock Armed");
                }
            }
    
         public void timer(object sender, EventArgs e)
            {
    
                Log.Info(TAG, "Starting Timer" );
    
    
                if (_timer != null)
                {
                    _timer.Enabled = false;
                    _timer.Dispose();
                    _timer = null;
                }
                _timer = new System.Timers.Timer();
                //Trigger event every 5 seconds
                _timer.Interval = 5000;
                _timer.Elapsed += OnTimeEvent; //new System.Timers.ElapsedEventHandler(OnTimeEvent);
                _timer.Enabled = true;
                _timer.AutoReset = true;
                _timer.Start();
            }
    
        private async void OnTimeEvent(object sender, System.Timers.ElapsedEventArgs e)
            {
               //This will be called when the timer.Elapsed. So do your work here.
            }
    
  • RubberyDevRubberyDev Member ✭✭
    edited March 14

    Hi @AlbertK

    First of all, I'm relatively new in Xamarin,

    I'm going to do the same process in my app and I have a questions, Why the invoke SetWakeLock method is commented and what is the purpose to use wakelock.SetReferenceCounted(false) on constructor service, can I have process running constantly in the background without plug in the device to a power source of this way?

  • AlbertKAlbertK MYMember ✭✭✭

    @RubberyDev , Well SetReferenceCounted() is referenced counted by default and setting it to false means that I am counting the wakelock call locally. SetWakeLock() is not called here as you need to unset it somewhere (no included in this example). Hope this helps.

  • RubberyDevRubberyDev Member ✭✭

    Got it!! @AlbertK
    thanks

  • mivewimivewi DKMember ✭✭

    Hi @AlbertK

    Thanks for sharing your code.

    I'm new to Xamarin and have a question about your code:
    When pasting in the code, "AlarmReceiver" is not recognized on the line:
    Intent alarmintent = new Intent(this, typeof(AlarmReceiver));

    Can you please also share that class, as I don't really know what to do in that class?

    Also what is the correct way of calling your the service? (the way I do it is deprecated):

    • Forms.Context.StartService(new Intent(Forms.Context, typeof(AlarmManagerService)));

    Kind regards

  • AlbertKAlbertK MYMember ✭✭✭

    @mivewi said:
    Hi @AlbertK

    Thanks for sharing your code.

    I'm new to Xamarin and have a question about your code:
    When pasting in the code, "AlarmReceiver" is not recognized on the line:
    Intent alarmintent = new Intent(this, typeof(AlarmReceiver));

    Can you please also share that class, as I don't really know what to do in that class?

    Also what is the correct way of calling your the service? (the way I do it is deprecated):

    • Forms.Context.StartService(new Intent(Forms.Context, typeof(AlarmManagerService)));

    Kind regards

    You cannot call Android specific code from Xamarin Forms. Forms is for cross platform ie the same UI and code should work in all the supported platforms (eg Android, IOS, MacOS, Tizen, UWP,etc). To run platform specific code you need to utilize Dependency Service.

    In this example raising an alarm. All the different platform has their own API to set alarm. A good start is the official documentation

    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/introduction

  • mivewimivewi DKMember ✭✭
    Hi

    Thanks for the answer. I made it work with your code.

    In OnTimeEvent I post the current position to my server, which works really well. I set the timer to 60000 ms = 1 minute and it posts positions every minute.

    But after I turn off the screen and the app goes in the background I excepted the service to post every tenth minute, as I wrote the same code in the alarmreceiver which also should post the current position. What happens is that it only calls the alarmreceiver once and then never again.

    Don't really know what's wrong, but could be the code is not working on newer phones. I'm using huawei P30 Pro.

    Kind regards
  • mivewimivewi DKMember ✭✭
    edited July 20

    Maybe its the wakelock which I'm not utilizing in my code? You wrote that I have to unset it from somewhere, where would be a good place for that in my case, when posting locations.

    I tried to uncomment it the call to start the wakelock but didnt unset it. Would it be expected behavior, that it still only hit the alarmreceiver once?

  • AlbertKAlbertK MYMember ✭✭✭

    I don't have the latest Android OS phone. Wakelock is to hold a lock so that you can finish the task but that does not work if the phone OS does not allows it to run. Why do you ask is whatsapp, facebook working? well they are in the phone whitelist (this is fixed in the OS, ie set in factory), unfair? yes. :(

    Also with Android 6.0 and above you have to live with Doze mode where you are only allowed to wake up every 10 minutes or so. Any lower time resolution will be ignored. I think that is what you are facing. With regards to the wake lock, yes you have to release them after running your task.

    https://developer.android.com/training/monitoring-device-state/doze-standby

Sign In or Register to comment.