简体   繁体   English

Android Job Scheduler - 定期调用作业直到被取消?

[英]Android Job Scheduler - calling a job on a recurring schedule until cancelled?

With the release of Oreo/Android 8.0, I'm wanting to replace my GPS foreground service with a scheduled job.随着 Oreo/Android 8.0 的发布,我想用预定的工作替换我的 GPS 前台服务。

I've looked at the Xamarin Example for Job Scheduler, and honestly I find it quite confusing.我查看了 Job SchedulerXamarin 示例,老实说,我觉得它很混乱。

Here is all I want to do:这是我想做的所有事情:

  1. User taps a button用户点击按钮
  2. A job is created and scheduled.一个作业被创建和调度。
  3. The job is called instantly, and every 30 seconds afterwards until the user taps the button again作业被立即调用,之后每 30 秒调用一次,直到用户再次点击按钮

The job will be the replacement for my GPS service (which implementes ILocationListener).这份工作将取代我的 GPS 服务(它实现了 ILocationListener)。

Could anyone help clear up what I need to do to achieve this?任何人都可以帮助澄清我需要做什么才能实现这一目标吗?

I have tried .SetPeriodic(30000), which doesn't seem to work on my test device running Oreo.我试过 .SetPeriodic(30000),它似乎在我运行 Oreo 的测试设备上不起作用。

The example uses .SetMinimumLatency() and .SetOverrideDeadline() to set some sort of schedule range.该示例使用 .SetMinimumLatency() 和 .SetOverrideDeadline() 来设置某种计划范围。 Can I set both of these to 30000 (30 seconds) to achieve what I need?我可以将这两个都设置为 30000(30 秒)来实现我的需要吗? There also seems to be a 15 minute minimum time for scheduled jobs.预定作业的最短时间似乎也为 15 分钟。

The example uses handlers and passes parameters, etc. Is this required for a Job Scheduler?该示例使用处理程序并传递参数等。这是作业调度程序所必需的吗? There's a lot going on for a basic example project, and I'm just getting confused about the requirements.一个基本的示例项目有很多事情要做,我只是对需求感到困惑。

Thanks in advance, hoping someone can help me clear this up a bit.提前致谢,希望有人能帮我澄清一下。

Cheers.干杯。

EDIT - EXAMPLE CODE编辑 - 示例代码

private void StartTracking()
{
    Log.Debug(TAG, "Starting Tracking");
    var component = new ComponentName(Context, Java.Lang.Class.FromType(typeof(LocationJobService)));

    //// This will run a service as normal on pre-Oreo targets, and use the Job Scheduler on Oreo+.
    //// Unfortunately we can't use it as there is no way to cancel it.
    //var locationJobIntent = new Intent(Context, typeof(LocationJobIntentService));
    //JobIntentService.EnqueueWork(Context, component, LocationJobIntentService.JobId, locationJobIntent);

    var deadline = 15 * 1000;
    var retryTime = 60 * 1000;

    var builder = new JobInfo.Builder(LocationJobService.JobId, component)
                                .SetMinimumLatency(0)
                                .SetOverrideDeadline(deadline)
                                .SetBackoffCriteria(retryTime, BackoffPolicy.Linear)
                                .SetRequiredNetworkType(NetworkType.Any);

    Log.Debug(TAG, "Scheduling LocationJobService...");

    var result = _jobScheduler.Schedule(builder.Build());

    if (result != JobScheduler.ResultSuccess)
    {
        Log.Warn(TAG, "Job Scheduler failed to schedule job!");
    }
}

And likewise, this is hit when a the user taps the button again to stop the location updates同样,当用户再次点击按钮以停止位置更新时,它会被点击

private void StopTracking()
{
    Log.Debug(TAG, "Stopping Tracking");

    _jobScheduler.CancelAll();
}

