所以首先是一些背景。我的应用程序应该在运行时和不运行时进行定期的位置更新。为了在关闭应用程序时实现此目的,我将服务移到了前台。我已将我的应用列入白名单,并添加了部分唤醒锁。我的应用程序正在运行的手机正在运行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模式是否正确?
同样,我知道还有其他类似的问题,但是答案都各不相同,很多人都说,您需要的只是唤醒锁并将应用列入白名单,但这似乎仍然行不通。任何指导是非常感谢。