Xamarin/Maui应用仍会进入休眠状态,尽管已告知不要这样做。

huangapple go评论90阅读模式
英文:

Xamarin/Maui app still goes to sleep even though told not to

问题

My .Net Maui app,在 Android (API 33) 上运行,需要保持运行。我曾认为使用 Android 前台服务可以解决这个问题,但现在发现 Android 在几分钟后会将应用程序及其前台服务休眠。我相信它正在休眠,因为应用程序应该定期更新的日志文件停止更新,只有当我解锁手机时,应用程序才会“唤醒”并开始记录。如果我在真实设备上运行应用程序,并启用无线调试,连接到我的开发笔记本电脑,应用程序会保持唤醒状态,但如果不进行调试,它会休眠。
我尝试了两种方法,但都无法保持应用程序的运行:

  1. 我告诉 Android 不要对应用程序应用电池优化,方法是进入设置 - > 应用程序 - > 我的应用程序名称 - > 电池 - > 选择不受限制
  2. 我告诉 Android 将我的应用程序设置为永不休眠的应用程序,方法是进入设置 - > 电池与设备护理 - > 电池 - > 背景使用限制 - > 从不休眠的应用程序 - > 添加我的应用程序名称(请注意,这会撤消第1步的操作)

是否有其他方法可以保持应用程序的前台服务运行?

英文:

My .Net Maui app, running on Android (API 33), needs to stay running. I had thought that using an Android Foreground Service took care of this, but have now discovered that Android sends the app and its foreground service to sleep after a few minutes. Well, I believe it is sleeping, as the log file that the app should update regularly stops getting updated, and the app "wakes up" and starts logging again if I unlock the phone. If I run the app on a real device and with wireless debugging running, connected to my dev laptop, the app stays awake, but goes to sleep if not debugging.
I have tried 2 approaches and neither is keeping my app running:

  1. I told Android to not apply battery optimisations to the app, by going into Settings -> Apps -> My App Name -> Battery -> select Unrestricted
  2. I told Android to set my app as a never-sleeping app, by going into Settings -> Battery and device care -> Battery -> Background usage limits -> Never sleeping apps -> add My App Name (NOTE this undoes what was done in step 1)

Is there some other way to keep the app's foreground service running?

答案1

得分: 1

由于您的请求是代码,我将只翻译代码的部分。以下是您提供的代码的翻译:

AndroidManifest.xml
	<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
	<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
	<uses-permission android:name="android.permission.WAKE_LOCK" />
    var intent = new Intent(Android.App.Application.Context, typeof(ScreenOffService));
    intent.SetAction(ScreenOffService.ActionStartScreenOffService);
    Android.App.Application.Context.StartForegroundService(intent);

    [Service(Label = nameof(ScreenOffService))]
    [RequiresApi(Api = (int)BuildVersionCodes.R)]
    public class ScreenOffService : Service
    {
        // ... 翻译的部分省略 ...
    }

    [BroadcastReceiver(Name = "com.eip.MobileApp.ScreenOffBroadcastReceiver", Label = "ScreenOffBroadcastReceiver", Exported = true)]
    [IntentFilter(new[] { Intent.ActionScreenOff }, Priority = (int)IntentFilterPriority.HighPriority)]
    public class ScreenOffBroadcastReceiver : BroadcastReceiver
    {
        // ... 翻译的部分省略 ...
    }

请注意,这只是代码的翻译,不包括解释或其他附加信息。

英文:

Since posting this question I've done more research found that creating a wake lock via a foreground service keeps the app running even when the screen is off.

To do this, I created a foreground service (ScreenOffService, inherited from Android.App.Service).

I also created a BroadcastReceiver (ScreenOffBroadcastReceiver, inherited from Android.Content.BroadcastReceiver) that recieves SCREEN_OFF events and, when it does, acquires an Android.OS.PowerManager.WakeLock with WakeLockFlags.Partial.

ScreenOffService calls its base class's RegisterReceiver(new ScreenOffBroadcastReceiver()).
My app stays awake now.

