Xamarin Android FusedLocationProvider白名单

时间:2018-07-26 22:34:37

标签: android service xamarin.android foreground-service fusedlocationproviderclient

所以首先是一些背景。我的应用程序应该在运行时和不运行时进行定期的位置更新。为了在关闭应用程序时实现此目的,我将服务移到了前台。我已将我的应用列入白名单,并添加了部分唤醒锁。我的应用程序正在运行的手机正在运行Android 7.0。现在,无论打入白名单模式还是打lock睡模式,无论何时进入我的位置,我的位置更新都变得越来越少。但是我的服务仍在运行,我记录每个事件,以便查看服务是否被杀死。

给我的印象是,当设备开始移动时它将唤醒,但是只有在按下锁定按钮时它才能唤醒。由于位置更新是此应用程序的主要目的,因此我需要确保位置更新不断出现。

我们围绕example构建了服务。

服务代码在此处

[Service(Enabled = true, Exported = true)]
public class LocationUpdatesService : Service
{
    [InjectService] public ILocationService LocationsService { get; set; }
    [InjectService] public IConfigService ConfigService { get; set; }

    public static string Tag = typeof(LocationUpdatesService).Name;

    public new const string PackageName = AppConstants.AccountType + ".services.location";
    public const string ActionBroadcast = PackageName + ".broadcast";
    public const string ExtraLocation = PackageName + ".location";
    public const string ExtraStartedFromNotification = PackageName + ".notification.started_from";
    public const string LocationWakeLock = PackageName + ".location.wakelock";
    public static DateTime LastLocationTime { get; set; }

    private IBinder Binder { get; }

    private LocationRequest LocationRequest { get; set; }
    private FusedLocationProviderClient FusedLocationProviderClient { get; set; }
    private LocationCallback LocationCallback { get; set; }
    private Handler ServiceHandler { get; set; }
    private LocationTrackingNotification Notification { get; set; }
    private PowerManager.WakeLock WakeLock { get; set; }

    private Android.Locations.Location Location { get; set; }
    private Android.Locations.Location CurrentBestLocation { get; set; }

    private const int TooOldLocationDelta = 1000 * 60 * 2;

    /// <summary>
    /// Constructor
    /// </summary>
    public LocationUpdatesService()
    {
        Binder = new LocalBinder(this);
        TinyIoC.Injector.Inject(this);
    }

