Xamarin.Forms Tutorial for the Push Notification Plugin breaks Android Notifications when App is Not Running or Backgrounded (Fix Included in Comments)
See original GitHub issuetl;dr The Xamarin.Forms’ DependancyService
prevents Android apps from receiving push notifications when not running or backgrounded.
I recommend updating the tutorial on the Push Notification Plugin’s GitHub page to help prevent confusion from other developers in the future: Adding Push Notification Post to your Xamarin Forms Application. I followed this tutorial to implement the Push Notification Plugin, and it lead me to this road block: my Android app could not receive push notifications when it was not running or backgrounded.
After following this step-by-step tutorial to setup the Push Notification Plugin on Xamarin.Forms, the iOS push notifications work flawlessly and the Android devices receive push notifications when the app is running or backgrounded. But on Android, if I force-close the app so that it is no longer running (e.g. press the Recent Apps button, and swipe away my app), and then send a push notification, the device shows this error message: “Unfortunately, [App Name] has stopped” (screen shot below).
The error below was appearing because I did not call the CrossPushNotification.Initialize<CrossPushNotificationListener>(Keys_Constants.GOOGLE_APIs_ID);
method inside [Application] OnCreate()
as is recommended on the Push Notification Plugin’s GitHub page.
But, the reason why I didn’t/couldn’t include this method in [Application] OnCreate()
was because CrossPushNotificationListener
requires the Xamarin.Forms’ DependencyService
, which is impossible in [Application] OnCreate()
because the DependencyService
hasn’t been initialized yet; global::Xamarin.Forms.Forms.Init(this, bundle);
is called in Android’s MainActivity
which hasn’t been yet instantiated when the code is executing the [Application]
class.
The remedy was to break apart my cross-platform implementation of the CrossPushNotificationListener
into a platform-specific NotificationListener
that now live in the platform-specific PCL (code below).
I recommend updating the tutorial on the GitHub page to implement this plugin for Xamarin.Forms. The tutorial has the user create a CrossPushNotificationListener
class that uses Xamarin.Forms’ DependencyService
. This tutorial works flawlessly to receive push notifications on iOS, but on Android, it will only allow the device to receive push notifications while the app is running or backgrounded.
Error When Sending a Push Notification to a App That Is Not Running and Not Backgrounded (Before implementing the fix)
Updated Platform-Specific Notification Listener for Android
[Application] File
using System;
using Android.OS;
using Android.App;
using Android.Content;
using Android.Runtime;
using Plugin.CurrentActivity;
using PushNotification.Plugin;
using Xamarin;
namespace MondayPundayApp.Droid
{
//You can specify additional application information in this attribute
[Application]
public class Punday : Application, Application.IActivityLifecycleCallbacks
{
public static Context AppContext;
public Punday(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
AppContext = this.ApplicationContext;
CrossPushNotification.Initialize<AndroidPushNotificationListener>(Keys_Constants.GOOGLE_APIs_ID);
StartPushService();
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
void IActivityLifecycleCallbacks.OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed(Activity activity)
{
}
public void OnActivityPaused(Activity activity)
{
}
public void OnActivityResumed(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
}
public void OnActivityStarted(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped(Activity activity)
{
}
public static void StartPushService()
{
AppContext.StartService(new Intent(AppContext, typeof(PushNotificationService)));
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext, typeof(PushNotificationService)), 0);
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(Context.AlarmService);
alarm.Cancel(pintent);
}
}
public static void StopPushService()
{
AppContext.StopService(new Intent(AppContext, typeof(PushNotificationService)));
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext, typeof(PushNotificationService)), 0);
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(Context.AlarmService);
alarm.Cancel(pintent);
}
}
}
}
AndroidPushNotificationListener.cs
using PushNotification.Plugin;
using PushNotification.Plugin.Abstractions;
using Newtonsoft.Json.Linq;
using MondayPundayApp.Droid;
namespace MondayPundayApp.Droid
{
public class AndroidPushNotificationListener : IPushNotificationListener
{
AzureNotificationHubService_Android _azureNotificationHubService;
public AndroidPushNotificationListener()
{
_azureNotificationHubService = new AzureNotificationHubService_Android();
}
public async void OnRegistered(string Token, DeviceType deviceType)
{
await _azureNotificationHubService.RegisterNativeAsync(Token);
}
public async void OnUnregistered(DeviceType deviceType)
{
await _azureNotificationHubService.UnregisterNativeAsync();
}
public void OnError(string message, DeviceType deviceType)
{
}
public void OnMessage(JObject values, DeviceType deviceType)
{
}
public bool ShouldShowNotification()
{
return true;
}
}
}
AzureNotificationHubService_Android.cs
using System;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MobileServices;
using Xamarin;
namespace MondayPundayApp.Droid
{
public class AzureNotificationHubService_Android
{
private MobileServiceClient _mobileServiceClient;
private Push _push;
public string CurrentDeviceId { get; private set; }
public AzureNotificationHubService_Android()
{
try
{
_mobileServiceClient = new MobileServiceClient(Keys_Constants.AzureMobileService_URL, Keys_Constants.AzureMobileService_KEY);
_push = _mobileServiceClient.GetPush();
}
catch (Exception e)
{
Insights.Report(e);
}
}
public async Task UnregisterNativeAsync()
{
try
{
await _push.UnregisterNativeAsync();
}
catch (Exception e)
{
Insights.Report(e);
}
}
public async Task RegisterNativeAsync(string deviceId)
{
try
{
CurrentDeviceId = deviceId;
await _push.RegisterNativeAsync(deviceId);
}
catch (Exception e)
{
Insights.Report(e);
}
}
}
}
Issue Analytics
- State:
- Created 8 years ago
- Reactions:1
- Comments:38
Top GitHub Comments
@lanceking @MKahmen I recommend creating your own implementation too, because the Push Notification Plugin doesn’t provide much added-value.
The Xamarin Evolve app implements Push for both iOS and Android and it’s what I reference when implementing push notifications now: https://github.com/xamarinhq/app-evolve
https://documentation.onesignal.com/docs/xamarin-sdk