应用程序有时会崩溃并显示“不允许后台启动:服务意图”

Ger*_*rry 6 android azure background-service xamarin

我只兼职做 Android 开发(考虑到 API 的许多微妙之处和低水平,这似乎具有挑战性),并且当我的应用程序在后台时接收通知时遇到问题。我的目标是 Android 8.1。我的应用程序碰巧使用 Azure 和 Xamarin,但我不知道这些细节是否是问题所固有的。

\n\n

如果我的应用程序在后台运行,有时会显示通知,有时我的应用程序会因以下错误而崩溃:

\n\n
2019-03-06 09:12:24.486 9495-9702/? D/tyresense.azurelistenerservice: OnMessage() done\n2019-03-06 09:12:28.278 1548-3232/? W/ActivityManager: Background start not allowed: service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) } to com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService from pid=9495 uid=10173 pkg=com.rimex.tyresense\n2019-03-06 09:12:28.341 9495-9495/? I/MonoDroid: Java.Lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}\n2019-03-06 09:12:28.342 9495-9495/? I/MonoDroid: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}\n2019-03-06 09:12:28.347 9495-9495/? E/AndroidRuntime: FATAL EXCEPTION: main\n    Process: com.rimex.tyresense, PID: 9495\n    java.lang.RuntimeException: Unable to start receiver md5bf34da605c12d097dc5a495af95e9376.TyreSenseBroadcastReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}\n        at android.app.ActivityThread.handleReceiver(ActivityThread.java:3285)\n        at android.app.ActivityThread.-wrap17(Unknown Source:0)\n        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1713)\n        at android.os.Handler.dispatchMessage(Handler.java:106)\n        at android.os.Looper.loop(Looper.java:164)\n        at android.app.ActivityThread.main(ActivityThread.java:6626)\n        at java.lang.reflect.Method.invoke(Native Method)\n        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)\n        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)\n     Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}\n        at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1536)\n        at android.app.ContextImpl.startService(ContextImpl.java:1492)\n        at android.content.ContextWrapper.startService(ContextWrapper.java:650)\n        at android.content.ContextWrapper.startService(ContextWrapper.java:650)\n        at md513d040a829b3f298fbeeee5a6e2c042a.GcmBroadcastReceiverBase_1.n_onReceive(Native Method)\n        at md513d040a829b3f298fbeeee5a6e2c042a.GcmBroadcastReceiverBase_1.onReceive(GcmBroadcastReceiverBase_1.java:29)\n        at android.app.ActivityThread.handleReceiver(ActivityThread.java:3278)\n        at android.app.ActivityThread.-wrap17(Unknown Source:0)\xc2\xa0\n        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1713)\xc2\xa0\n        at android.os.Handler.dispatchMessage(Handler.java:106)\xc2\xa0\n        at android.os.Looper.loop(Looper.java:164)\xc2\xa0\n        at android.app.ActivityThread.main(ActivityThread.java:6626)\xc2\xa0\n        at java.lang.reflect.Method.invoke(Native Method)\xc2\xa0\n        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)\xc2\xa0\n        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)\xc2\xa0\n2019-03-06 09:12:28.348 2761-2761/? W/GCM: broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=com.rimex.tyresense (has extras) }\n2019-03-06 09:12:28.353 9495-9495/? E/tyresense.splashscreen: System.UnhandledExceptionEventArgs\n2019-03-06 09:12:28.357 1548-7265/? W/ActivityManager:   Force finishing activity com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.MainActivity\n2019-03-06 09:12:28.363 1548-1632/? I/ActivityManager: Showing crash dialog for package com.rimex.tyresense u0\n
Run Code Online (Sandbox Code Playgroud)\n\n

我看到这样的文章:https://blog.xamarin.com/replacing-services-jobs-android-oreo-8-0/

\n\n

但我不清楚问题到底是什么,所以我不知道这是否能解决问题。

\n\n

也许在这里?

\n\n
using System;\nusing System.Collections.Generic;\nusing Android.App;\nusing Android.Content;\nusing Android.Util;\nusing Gcm.Client;\nusing WindowsAzure.Messaging;\nusing SharedMobile; // keep it simple, the app might not be running\nusing MyApp.Android; // we need to reference ourselves...\nusing Android.Support.V4.App;\nusing Android.Content.PM;\nusing System.Threading.Tasks;\n\n[assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]\n[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]\n[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]\n[assembly: Xamarin.Forms.Dependency(typeof(DependencyListener))]\nnamespace MyApp.Android\n{\n    [BroadcastReceiver(Permission = Constants.PERMISSION_GCM_INTENTS)]\n    [IntentFilter(new string[] { Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]\n    [IntentFilter(new string[] { Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]\n    [IntentFilter(new string[] { Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]\n    public class MyAppBroadcastReceiver : GcmBroadcastReceiverBase<AzureListenerService>\n    {\n        // this declaration somehow enables messages\n    }\n\n    [Service]\n    public class AzureListenerService : GcmServiceBase // this service might be started at system boot, when the app is otherwise not active\n    {\n        const string TAG = "MyApp.azurelistenerservice";\n        const string HUBNAME = "myhubname";\n        const string CONNECTION =\n               "Endpoint=sb://MyApp.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=secretstuff...";\n\n        public const string GcmProjectNumber = "1234567890";\n        private static NotificationHub noteHub = null;\n\n        public AzureListenerService() : base(GcmProjectNumber)\n        {\n        }\n\n        // need client and area ids before this is called. Application.Context is not available if called from system boot\n        public static void InitializeAzure(Context context = null)\n        {\n            try\n            {\n                if (context == null)\n                    context = Application.Context;\n                var enabled = NotificationManagerCompat.From(context).AreNotificationsEnabled();\n                SharedMobile.API.ApiService.Instance.NotificationsEnabled(enabled);\n                if (enabled)\n                {\n                    GcmClient.CheckDevice(context);\n                    GcmClient.CheckManifest(context);\n                    GcmClient.Register(context, GcmProjectNumber);\n                }\n            }\n            catch (Exception ex)\n            {\n                Log.Error(TAG, ex.Message);\n                Messenger.SendException(ex.Message);\n            }\n        }\n\n        // incoming GCM registration\n        protected override void OnRegistered(Context context, string token)\n        {\n            try\n            {\n                noteHub = new NotificationHub(HUBNAME, CONNECTION, context);\n                var customerId = Int32.Parse(PropertyHelper.ClientId);\n                var tags = BuildTagList(customerId, PropertyHelper.AreaIds);\n                noteHub.Register(token, tags.ToArray());\n                Messenger.RegisterDevice(token);\n            }\n            catch (Exception ex)\n            {\n                Log.Error(TAG, "OnRegistered() " + ex.Message); // ignore register failure when using the Android emulator\n            }\n        }\n\n        protected override void OnMessage(Context context, Intent intent)\n        {\n            try\n            {\n                var areaIds = PropertyHelper.AreaIds;\n                if (string.IsNullOrWhiteSpace(areaIds)) // in case the user logged out\n                    return;\n                new Notifications().CreateEventNotification(intent);\n            }\n            catch (Exception ex)\n            {\n                Log.Error(TAG, "OnMessage() " + ex.Message);\n            }\n        }\n\n        protected List<string> BuildTagList(int clientId, string areaIds)\n        {\n            var list = new List<string>();\n            list.Add($"client_{clientId}");\n            foreach (var id in areaIds.Split(\',\'))\n                list.Add($"area_{id}");\n            return list;\n        }\n\n        protected override void OnUnRegistered(Context context, string registrationId)\n        {\n            Log.Error(TAG, "OnUnRegistered(): " + registrationId);\n        }\n\n        protected override bool OnRecoverableError(Context context, string errorId)\n        {\n            Log.Error(TAG, "OnRecoverableError(): " + errorId);\n            return base.OnRecoverableError(context, errorId);\n        }\n\n        protected override void OnError(Context context, string errorId)\n        {\n            Log.Error(TAG, "OnError(): " + errorId);\n        }\n    }\n}\n\n\nusing System;\nusing Android.App;\nusing Android.Content;\nusing Android.OS;\nusing Android.Support.V4.App;\nusing Android.Support.V4.Content;\nusing Android.Util;\nusing Exception = System.Exception;\n\nnamespace My.Android\n{\n    internal class Notifications\n    {\n        const string TAG = "My.notifications";\n        const string TITLE = "Application is running";\n        const string DESC = "Tap to view";\n        const int MainProgramNotificationId = 775566;\n\n        // unique ID for our notification: \n        static readonly string CHANNEL_ID = "My_channel_23";\n        static readonly string CHANNEL_NAME = "[My_channel]";\n\n        internal void CreateProgramIcon_on_hold(Intent intent)\n        {\n            var extras = new Bundle();\n            extras.PutString("scopeDescription", TITLE);\n            extras.PutString("stateDescription", DESC);\n            intent.PutExtras(extras);\n\n            ShowNotification(intent.Extras, false);\n        }\n\n        internal void CreateEventNotification(Intent extra)\n        {\n            if (extra.Extras != null && !extra.Extras.IsEmpty)\n                ShowNotification(extra.Extras, true);\n\n            var notificationId = extra?.Extras?.GetString("notification-id", null);\n            if (notificationId != null && notificationId.Length > 0)\n                SharedMobile.API.ApiService.NotificationConfirmation(notificationId);\n        }\n\n        private void ShowNotification(Bundle extras, bool autoCancel)\n        {\n            try\n            {\n                string title = TITLE; \n                string description = DESC;\n                if (extras != null && !extras.IsEmpty)\n                {\n                    title = extras.GetString("scopeDescription", null);\n                    description = extras.GetString("stateDescription", "");\n                }\n\n                Intent intent = new Intent(Application.Context, typeof(SplashScreen));\n                BuildNotification(intent, extras, title, description, autoCancel);\n\n            }\n            catch (Exception ex)\n            {\n                Log.Debug(TAG, ex.Message);\n            }\n        }\n\n        private void BuildNotification(Intent intent, Bundle extras, string title, string description, bool autoCancel)\n        {\n            bool onGoing = !autoCancel;\n            int icon = IconId(null, null); // code not shown\n            intent.AddFlags(ActivityFlags.ReorderToFront);\n            var pendingIntent = PendingIntent.GetActivity(Application.Context, 0, intent, PendingIntentFlags.UpdateCurrent);\n\n            CreateNotificationChannel(autoCancel);\n\n            var notificationManager = Application.Context.GetSystemService(Context.NotificationService) as NotificationManager;\n            NotificationCompat.Builder builder = new NotificationCompat.Builder(Application.Context, CHANNEL_ID);\n\n            builder.SetSmallIcon(icon)\n                .SetContentTitle(title)\n                .SetContentText(description)\n                .SetSmallIcon(Resource.Drawable.notification_icon)\n                .SetAutoCancel(autoCancel)\n                .SetOngoing(onGoing)\n                .SetContentIntent(pendingIntent);\n\n            if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)\n                builder.SetColor(ContextCompat.GetColor(Application.Context, Resource.Color.exBackgroundColor));\n\n            string utcTicks = null;\n            if (extras != null)\n                utcTicks = extras.GetString("utcTicks", null);\n            if (utcTicks != null)\n            {\n                var ticks = Int64.Parse(utcTicks);\n                DateTimeOffset dto = new DateTimeOffset(ticks, TimeSpan.Zero);\n                builder.SetWhen(dto.ToUnixTimeMilliseconds());\n            }\n            else\n                builder.SetShowWhen(true);\n\n            notificationManager?.Notify(MainProgramNotificationId, builder.Build());\n        }\n\n        private void CreateNotificationChannel(bool evnt)\n        {\n            if (Build.VERSION.SdkInt < BuildVersionCodes.O)\n            {\n                // There is no need to create a notification channel on older versions of Android.\n                return;\n            }\n            NotificationImportance ni;\n            if (evnt)\n                ni = NotificationImportance.Default;\n            else\n                ni = NotificationImportance.Low;\n            var channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, ni)\n            {\n                Description = "My App",\n                LockscreenVisibility = NotificationVisibility.Private,\n            };\n            var notificationManager = Application.Context.GetSystemService(Context.NotificationService) as NotificationManager;\n            notificationManager.CreateNotificationChannel(channel);\n        }\n\n        string FormatTimeStamp(string utcTicks)\n        {\n            try\n            {\n                var ticks = Int64.Parse(utcTicks);\n                DateTimeOffset dto = new DateTimeOffset(ticks, TimeSpan.Zero);\n                return dto.ToLocalTime().ToString("MM/dd HH:mm:ss");\n            }\n            catch (Exception)\n            {\n                return string.Empty;\n            }\n        }\n\n        bool IgnoreActionProcess(string actionProcessId, int ackedProcessId)\n        {\n            try\n            {\n                if (int.Parse(actionProcessId) == ackedProcessId)\n                    return true;\n            }\n            catch (Exception ex)\n            {\n                Log.Debug(TAG, $"int parse: {actionProcessId} {ex.Message}");\n            }\n            return false;\n        }\n\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

更新:\n我将公共类 AzureListenerService : GcmServiceBase 中的 OnMessage 更改为不执行任何操作,但它仍然崩溃:

\n\n

未处理的异常:\nJava.Lang.IllegalStateException:不允许启动服务 Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.mycompany.myapp cmp=com.mycompany.myapp/md5bf34da605c12d097dc5a495af95e9376。 AzureListenerService(有额外功能)}:应用程序在后台 uid UidRecord{d45626f u0a173 RCVR bg:+1m29s589ms 空闲更改:未缓存的过程:1 seq(0,0,0)} 发生

\n\n

更新\n我实现了 JobScheduler,这没有什么区别。此外,该应用程序必须在后台运行“一段时间”,然后问题总会发生。我还更改了我的应用程序,在通知到达时不执行任何操作(但记录),但它仍然崩溃。

\n\n

更新\n我得到了这个工作:https://developer.xamarin.com/samples/monodroid/Firebase/FCMNotifications/ \n通过将其“按原样”添加到我的 firebase 帐户,下载 google-services.json 并使用http 进行测试: //pushtry.com/。Azure 门户测试发送尚未成功。如果我能让 Azure 正常工作,希望将 FCMNotifications 中的代码合并到我的应用程序中...

\n

Ran*_*mar 6

Android 8.0(API 级别 26)及更高版本对后台服务有限制。它包括对具体方法的以下更改:

现在,如果面向 Android 8.0 的应用尝试在不允许创建后台服务的情况下使用该方法,则 startService() 方法会引发 IllegalStateException。新的 Context.startForegroundService() 方法启动前台服务。即使应用程序处于后台,系统也允许应用程序调用 Context.startForegroundService()。但是,应用程序必须在创建服务后五秒内调用该服务的 startForeground() 方法。