Xamarin 表单,当手机 phone 被锁定时前台服务不工作
Xamarin forms, foreground service does not work when the mobile phone is locked
我正在创建一个每 10 分钟向服务器发送一次用户位置信息的应用程序。为了功能的目的,即使应用程序在后台或手机被锁定,我使用了前台服务。当应用程序直接从 Visual Studio 构建到模拟器或通过 USB 构建到真实设备时,即使设备被锁定,一切也能正常工作。 设备断开连接或执行发布构建的那一刻 - 当设备被锁定时,前台服务不会发送信息。有没有人有这方面的经验?谢谢
共享代码:
MainPage.xaml.cs:
if (MainViewModel.WorkingActions.Contains(buttonText))
{
DependencyService.Get<IBackgroundService>().StartService(1);
MessagingCenter.Subscribe<Application>(this, "Ping", async (sender) =>
{
await PingWorkingToServer();
});
}
else
{
DependencyService.Get<IBackgroundService>().StopService();
}
IBackgroundService.cs
public interface IBackgroundService
{
// 1 = logged in, 2 = not logged in
void StartService(int pingType);
void StopService();
}
Android:
AndroidServiceHelper.cs
internal class AndroidServiceHelper : IBackgroundService
{
private static readonly Context context = global::Android.App.Application.Context;
private static readonly Intent intent = new Intent(context, typeof(DependentService));
private bool isRunning = false;
// 1 = logged in 2 = not logged in
public void StartService(int pingType)
{
intent.PutExtra("PingType", pingType);
if(!isRunning)
{
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
context.StartForegroundService(intent);
}
else
{
context.StartService(intent);
}
isRunning = true;
}
}
public void StopService()
{
context.StopService(intent);
isRunning = false;
}
}
DependentService.cs
[Service(Enabled = true)]
public class DependentService : Service
{
private Handler handler;
private Action runnable;
private bool isStarted;
private int DELAY_BETWEEN_MESSAGES;
private readonly int NOTIFICATION_SERVICE_ID = 1001;
private readonly int NOTIFICATION_PING_ID = 1002;
private readonly string NOTIFICATION_CHANNEL_ID = "1003";
private readonly string NOTIFICATION_CHANNEL_NAME = "MyChannel";
private const int ONE_MINUTE_MILIS = 60000;
private int PingType = -1;
public override void OnCreate()
{
base.OnCreate();
var minutes = Xamarin.Essentials.Preferences.Get("PingTime", 10);
DELAY_BETWEEN_MESSAGES = minutes * ONE_MINUTE_MILIS;
handler = new Handler();
runnable = new Action(() =>
{
if (isStarted)
{
DispatchNotificationThatPingIsGenerated();
handler.PostDelayed(runnable, DELAY_BETWEEN_MESSAGES);
}
});
}
private void DispatchNotificationThatPingIsGenerated()
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.dochazka_icon)
.SetContentTitle("Ukládání pozice")
.SetContentText("Právě odesílám vaši polohu.")
.SetAutoCancel(true);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(NOTIFICATION_PING_ID, notificationBuilder.Build());
if (PingType == 1)
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "Ping");
}
else if (PingType == 2)
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "PingLocally");
}
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
if (isStarted)
{
// service is already started
}
else
{
PingType = intent.GetIntExtra("PingType", -1);
CreateNotificationChannel();
DispatchNotificationThatServiceIsRunning();
handler.PostDelayed(runnable, DELAY_BETWEEN_MESSAGES);
isStarted = true;
}
return StartCommandResult.Sticky;
}
//start a foreground notification to keep alive
private void DispatchNotificationThatServiceIsRunning()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.dochazka_icon)
.SetSound(null)
.SetChannelId(NOTIFICATION_CHANNEL_ID)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Ukládání pozice")
.SetContentText("Jste v práci, aplikace pravidelně ukládá polohu.")
.SetOngoing(true);
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
public override void OnDestroy()
{
// Stop the handler.
handler.RemoveCallbacks(runnable);
// Remove the notification from the status bar.
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Cancel(NOTIFICATION_SERVICE_ID);
isStarted = false;
base.OnDestroy();
}
private void CreateNotificationChannel()
{
//Notification Channel
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(notificationChannel);
}
}
AndroidManifest.xml
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
<application android:label="Docházka lokalita" android:theme="@style/MainTheme" android:icon="@mipmap/launcher_foreground">
<service android:name=".DependentService" android:foregroundServiceType="location" />
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
可以使用wakeloke保持cpu熄屏后的唤醒状态
或锁定,以便服务继续 运行。
喜欢:
public override void OnDestroy()
{
if (wakeLock != null)
{
wakeLock.Release();
wakeLock = null;
}
...
base.OnDestroy();
}
我正在创建一个每 10 分钟向服务器发送一次用户位置信息的应用程序。为了功能的目的,即使应用程序在后台或手机被锁定,我使用了前台服务。当应用程序直接从 Visual Studio 构建到模拟器或通过 USB 构建到真实设备时,即使设备被锁定,一切也能正常工作。 设备断开连接或执行发布构建的那一刻 - 当设备被锁定时,前台服务不会发送信息。有没有人有这方面的经验?谢谢
共享代码:
MainPage.xaml.cs:
if (MainViewModel.WorkingActions.Contains(buttonText))
{
DependencyService.Get<IBackgroundService>().StartService(1);
MessagingCenter.Subscribe<Application>(this, "Ping", async (sender) =>
{
await PingWorkingToServer();
});
}
else
{
DependencyService.Get<IBackgroundService>().StopService();
}
IBackgroundService.cs
public interface IBackgroundService
{
// 1 = logged in, 2 = not logged in
void StartService(int pingType);
void StopService();
}
Android:
AndroidServiceHelper.cs
internal class AndroidServiceHelper : IBackgroundService
{
private static readonly Context context = global::Android.App.Application.Context;
private static readonly Intent intent = new Intent(context, typeof(DependentService));
private bool isRunning = false;
// 1 = logged in 2 = not logged in
public void StartService(int pingType)
{
intent.PutExtra("PingType", pingType);
if(!isRunning)
{
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
context.StartForegroundService(intent);
}
else
{
context.StartService(intent);
}
isRunning = true;
}
}
public void StopService()
{
context.StopService(intent);
isRunning = false;
}
}
DependentService.cs
[Service(Enabled = true)]
public class DependentService : Service
{
private Handler handler;
private Action runnable;
private bool isStarted;
private int DELAY_BETWEEN_MESSAGES;
private readonly int NOTIFICATION_SERVICE_ID = 1001;
private readonly int NOTIFICATION_PING_ID = 1002;
private readonly string NOTIFICATION_CHANNEL_ID = "1003";
private readonly string NOTIFICATION_CHANNEL_NAME = "MyChannel";
private const int ONE_MINUTE_MILIS = 60000;
private int PingType = -1;
public override void OnCreate()
{
base.OnCreate();
var minutes = Xamarin.Essentials.Preferences.Get("PingTime", 10);
DELAY_BETWEEN_MESSAGES = minutes * ONE_MINUTE_MILIS;
handler = new Handler();
runnable = new Action(() =>
{
if (isStarted)
{
DispatchNotificationThatPingIsGenerated();
handler.PostDelayed(runnable, DELAY_BETWEEN_MESSAGES);
}
});
}
private void DispatchNotificationThatPingIsGenerated()
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.dochazka_icon)
.SetContentTitle("Ukládání pozice")
.SetContentText("Právě odesílám vaši polohu.")
.SetAutoCancel(true);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(NOTIFICATION_PING_ID, notificationBuilder.Build());
if (PingType == 1)
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "Ping");
}
else if (PingType == 2)
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "PingLocally");
}
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
if (isStarted)
{
// service is already started
}
else
{
PingType = intent.GetIntExtra("PingType", -1);
CreateNotificationChannel();
DispatchNotificationThatServiceIsRunning();
handler.PostDelayed(runnable, DELAY_BETWEEN_MESSAGES);
isStarted = true;
}
return StartCommandResult.Sticky;
}
//start a foreground notification to keep alive
private void DispatchNotificationThatServiceIsRunning()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.dochazka_icon)
.SetSound(null)
.SetChannelId(NOTIFICATION_CHANNEL_ID)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Ukládání pozice")
.SetContentText("Jste v práci, aplikace pravidelně ukládá polohu.")
.SetOngoing(true);
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
public override void OnDestroy()
{
// Stop the handler.
handler.RemoveCallbacks(runnable);
// Remove the notification from the status bar.
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Cancel(NOTIFICATION_SERVICE_ID);
isStarted = false;
base.OnDestroy();
}
private void CreateNotificationChannel()
{
//Notification Channel
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(notificationChannel);
}
}
AndroidManifest.xml
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
<application android:label="Docházka lokalita" android:theme="@style/MainTheme" android:icon="@mipmap/launcher_foreground">
<service android:name=".DependentService" android:foregroundServiceType="location" />
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
可以使用wakeloke保持cpu熄屏后的唤醒状态 或锁定,以便服务继续 运行。 喜欢:
public override void OnDestroy()
{
if (wakeLock != null)
{
wakeLock.Release();
wakeLock = null;
}
...
base.OnDestroy();
}