And here's my Job Service这是我的工作服务

    [Service(Name = "my.assembly.name.etc.LocationJobIntentService", Permission = "android.permission.BIND_JOB_SERVICE")]
    public class LocationJobService : JobService, ILocationListener
    {
        public const int JobId = 69;

        private const long LOCATION_UPDATE_INTERVAL = 5 * 1000;
        private const string NOTIFICATION_PRIMARY_CHANNEL = "default";
        private const int NOTIFICATION_SERVICE_ID = 261088;
        private static readonly string TAG = typeof(LocationJobService).FullName;

        private LocationManager _locationManager;
        private string _locationProvider;

        #region Base Overrides

        public override void OnCreate()
        {
            base.OnCreate();

            Log.Debug(TAG, "OnCreate called.");

            _locationManager = (LocationManager)GetSystemService(LocationService);

            //// TODO: Start the Foreground Service, and display the required notification.
            //var intent = new Intent(this, typeof(LocationJobService));

            //if (AndroidTargetHelper.IsOreoOrLater())
            //{
            //    StartForegroundService(intent);
            //}
            //else
            //{
            //    StartService(intent);
            //}

            //CreateForegroundNotification("It started! yay!");
        }

        /// <summary>Called to indicate that the job has begun executing.</summary>
        /// <remarks>This method executes on the main method. Ensure there is no blocking.</remarks>
        public override bool OnStartJob(JobParameters jobParameters)
        {
            Log.Debug(TAG, "OnStartJob called.");

            Task.Run(() =>
            {
                Log.Debug(TAG, "Starting location updates...");

                StartLocationUpdates();

                Log.Debug(TAG, "Location updates started. Stopping job.");

                JobFinished(jobParameters, true);
            });

            // TODO: We need a way to cancel the SERVICE (eg. Foreground notifications, etc) if required. This needs to happen on both logging out, and when the user disables tracking. 
            // Perhaps the notifications need to happen on the Fragment, as opposed to here in the job service?

            // Will this job be doing background work?
            return true;
        }

        /// <summary>This method is called if the system has determined that you must stop execution of your job even before you've had a chance to call JobFinished().</summary>
        public override bool OnStopJob(JobParameters @params)
        {
            // The OS has determined that the job must stop, before JobFinished() has been called.
            // TODO: Here, we want to restart the Foreground service to continue it's lifespan, but ONLY if the user didn't stop tracking.
            Log.Debug(TAG, "OnStopJob called.");

            // Reschedule the job?
            return false;
        }

        public override void OnDestroy()
        {
            base.OnDestroy();

            // TODO: StopForeground(true);

            Log.Debug(TAG, "OnDestroy called.");
        }

        #endregion

        #region ILocationListener Members

        public void OnLocationChanged(Location location)
        {
            Log.Debug(TAG, String.Format("Location changed to '{0}, {1}'", location.Latitude, location.Longitude));

            StopLocationUpdates();

            // TODO: Do we finish the job here? Or right after we ASK for the location updates? (job params would have to be accessible)
            //JobFinished(_jobParameters, true);

            // THIS IS WHERE THE LOCATION IS POSTED TO THE API
            await PostLocationPing(location.Latitude, location.Longitude);
        }

        public void OnProviderDisabled(string provider)
        {
            Log.Debug(TAG, String.Format("Provider '{0}' disabled by user.", provider));

            // TODO: Get new provider via StartLocationupdates()?
        }

        public void OnProviderEnabled(string provider)
        {
            Log.Debug(TAG, String.Format("Provider '{0}' enabled by user.", provider));

            // TODO: Get new provider via StartLocationupdates()?
        }

        public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras)
        {
            Log.Debug(TAG, String.Format("Provider '{0}' availability has changed to '{1}'.", provider, status.ToString()));

            // TODO: Get new provider via StartLocationupdates()?
        }
        #endregion

        private async Task<string> PostLocationPing(double latitude, double longitude, bool isFinal = false)
        {
            var client = new NetworkClient();
            var locationPing = new LocationPingDTO()
            {
                TimestampUtc = DateTime.UtcNow,
                Latitude = latitude,
                Longitude = longitude,
            };

            return await client.PostLocationAndGetSiteName(locationPing);
        }

        #region Helper Methods

        private void StartLocationUpdates()
        {
            // TODO: Specify the criteria - in our case we want accurate enough, without burning through the battery
            var criteria = new Criteria
            {
                Accuracy = Accuracy.Fine,
                SpeedRequired = false,
                AltitudeRequired = false,
                BearingRequired = false,
                CostAllowed = false,
                HorizontalAccuracy = Accuracy.High
            };

            // Get provider (GPS, Network, etc)
            _locationProvider = _locationManager.GetBestProvider(criteria, true);

            // Start asking for locations, at a specified time interval (eg. 5 seconds to get an accurate reading)
            // We don't use RequestSingleUpdate because the first location accuracy is pretty shitty.
            _locationManager.RequestLocationUpdates(_locationProvider, LOCATION_UPDATE_INTERVAL, 0.0f, this);
        }

        private void StopLocationUpdates()
        {
            Log.Debug(TAG, "StopLocationUpdates called.");

            _locationManager.RemoveUpdates(this);
        }

        #endregion
    }
}

For those still trying to achieve this, there are many issues regarding continuous tracking.对于那些仍在努力实现这一目标的人来说,在持续跟踪方面存在许多问题。 One article you should read regarding this is https://developer.android.com/training/location/background您应该阅读的一篇关于此的文章是https://developer.android.com/training/location/background

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM