Android Job Scheduler - 按周期性调度作业直到取消?

时间:2018-03-04 21:04:13

标签: android xamarin xamarin.android android-service android-jobscheduler

随着Oreo / Android 8.0的发布,我想用预定的工作替换我的GPS前台服务。

我看过Xamarin Example for Job Scheduler,,说实话,我觉得很混乱。

以下是我想要做的所有事情:

  1. 用户点击按钮
  2. 创建并安排作业。
  3. 立即调用作业,之后每30秒调用一次,直到用户再次点击按钮
  4. 该作业将取代我的GPS服务(实现ILocationListener)。

    任何人都可以帮助澄清我需要做些什么才能实现这一目标吗?

    我尝试过.SetPeriodic(30000),它似乎不适用于运行Oreo的测试设备。

    该示例使用.SetMinimumLatency()和.SetOverrideDeadline()来设置某种计划范围。我可以将这两个设置为30000(30秒)以达到我的需要吗? 预定工作的最短时间似乎还有15分钟。

    该示例使用处理程序并传递参数等。这是Job Scheduler所必需的吗?基本的示例项目有很多,我只是对这些要求感到困惑。

    提前致谢,希望有人可以帮我清理一下。

    干杯。

    编辑 - 示例代码

    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!");
        }
    }
    

    同样,当用户再次点击按钮以停止位置更新时,会出现这种情况

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

    这是我的工作服务

        [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
        }
    }
    

1 个答案:

答案 0 :(得分:0)

对于那些仍在努力实现这一目标的人来说,关于连续跟踪存在许多问题。您应该阅读与此相关的一篇文章https://developer.android.com/training/location/background