我正在尝试制作一个xamarin应用程序,带有android和ios。 我已经制作了一项服务,以间隔时间广播用户位置,但每次尝试广播(使用 SendLocationToServer() )时,它都会使应用程序崩溃。
[Service]
public class LocationService : Service {
public string role = "";
public string auth = "";
System.Timers.Timer _timer;
const string foregroundChannelId = "location_service_channel";
const int serviceId = 209345;
const string channelId = "location_service_channel";
const string channelName = "Location Service";
const string endpoint = @"REMOVED FOR STACK OVERFLOW POST";
public override IBinder OnBind(Intent intent) {
return null;
}
public override void OnCreate() {
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O) {
var channel = new NotificationChannel(channelId, channelName, NotificationImportance.Default) {
Description = "Location Service is running"
};
notificationManager.CreateNotificationChannel(channel);
}
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) {
StartForeground(serviceId, CreateNotification());
auth = intent.GetStringExtra("authToken");
role = intent.GetStringExtra("role");
_timer = new System.Timers.Timer(10000);
_timer.Elapsed += async (sender, e) => {
try {
await SendLocationToServer();
} catch (Exception ex) {
// Log exception
Toast.MakeText(Android.App.Application.Context, "Timer Toast " + ex.Message, ToastLength.Short).Show();
}
};
_timer.AutoReset = true;
_timer.Start();
return StartCommandResult.Sticky;
}
private Notification CreateNotification() {
var intent = new Intent(MainActivity.Instance, typeof(MainActivity));
intent.AddFlags(ActivityFlags.SingleTop);
intent.PutExtra("Title", "Message");
var pendingIntent = PendingIntent.GetActivity(MainActivity.Instance, 0, intent, PendingIntentFlags.UpdateCurrent);
var notificationBuilder = new Notification.Builder(MainActivity.Instance)
.SetContentTitle(channelName)
.SetContentText("Broadcasting location in the background")
.SetSmallIcon(Resource.Drawable.Icon_small)
.SetOngoing(true)
.SetContentIntent(pendingIntent);
if (global::Android.OS.Build.VERSION.SdkInt >= BuildVersionCodes.O) {
NotificationChannel notificationChannel = new NotificationChannel(foregroundChannelId, "Title", NotificationImportance.High);
notificationChannel.Importance = NotificationImportance.High;
notificationChannel.EnableLights(true);
notificationChannel.EnableVibration(true);
notificationChannel.SetShowBadge(true);
notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300 });
var notificationManager = MainActivity.Instance.GetSystemService(Context.NotificationService) as NotificationManager;
if (notificationManager != null) {
notificationBuilder.SetChannelId(foregroundChannelId);
notificationManager.CreateNotificationChannel(notificationChannel);
}
}
return notificationBuilder.Build();
//try {
// var notification = new Notification.Builder(this, channelId)
// .SetContentTitle(channelName)
// .SetContentText("Broadcasting location in the background")
// .SetSmallIcon(Resource.Drawable.Icon_small)
// .SetOngoing(true) // Keep the notification active
// .Build();
// return notification;
//}
//catch(Exception e) {
// Toast.MakeText(Android.App.Application.Context, "Error: " + e.Message, ToastLength.Short).Show();
// return null;
//}
}
private async Task SendLocationToServer() {
if (!string.IsNullOrEmpty(role) && !string.IsNullOrEmpty(auth)) {
using (var client = new HttpClient()) {
try {
var location = await Geolocation.GetLocationAsync(new GeolocationRequest(GeolocationAccuracy.Best));
if (location != null) {
var gpsData = new {
AuthToken = auth,
Role = role,
Longitude = location.Longitude,
Latitude = location.Latitude
};
var jsonContent = JsonConvert.SerializeObject(gpsData);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
var response = await client.PostAsync(endpoint + "/logbeacon", content);
//i++;
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) {
Toast.MakeText(Android.App.Application.Context, "Unauthorized. Please login again", ToastLength.Short).Show();
} else if (response.StatusCode == System.Net.HttpStatusCode.OK) {
Toast.MakeText(Android.App.Application.Context, "Check broadcast 3", ToastLength.Short).Show();
} else {
Toast.MakeText(Android.App.Application.Context, "Connection Error. ", ToastLength.Short).Show();
}
} else {
}
} catch (Exception ex) {
// Handle exception
Toast.MakeText(Android.App.Application.Context, "Check broadcast 4 " + ex.Message, ToastLength.Short).Show();
await client.GetAsync(endpoint + "/test/" + ex.Message); // TESTING
}
}
}
}
public override void OnDestroy() {
_timer?.Stop();
_timer?.Dispose();
base.OnDestroy();
}
}
实施者:
[assembly: Xamarin.Forms.Dependency(typeof(LocationServiceImplementation))]
namespace MyAppName.Droid
{
public class LocationServiceImplementation : ILocationService {
public void StartLocationService(string role, string auth) {
Toast.MakeText(Android.App.Application.Context, "Starting", ToastLength.Short).Show();
var intent = new Intent(MainActivity.Instance, typeof(LocationService));
intent.PutExtra("auth", auth);
intent.PutExtra("role", role);
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O) {
MainActivity.Instance.StartForegroundService(intent);
} else {
MainActivity.Instance.StartService(intent);
}
}
public void StopLocationService() {
Toast.MakeText(Android.App.Application.Context, "Stopping", ToastLength.Short).Show();
throw new NotImplementedException();
}
}
在Manifest和MainActivity中设置和请求权限:包括粗略位置、精细位置和始终位置。请求的顺序花了一些时间才弄清楚,但现在请求正确了。
这个函数在android项目的MainActivity中:
public async Task GetLocationConsent() {
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
if (status == PermissionStatus.Denied || status == PermissionStatus.Unknown) {
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
}
// Only request background location on Android 10 (API level 29) or higher
if (status == PermissionStatus.Granted && Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Q) {
var backgroundStatus = await Permissions.CheckStatusAsync<Permissions.LocationAlways>();
if (backgroundStatus == PermissionStatus.Denied || backgroundStatus == PermissionStatus.Unknown) {
await Permissions.RequestAsync<Permissions.LocationAlways>();
// Check again if permission is not granted, then navigate to settings
backgroundStatus = await Permissions.CheckStatusAsync<Permissions.LocationAlways>();
if (backgroundStatus != PermissionStatus.Granted) {
// Inform the user to manually enable background location in settings
Toast.MakeText(Application.Context, "Please enable Location Always in App Permissions.", ToastLength.Long).Show();
var intent = new Intent(Android.Provider.Settings.ActionApplicationDetailsSettings);
intent.AddFlags(ActivityFlags.NewTask);
var uri = Android.Net.Uri.FromParts("package", Application.Context.PackageName, null);
intent.SetData(uri);
Application.Context.StartActivity(intent);
// Show a message to guide the user
Toast.MakeText(Application.Context, "Please enable Background Location in App Permissions.", ToastLength.Long).Show();
}
}
}
}
我花了很多时间试图破解这个难题,以至于我看不清,工作的截止日期很快就到了……任何帮助将不胜感激。请。
我正在尝试在 Android 上运行后台服务,定期广播用户位置,但当我尝试时应用程序崩溃了。
显然问题是“异步”。一旦我改变了
var location = await Geolocation.GetLocationAsync(new GeolocationRequest(GeolocationAccuracy.Best));
进入这个
var location = await Task.Run(async () => await Geolocation.GetLocationAsync(new GeolocationRequest(GeolocationAccuracy.Best)));
并改变了
SendLocationToServer()
为了不再异步,代码运行顺利。我不知道为什么代码不喜欢运行异步,但是将该异步部分隔离到单个任务中,解决了崩溃。