AndroidManifest.xml
	&lt;uses-permission android:name=&quot;android.permission.FOREGROUND_SERVICE&quot; /&gt;
	&lt;uses-permission android:name=&quot;android.permission.POST_NOTIFICATIONS&quot; /&gt;
	&lt;uses-permission android:name=&quot;android.permission.WAKE_LOCK&quot; /&gt;

    var intent = new Intent(Android.App.Application.Context, typeof(ScreenOffService));
    intent.SetAction(ScreenOffService.ActionStartScreenOffService);
    Android.App.Application.Context.StartForegroundService(intent);


    [Service(Label = nameof(ScreenOffService))]
    [RequiresApi(Api = (int)BuildVersionCodes.R)]
    public class ScreenOffService : Service
    {
        private static readonly string TypeName = typeof(ScreenOffService).FullName;
        public static readonly string ActionStartScreenOffService = TypeName + &quot;.action.START&quot;;

        internal const int NOTIFICATION_ID = 12345678;
        private const string NOTIFICATION_CHANNEL_ID = &quot;screen_off_service_channel_01&quot;;
        private const string NOTIFICATION_CHANNEL_NAME = &quot;screen_off_service_channel_name&quot;;
        private NotificationManager _notificationManager;

        private bool _isStarted;

        private readonly ScreenOffBroadcastReceiver _screenOffBroadcastReceiver;

        public ScreenOffService()
        {
            _screenOffBroadcastReceiver = Microsoft.Maui.Controls.Application.Current.Handler.MauiContext.Services.GetService&lt;ScreenOffBroadcastReceiver&gt;();
        }

        public override void OnCreate()
        {
            base.OnCreate();

            _notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;

            RegisterScreenOffBroadcastReceiver();
        }

        public override void OnDestroy()
        {
            base.OnDestroy();

            UnregisterScreenOffBroadcastReceiver();
        }

        [return: GeneratedEnum]
        public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
        {
            CreateNotificationChannel(); // Elsewhere we must&#39;ve prompted user to allow Notifications

            if (intent.Action == ActionStartScreenOffService)
            {
                try
                {
                    StartForeground();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(&quot;Unable to start Screen On/Off foreground svc: &quot; + ex);
                }
            }

            return StartCommandResult.Sticky;
        }

        private void RegisterScreenOffBroadcastReceiver()
        {
            var filter = new IntentFilter();
            filter.AddAction(Intent.ActionScreenOff);
            RegisterReceiver(_screenOffBroadcastReceiver, filter);
        }

        private void UnregisterScreenOffBroadcastReceiver()
        {
            try
            {
                if (_screenOffBroadcastReceiver != null)
                {
                    UnregisterReceiver(_screenOffBroadcastReceiver);
                }
            }
            catch (Java.Lang.IllegalArgumentException ex)
            {
                Console.WriteLine($&quot;Error while unregistering {nameof(ScreenOffBroadcastReceiver)}. {ex}&quot;);
            }
        }

        private void StartForeground()
        {
            if (!_isStarted)
            {
                Notification notification = BuildInitialNotification();
                StartForeground(NOTIFICATION_ID, notification);

                _isStarted = true;
            }
        }

        private Notification BuildInitialNotification()
        {
            var intentToShowMainActivity = BuildIntentToShowMainActivity();

            var notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .SetContentTitle(Resources.GetString(Resource.String.app_name))
                .SetContentText(Resources.GetString(Resource.String.screen_off_service_started_notification_text))
                .SetSmallIcon(Resource.Drawable.eip_logo_symbol_yellow) // Android top bar icon and Notification drawer item LHS icon
                .SetLargeIcon(global::Android.Graphics.BitmapFactory.DecodeResource(Resources, Resource.Drawable.eip_logo_yellow)) // Notification drawer item RHS icon
                .SetContentIntent(intentToShowMainActivity)
                .SetOngoing(true)
                .Build();

            return notification;
        }

        private PendingIntent BuildIntentToShowMainActivity()
        {
            var mainActivityIntent = new Intent(this, typeof(MainActivity));
            mainActivityIntent.SetAction(Constants.ACTION_MAIN_ACTIVITY);
            mainActivityIntent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTask);
            mainActivityIntent.PutExtra(Constants.SERVICE_STARTED_KEY, true);

            PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, mainActivityIntent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);

            return pendingIntent;
        }

        private void CreateNotificationChannel()
        {
            NotificationChannel chan = new(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Default)
            {
                LightColor = Microsoft.Maui.Graphics.Color.FromRgba(0, 0, 255, 0).ToInt(),
                LockscreenVisibility = NotificationVisibility.Public
            };

            _notificationManager.CreateNotificationChannel(chan);
        }

        public override IBinder OnBind(Intent intent)
        {
            return null;
        }
    }

    [BroadcastReceiver(Name = &quot;com.eip.MobileApp.ScreenOffBroadcastReceiver&quot;, Label = &quot;ScreenOffBroadcastReceiver&quot;, Exported = true)]
    [IntentFilter(new[] { Intent.ActionScreenOff }, Priority = (int)IntentFilterPriority.HighPriority)]
    public class ScreenOffBroadcastReceiver : BroadcastReceiver
    {
        private readonly ILogger&lt;ScreenOffBroadcastReceiver&gt; _logger;

        private PowerManager.WakeLock _wakeLock;

        public ScreenOffBroadcastReceiver()
        {
            _logger = Microsoft.Maui.Controls.Application.Current.Handler.MauiContext.Services.GetService&lt;ILogger&lt;ScreenOffBroadcastReceiver&gt;&gt;();
        }

        public override void OnReceive(Context context, Intent intent)
        {
            if (intent.Action == Intent.ActionScreenOff)
            {
                AcquireWakeLock();
            }
        }

        private void AcquireWakeLock()
        {
            _wakeLock?.Release();

            WakeLockFlags wakeFlags = WakeLockFlags.Partial;

            PowerManager pm = (PowerManager)global::Android.App.Application.Context.GetSystemService(global::Android.Content.Context.PowerService);
            _wakeLock = pm.NewWakeLock(wakeFlags, typeof(ScreenOffBroadcastReceiver).FullName);
            _wakeLock.Acquire();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            _wakeLock?.Release();
        }
    }

huangapple
  • 本文由 发表于 2023年3月31日 04:21:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75892669.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定