    /// <inheritdoc />
    /// <summary>
    /// On create we setup the service and get the last chached location before registering for location updates.
    /// </summary>
    public override void OnCreate()
    {
        FusedLocationProviderClient = LocationServices.GetFusedLocationProviderClient(this);
        LocationCallback = new MLocationCallback(this);

        CreateLocationRequest();
        GetLastLocation();

        Notification = new LocationTrackingNotification(this, Locations.GetLocationText(Location));

        HandlerThread handlerThread = new HandlerThread(Tag);
        handlerThread.Start();
        ServiceHandler = new Handler(handlerThread.Looper);
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {

        //base.OnStartCommand(intent, flags, startId);
        Log.Info(Tag, "Service Started");
        Logging.Log("Location Service Started");
        PowerManager pm = (PowerManager) GetSystemService(Context.PowerService);
        WakeLock = pm.NewWakeLock(WakeLockFlags.Partial, LocationWakeLock);
        WakeLock.Acquire();

        StartForeground(LocationTrackingNotification.NotificationId, Notification.GenerateNotification());
        //Make it stick to the notification panel so it is less prone to get cancelled by the Operating System.
        return StartCommandResult.Sticky;
    }

    public override IBinder OnBind(Intent intent)
    {
        // Called when a client (MainActivity in case of this sample) comes to the foreground
        // and binds with this service. The service should cease to be a foreground service
        // when that happens.
        Log.Info(Tag, "in onBind()");
        Logging.Log("Location service bound, cease to be foreground");
        StopForeground(true);
        return Binder;
    }

    public override void OnRebind(Intent intent)
    {
        // Called when a client (MainActivity in case of this sample) returns to the foreground
        // and binds once again with this service. The service should cease to be a foreground
        // service when that happens.
        Log.Info(Tag, "in onRebind()");
        Logging.Log("Location Service rebound, cease to be foreground");
        StopForeground(true);
        base.OnRebind(intent);
    }

    public override bool OnUnbind(Intent intent)
    {
        Logging.Log("Location Service last client unbound from service");
        Log.Info(Tag, "Last client unbound from service");
        if (Locations.RequestingLocationUpdates(this))
        {
            Logging.Log("Location Service moving to foreground");
            Log.Info(Tag, "Starting foreground service");
            StartForeground(LocationTrackingNotification.NotificationId, Notification.GenerateNotification());
        }

        return true;
    }

    public override void OnDestroy()
    {
        Logging.Log("Location Service on destory");
        WakeLock.Release();
        ServiceHandler.RemoveCallbacksAndMessages(null);
    }

    public void RequestLocationUpdates()
    {
        if (!Locations.RequestingLocationUpdates(this))
        {
            Logging.Log("Location Service requesting location updates");
            Log.Info(Tag, "Requesting location updates");
            Locations.SetRequestingLocationUpdates(this, true);
            StartService(new Intent(ApplicationContext, typeof(LocationUpdatesService)));
            StartForeground(LocationTrackingNotification.NotificationId, Notification.GenerateNotification());

            try
            {
                FusedLocationProviderClient.RequestLocationUpdates(LocationRequest, LocationCallback, Looper.MyLooper());
                //ActivityRecognitionClient.RequestActivityUpdates()
            }
            catch (SecurityException ex)
            {
                Locations.SetRequestingLocationUpdates(this, false);
                Logging.Log("Location Service could not request updates");
                Log.Error(Tag, ex, "Lost location permission. Could not request updates.");
                FirebaseCrash.Report(ex);
            }
        }
    }

    public void RemoveLocationUpdates()
    {
        Logging.Log("Location Service remove location updates");
        Log.Info(Tag, "Removing location updates");
        try
        {
            FusedLocationProviderClient.RemoveLocationUpdates(LocationCallback);
            Locations.SetRequestingLocationUpdates(this, false);
            StopSelf();
        }
        catch (SecurityException e)
        {
            Locations.SetRequestingLocationUpdates(this, true);
            FirebaseCrash.Report(e);
            Logging.Log("Location Service could not request updates");
            Log.Error(Tag, e, "Lost location permission. Could not request updates.");
        }
    }

    private async Task OnNewLocation(Android.Locations.Location location)
    {
        Logging.Log("Location Service New Location:" + location);
        Log.Info(Tag, "New Location:" + location);
        Location = location;

        var intent = new Intent(ActionBroadcast);
        intent.PutExtra(ExtraLocation, location);
        SendBroadcast(intent);

        if (!IsBetterLocation(location, CurrentBestLocation)) return;
        Logging.Log("Location Service is better location");
        Log.Info(Tag, "Location looks good");
        CurrentBestLocation = location;

        Notification.UpdateData(Locations.GetLocationText(location));
        Intent i = new Intent();
        i.SetAction("LocationData");
        i.PutExtra("Accuracy_int", (int)location.Accuracy);
        i.PutExtra("Latitude", location.Latitude);
        i.PutExtra("Longitude", location.Longitude);
        // Notify anyone listening for broadcasts about the new location.
        SendBroadcast(i);  // this broadcast will be received by mainactivity

        // run async task
        await UpdateLocation(location);
    }

    /// <summary>
    /// Check if the latest location is better than the previous one.
    /// </summary>
    /// <param name="location">the current location update</param>
    /// <param name="currentBestLocation">the last accepted location update</param>
    /// <returns></returns>
    private static bool IsBetterLocation(Android.Locations.Location location, Android.Locations.Location currentBestLocation)
    {
        // A new location is always better than none.
        if (currentBestLocation == null)
        {
            return true;
        }

        // Check if the new location is newer or older.
        var timeDelta = location.Time - currentBestLocation.Time;
        var isSignificantlyNewer = timeDelta > TooOldLocationDelta;
        var isSignificantlyOlder = timeDelta < -TooOldLocationDelta;
        var isNewer = timeDelta > 0;

        // If its been more than two min since the current location, use the new location because the user has likely moved.
        if (isSignificantlyNewer)
        {
            return true;
        }

        // If the location is more than two min older, it must be worse.
        if (is significantly older)
        {
            return false;
        }

        // Check if the new location is more or less accurate.
        var accuracyDelta = (int)(location.Accuracy - currentBestLocation.Accuracy);
        var isLessAccurate = accuracyDelta > 0;
        var isMoreAccurate = accuracyDelta < 0;
        var isSignificantlyLessAccurate = accuracyDelta > 20;

        // Determin location quality using a combination of timeliness and accuracy.
        if (isMoreAccurate)
        {
            return true;
        }
        if (isNewer && !isLessAccurate)
        {
            return true;
        }

        return isNewer && !isSignificantlyLessAccurate;
    }

    private void CreateLocationRequest()
    {
        LocationRequest = LocationRequest.Create();
        LocationRequest.SetInterval(Locations.MaxTimeOnUpdates(this));
        LocationRequest.SetFastestInterval(Locations.MinTimeOnUpdates(this));
        LocationRequest.SetPriority(LocationRequest.PriorityHighAccuracy);
        LocationRequest.SetSmallestDisplacement(0);
    }

    private void GetLastLocation()
    {
        try
        {
            FusedLocationProviderClient.GetLastLocationAsync().ContinueWith(
                location =>
                {
                    if (location.IsCompleted && location.Result != null)
                    {
                        Location = location.Result;
                    }
                    else
                    {
                        Logging.Log("Location Service failed to get location");
                        Log.Warn(Tag, "Failed to get location");
                    }
                });
        }
        catch (SecurityException e)
        {
            FirebaseCrash.Report(e);
            Log.Error(Tag, "Lost location permission", e);
        }
    }

    private async Task UpdateLocation(Android.Locations.Location location)
    {
        if (LocationsService != null && location != null)
        {
            if (LastLocationTime == null ||
                (DateTime.Now - LastLocationTime).TotalMilliseconds >= ConfigService.GetMinTime())
            {
                LastLocationTime = DateTime.Now;

                try
                {
                    var item = new LocationUpdate()
                    {
                        Latitude = location.Latitude,
                        Longitude = location.Longitude,
                        Accuracy = location.Accuracy,
                        Altitude = location.Altitude,
                        Bearing = location.Bearing,
                        Speed = location.Speed
                    };

                    await LocationsService.HandleLocationUpdate(item);
                }
                catch (System.Exception ex)
                {
                    FirebaseCrash.Report(ex);
                }
            }
        }
    }

    public class LocalBinder : Binder
    {
        public LocationUpdatesService Service { get; }

        public LocalBinder(LocationUpdatesService service)
        {
            Service = service;
        }
    }

    private class MLocationCallback : LocationCallback
    {
        private LocationUpdatesService LocationUpdatesService { get; }

        public MLocationCallback(LocationUpdatesService locationUpdatesService)
        {
            LocationUpdatesService = locationUpdatesService;
        }

        public override async void OnLocationResult(LocationResult result)
        {
            base.OnLocationResult(result);
            await LocationUpdatesService.OnNewLocation(result.LastLocation);
        }
    }


}

现在Logger只需将行写到文本文件中,这样我就可以查看我的服务是否被杀死。日志文件很长,因此我已将其缩减为最近的一些活动。

07/26/2018 18:13:52 : Location Service New Location:Location[fused 53.411345,-2.995635 acc=10 et=+7d7h34m5s377ms alt=-3.3 vel=1.4381338 bear=217.0 {Bundle[mParcelledData.dataSize=52]}]
07/26/2018 18:13:52 : Location Service is better location
07/26/2018 18:14:54 : Location Service New Location:Location[fused 53.411040,-2.996565 acc=17 et=+7d7h35m6s782ms]
07/26/2018 18:14:54 : Location Service is better location
07/26/2018 18:15:56 : Location Service New Location:Location[fused 53.411328,-2.994868 acc=10 et=+7d7h36m9s27ms alt=-3.3 vel=0.07566417 bear=185.0 {Bundle[mParcelledData.dataSize=52]}]
07/26/2018 18:15:56 : Location Service is better location
07/26/2018 18:16:58 : Location Service New Location:Location[fused 53.410012,-2.996663 acc=104 et=+7d7h37m10s977ms]
07/26/2018 18:17:59 : Location Service New Location:Location[fused 53.409753,-2.996922 acc=17 et=+7d7h38m12s69ms]
07/26/2018 18:17:59 : Location Service is better location
07/26/2018 18:18:59 : Location Service New Location:Location[fused 53.408499,-2.997294 acc=116 et=+7d7h39m12s11ms]
07/26/2018 18:19:30 : Location Service New Location:Location[fused 53.410023,-2.996088 acc=10 et=+7d7h39m42s928ms alt=9.3 vel=0.3963361 bear=159.0 {Bundle[mParcelledData.dataSize=52]}]
07/26/2018 18:19:30 : Location Service is better location
07/26/2018 18:42:51 : Location Service New Location:Location[fused 53.372827,-2.934333 acc=13 et=+7d8h3m4s993ms alt=25.2 vel=0.15338722 bear=88.0 {Bundle[mParcelledData.dataSize=52]}]
07/26/2018 18:42:52 : Location Service is better location
07/26/2018 19:36:00 : Location Service New Location:Location[fused 53.347018,-2.894317 acc=15 et=+7d8h56m13s621ms alt=72.30000305175781 {Bundle[mParcelledData.dataSize=52]}]
07/26/2018 19:36:00 : Location Service is better location
07/26/2018 21:34:47 : Location Service New Location:Location[fused 53.347017,-2.894317 acc=15 et=+7d10h55m1s36ms alt=72.30000305175781 {Bundle[mParcelledData.dataSize=52]}]
07/26/2018 21:34:47 : Location Service is better location
07/26/2018 22:17:42 : Location Service rebound, cease to be foreground
07/26/2018 22:17:42 : Location Service New Location:Location[fused 53.347018,-2.894318 acc=15 et=+7d11h37m56s1ms alt=72.30000305175781 {Bundle[mParcelledData.dataSize=52]}]

因此,我们可以看到在22:17:42我单击了锁定按钮,并且随着应用程序的唤醒,服务移出了前台。但是在此之前,我一直在使用该应用程序,但这并没有导致它唤醒,再次给人留下的印象是,移动设备会打do睡。

我已经看到并阅读了许多有关此问题的问题,其中许多人指出移动中的设备应将其唤醒。我可以使用一些指导来解决这个问题,我知道我的服务并没有被杀死,就像融合的位置提供者要入睡一样。我有什么办法可以使位置更新进来,即使间隔减少了?我是否认为移动设备应该使应用程序退出打ze模式是否正确?

同样,我知道还有其他类似的问题,但是答案都各不相同,很多人都说,您需要的只是唤醒锁并将应用列入白名单,但这似乎仍然行不通。任何指导是非常感谢。

0 个答案:

没有答案