GcmPubSub.getInstance().subscribe()
后,我遇到了一些看似随机的Not Registered
故障。这个问题在一两周前就出现了,我认为通过始终使用主应用程序上下文进行令牌生成和getInstance()
调用来解决问题。
昨天中午12点左右,问题再次发生,然后突然在4:00 - 4:30之间开始工作。下午充满了注释代码以简化事情以及其他随机事情,例如删除和重新添加NuGet包。现在我回到了昨天停止工作之前放置的代码,并且一切都很愉快。
当此问题发生时,仅在通过wifi进行subscribe()
调用时才会出现。如果我在手机上使用蜂窝网络调试应用程序,则永远不会收到Not Registered
失败消息。
当前我正在应用程序中的用户注销时调用unsubscribe()
,并且我已经成功取消订阅并重新订阅(今天早上)。
当通知是特定于用户时,注销时是否最佳实践推送通知?我认为可能会导致GCM服务器以某种方式混淆。
您对我可能收到Not Registered
失败消息的原因有任何建议也将是令人敬畏的。
注册(订阅/取消订阅)服务:
namespace MyApp.Droid.Services
{
/// <summary>
/// The background process that handles retrieving GCM token
/// </summary>
[Service(Exported = true)]
public class GcmRegistrationService : IntentService
{
private static readonly object Locker = new object();
public GcmRegistrationService() : base("GcmRegistrationService") { }
public static Intent GetIntent(Context context, string topic)
{
var valuesForActivity = new Bundle();
valuesForActivity.PutString("topic", topic);
var intent = new Intent(context, typeof(GcmRegistrationService));
intent.PutExtras(valuesForActivity);
return intent;
}
protected override async void OnHandleIntent(Intent intent)
{
try
{
// Get the count value passed to us from MainActivity:
var topic = intent.Extras.GetString("topic", "");
if (string.IsNullOrWhiteSpace(topic))
throw new Java.Lang.Exception("Missing topic value");
string token;
Log.Info("RegistrationIntentService", "Calling InstanceID.GetToken");
lock (Locker)
{
var instanceId = InstanceID.GetInstance(Forms.Context);
var projectNumber = Resources.GetString(Resource.String.ProjectNumber);
token = instanceId.GetToken(projectNumber, GoogleCloudMessaging.InstanceIdScope, null);
Log.Info("RegistrationIntentService", "GCM Registration Token: " + token);
Subscribe(token, topic);
}
var applicationState = ApplicationStateService.GetApplicationState ();
// Save the token to the server if the user is logged in
if(applicationState.IsAuthenticated)
await SendRegistrationToAppServer(token);
}
catch (SecurityException e)
{
Log.Debug("RegistrationIntentService", "Failed to get a registration token because of a security exception");
Log.Debug ("RegistrationIntentService", "Exception message: " + e.Message);
//ToastHelper.ShowStatus("Google Cloud Messaging Security Error");
throw;
}
catch (Java.Lang.Exception e)
{
Log.Debug("RegistrationIntentService", "Failed to get a registration token");
Log.Debug ("RegistrationIntentService", "Exception message: " + e.Message);
//ToastHelper.ShowStatus("Google Cloud Messaging Error");
throw;
}
}
private async System.Threading.Tasks.Task SendRegistrationToAppServer(string token)
{
// Save the Auth Token on the server so messages can be pushed to the device
await DeviceService.UpdateCloudMessageToken (token);
}
void Subscribe(string token, string topic)
{
var pubSub = GcmPubSub.GetInstance(Forms.Context);
pubSub.Subscribe(token, "/topics/" + topic, null);
Log.Debug("RegistrationIntentService", "Successfully subscribed to /topics/" +topic);
ApplicationStateService.SaveCloudMessageToken(token, topic);
}
}
/// <summary>
/// The background process that handles unsubscribing GCM token
/// </summary>
[Service(Exported = false)]
public class GcmUnsubscribeService : IntentService
{
private static readonly object Locker = new object();
public GcmUnsubscribeService() : base("GcmUnsubscribeService") { }
public static Intent GetIntent(Context context, ApplicationState applicationState, bool resubscribe=false)
{
var valuesForActivity = new Bundle();
valuesForActivity.PutString ("token", applicationState.CloudMessageToken);
valuesForActivity.PutString ("topic", applicationState.Topic);
valuesForActivity.PutBoolean ("resubscribe", resubscribe);
var intent = new Intent(context, typeof(GcmUnsubscribeService));
intent.PutExtras(valuesForActivity);
return intent;
}
protected override void OnHandleIntent(Intent intent)
{
// Get the count value passed to us from MainActivity:
var token = intent.Extras.GetString("token", "");
var topic = intent.Extras.GetString("topic", "");
var resubscribe = intent.Extras.GetBoolean ("resubscribe");
var pubSub = GcmPubSub.GetInstance(Forms.Context);
try
{
pubSub.Unsubscribe (token, "/topics/" + topic);
}
catch(IOException e)
{
var x = e.Message;
}
if (resubscribe) {
var subscribeIntent = GcmRegistrationService.GetIntent(Forms.Context, topic);
Forms.Context.StartService(subscribeIntent);
}
}
}
}
AndroidManifest.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto"
package="com.me.notification_app"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="19" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<permission
android:name="com.me.notification_app.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission
android:name="com.me.notification_app.permission.C2D_MESSAGE" />
<application
android:label="Notification App"
android:icon="@drawable/icon">
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:permission="com.google.android.c2dm.permission.SEND"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.me.notification_app" />
</intent-filter>
</receiver>
</application>
</manifest>
主要活动:
[Activity(Label = "MyApp", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
public static string NotificationTopic = "MyEvent";
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App(DeviceType.Android));
if (IsPlayServicesAvailable())
{
var intent = GcmRegistrationService.GetIntent(this, NotificationTopic);
StartService(intent);
}
}
public bool IsPlayServicesAvailable()
{
var resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
ToastHelper.ShowStatus("Google Play Services error: " + GoogleApiAvailability.Instance.GetErrorString(resultCode));
else
{
ToastHelper.ShowStatus("Sorry, notifications are not supported");
}
return false;
}
else
{
return true;
}
}
}
服务器端发送通知。在上面的注册服务中,DeviceService.UpdateCloudMessageToken(token)
调用会填充 Device.CloudMessageToken
:
public async Task SendNotificationAsync(Device device, string message, Dictionary<string, string> extraData = null)
{
if (string.IsNullOrWhiteSpace(device.CloudMessageToken))
throw new Exception("Device is missing a CloudMessageToken");
var apiKey = _appSettingsHelper.GetValue("GoogleApiKey");
var gcmBaseUrl = _appSettingsHelper.GetValue("GoogleCloudMessageBaseUrl");
var gcmSendPath = _appSettingsHelper.GetValue("GoogleCloudMessageSendPath");
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(gcmBaseUrl);
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + apiKey);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var messageInfo = new MessageInfo
{
to = device.CloudMessageToken,
data = new Dictionary<string, string>
{
{"message", message}
}
};
if (extraData != null)
{
foreach (var data in extraData)
{
messageInfo.data.Add(data.Key, data.Value);
}
}
var messageInfoJson = JsonConvert.SerializeObject(messageInfo);
var response =
await
client.PostAsync(gcmSendPath,
new StringContent(messageInfoJson, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var contentValues = JsonConvert.DeserializeObject<Dictionary<string, object>>(content);
if ((long)contentValues["failure"] == 1)
{
var results = (JArray)contentValues["results"];
throw new Exception(results[0]["error"].ToString());
}
}
}
GcmRegistrationService.GetIntent(this, NotificationTopic)
可能指向与 InstanceID 和 GcmPubSub 不同的上下文。 - gerardnimo