1

從我的ASP.NET Web服務,用我的GCM瀏覽器API密鑰0(零)的要求,我可以成功發送GCM推送通知得到以下成功響應:GCM控制檯顯示成功後

{「multicast_id」 :4623804699821154941, 「成功」:1, 「失敗」:0, 「canonical_ids」:0 「結果」:[{ 「MESSAGE_ID」: 「0:1393876717064721%59cd098ff9fd7ecd」}

兩個問題:

1)即使在多次「發送」和瀏覽器刷新控制檯頁面後,Google Developers Console也會顯示0(零)個請求和0個錯誤。每次GCM推送通知都不應該改變計數嗎?

2)我的Android設備沒有收到推送通知。 remote server returned an error: (401) unathorized in C# GCM response

在Android裝置ID所傳遞的來自成功的Android設備登記被複制:後

我的代碼被建模。 我的服務器(網絡服務),代碼如下:

using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Web; 
    using System.Net; 
    using System.Text; 
    using System.IO; 
    using System.Security.Cryptography.X509Certificates; 
    using System.Net.Security; 
    using System.Collections.Specialized; 

    public class AndroidGCMPushNotification 
    { 
     public AndroidGCMPushNotification() 
     { 
      // 
      // TODO: Add constructor logic here 
      // 
     } 
     public string SendNotification(string deviceId, string message) 
     { 
      string BrowserAPIKey = "xxxxxxxxxxxxxxxxxxxxxxxxx";  // GCM Browser Key 

      string tickerText = "ticker test GCM"; 
      string contentTitle = "content title GCM"; 
      string postData = "{ \"registration_ids\": [ \"" + deviceId + "\" ], \"data\": {\"tickerText\":\"" + tickerText + "\", \"contentTitle\":\"" + contentTitle + "\", \"message\": \"" + message + "\"}}"; 

    string sResponseFromServer = SendGCMNotification(BrowserAPIKey, postData); 
    return sResponseFromServer; 
} 

private string SendGCMNotification(string apiKey, string postData, string postDataContentType = "application/json") 
{ 
    String sResponseFromServer = ""; 
    // from here: 
    // https://stackoverflow.com/questions/11431261/unauthorized-when-calling-google-gcm 
    // 
    // original: 
    // http://www.codeproject.com/Articles/339162/Android-push-notification-implementation-using-ASP 

    ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateServerCertificate); 

    // 
    // MESSAGE CONTENT 
    byte[] byteArray = Encoding.UTF8.GetBytes(postData); 

    // 
    // CREATE REQUEST 
    HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://android.googleapis.com/gcm/send"); 

    Request.Method = "POST"; 
    Request.KeepAlive = false; 
    Request.ContentType = postDataContentType; 
    //Request.Headers.Add(string.Format("Authorization: key={0}", apiKey)); 
    Request.Headers.Add(HttpRequestHeader.Authorization, String.Format("key={0}", apiKey)); 
    Request.ContentLength = byteArray.Length; 

    //Stream dataStream; 
    try 
    { 
     Stream dataStream = Request.GetRequestStream(); 

     dataStream.Write(byteArray, 0, byteArray.Length); 
     dataStream.Close(); 
    } 
    catch (Exception e) 
    { 
     sResponseFromServer = e.Message; 
    } 

    // 
    // SEND MESSAGE 
    try 
    { 
     WebResponse Response = Request.GetResponse(); 

     HttpStatusCode ResponseCode = ((HttpWebResponse)Response).StatusCode; 

     if (ResponseCode.Equals(HttpStatusCode.Unauthorized) || ResponseCode.Equals(HttpStatusCode.Forbidden)) 
     { 
      sResponseFromServer = "Unauthorized - need new token"; 

     } 
     else if (!ResponseCode.Equals(HttpStatusCode.OK)) 
     { 
      sResponseFromServer = "Response from web service isn't OK"; 
     } 

     StreamReader Reader = new StreamReader(Response.GetResponseStream()); 
     sResponseFromServer = Reader.ReadToEnd(); 
     Reader.Close(); 
    } 
    catch (Exception e) 
    { 
     sResponseFromServer = e.Message; 
    } 
    return sResponseFromServer; 
} 

public static bool ValidateServerCertificate(
              object sender, 
              X509Certificate certificate, 
              X509Chain chain, 
              SslPolicyErrors sslPolicyErrors) 
{ 
    return true; 
} 

}

在Android客戶端,我有以下通知,接收代碼:

package com.MichaelResslerFineArt.eclipmessenger; 

import java.io.IOException; 
import java.util.concurrent.atomic.AtomicInteger; 

import com.MichaelResslerFineArt.eclipmessenger.ProfileActivity; 
import com.MichaelResslerFineArt.eclipmessenger.R; 
import com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmBroadcastReceiver; 
import com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmIntentService; 
import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.GooglePlayServicesUtil; 
import com.google.android.gms.gcm.GoogleCloudMessaging; 

import android.os.AsyncTask; 
import android.os.Bundle; 
import android.os.SystemClock; 
import android.app.Activity; 
import android.app.IntentService; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.content.ComponentName; 

import android.content.Context; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.content.pm.PackageInfo; 
import android.content.pm.PackageManager.NameNotFoundException; 
import android.support.v4.app.NotificationCompat; 
import android.support.v4.content.WakefulBroadcastReceiver; 
import android.util.Log; 
import android.view.Menu; 
import android.view.View; 
import android.widget.Toast; 

public class ProfileActivity extends Activity { 

    public static final String EXTRA_MESSAGE = "message"; 
    public static final String PROPERTY_REG_ID = "registration_id"; 
    private static final String PROPERTY_APP_VERSION = "appVersion"; 
    private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; 

    String SENDER_ID = "594966827111"; // GCM Project Number 


    public static final String LOG_MSG_TAG = "eClipMessenger"; 
    GoogleCloudMessaging gcm; 
    AtomicInteger msgId = new AtomicInteger(); 
    SharedPreferences prefs; 
    Context context; 

    String regid; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_profile); 

     context = getApplicationContext(); 

     // Check device for Play Services APK. 
     GcmApi gcmApi = new GcmApi(); 
     if (gcmApi.checkPlayServices(this)) { 
      // If this check succeeds, proceed with normal processing. 
      // Otherwise, prompt user to get valid Play Services APK. 
      Toast.makeText(this, "Success checking APK.", Toast.LENGTH_LONG).show(); 
      Log.i(LOG_MSG_TAG, "Success checking APK."); 

      gcm = GoogleCloudMessaging.getInstance(this); 
      regid = getRegistrationId(context); 

      if (regid.isEmpty()) { 
       registerInBackground(); 
      } 
      else { 
       Log.i(LOG_MSG_TAG, "GCM Reg ID: " + regid); 
      } 
     } else { 
      Log.i(LOG_MSG_TAG, "No valid Google Play Services APK found."); 
     } 
    } 

    // You need to do the Play Services APK check here too. 
    @Override 
    protected void onResume() { 
     super.onResume(); 
     GcmApi gcmApi = new GcmApi(); 
     gcmApi.checkPlayServices(this); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.profile, menu); 
     return true; 
    } 

    /** 
    * Gets the current registration ID for application on GCM service. 
    * <p> 
    * If result is empty, the app needs to register. 
    * 
    * @return registration ID, or empty string if there is no existing 
    *   registration ID. 
    */ 
    private String getRegistrationId(Context context) { 
     final SharedPreferences prefs = getGCMPreferences(context); 
     String registrationId = prefs.getString(PROPERTY_REG_ID, ""); 
     if (registrationId.isEmpty()) { 
      Log.i(LOG_MSG_TAG, "Registration not found."); 
      return ""; 
     } 
     // Check if app was updated; if so, it must clear the registration ID 
     // since the existing regID is not guaranteed to work with the new 
     // app version. 
     int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); 
     int currentVersion = getAppVersion(context); 
     if (registeredVersion != currentVersion) { 
      Log.i(LOG_MSG_TAG, "App version changed."); 
      return ""; 
     } 
     return registrationId; 
    } 

    /** 
    * @return Application's {@code SharedPreferences}. 
    */ 
    private SharedPreferences getGCMPreferences(Context context) { 
     // This sample app persists the registration ID in shared preferences, but 
     // how you store the regID in your app is up to you. 
     return getSharedPreferences(ProfileActivity.class.getSimpleName(), 
       Context.MODE_PRIVATE); 
    } 

    /** 
    * @return Application's version code from the {@code PackageManager}. 
    */ 
    private static int getAppVersion(Context context) { 
     try { 
      PackageInfo packageInfo = context.getPackageManager() 
        .getPackageInfo(context.getPackageName(), 0); 
      return packageInfo.versionCode; 
     } catch (NameNotFoundException e) { 
      // should never happen 
      throw new RuntimeException("Could not get package name: " + e); 
     } 
    } 

    /** 
    * Registers the application with GCM servers asynchronously. 
    * <p> 
    * Stores the registration ID and app versionCode in the application's 
    * shared preferences. 
    */ 
    private void registerInBackground() { 
     new AsyncTask<Void, Void, String>() { 
      @Override 
      protected String doInBackground(Void... params) { 
       String msg = ""; 
       try { 
        if (gcm == null) { 
         gcm = GoogleCloudMessaging.getInstance(context); 
        } 
        regid = gcm.register(SENDER_ID); 
        msg = "Device registered, registration ID=" + regid; 

        // You should send the registration ID to your server over HTTP, 
        // so it can use GCM/HTTP or CCS to send messages to your app. 
        // The request to your server should be authenticated if your app 
        // is using accounts. 
        sendRegistrationIdToBackend(); 

        // For this demo: we don't need to send it because the device 
        // will send upstream messages to a server that echo back the 
        // message using the 'from' address in the message. 

        // Persist the regID - no need to register again. 
        storeRegistrationId(context, regid); 

       } catch (IOException ex) { 
        msg = "Error :" + ex.getMessage(); 
        // If there is an error, don't just keep trying to register. 
        // Require the user to click a button again, or perform 
        // exponential back-off. 
       } 
       return msg; 
      } 

      //@Override 
      protected void onPostExecute(String msg) { 
       Log.i(LOG_MSG_TAG, msg); 
       //mDisplay.append(msg + "\n"); 
      } 

     }.execute(null, null, null); 
    } 
    /** 
    * Sends the registration ID to your server over HTTP, so it can use GCM/HTTP 
    * or CCS to send messages to your app. Not needed for this demo since the 
    * device sends upstream messages to a server that echoes back the message 
    * using the 'from' address in the message. 
    */ 
    private void sendRegistrationIdToBackend() { 
     // Your implementation here. 
    }  
    /** 
    * Stores the registration ID and app versionCode in the application's 
    * {@code SharedPreferences}. 
    * 
    * @param context application's context. 
    * @param regId registration ID 
    */ 
    private void storeRegistrationId(Context context, String regId) { 
     Log.i(LOG_MSG_TAG, "entered storeRegistrationId()"); 
     final SharedPreferences prefs = getGCMPreferences(context); 
     Log.i(LOG_MSG_TAG, "retrieved preferences"); 
     int appVersion = getAppVersion(context); 
     Log.i(LOG_MSG_TAG, "Saving regId on app version " + appVersion); 
     SharedPreferences.Editor editor = prefs.edit(); 
     editor.putString(PROPERTY_REG_ID, regId); 
     editor.putInt(PROPERTY_APP_VERSION, appVersion); 
     editor.commit(); 
    }  

    /* 
    public void sendGcm(final View view) { 
     if (view == findViewById(R.id.button2)) { 
      new AsyncTask<Void, Void, String>() { 
       @Override 
       protected String doInBackground(Void... params) { 
        String msg = ""; 
        try { 
         Bundle data = new Bundle(); 
          data.putString("my_message", "Hello World"); 
          data.putString("my_action", 
            "com.google.android.gcm.demo.app.ECHO_NOW"); 
          String id = Integer.toString(msgId.incrementAndGet()); 
          gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data); 
          msg = "Sent message"; 
        } catch (IOException ex) { 
         msg = "Error :" + ex.getMessage(); 
        } 
        return msg; 
       } 

       @Override 
       protected void onPostExecute(String msg) { 
        Log.i(LOG_MSG_TAG, msg); 
        //mDisplay.append(msg + "\n"); 
       } 
      }.execute(null, null, null); 
//  } else if (view == findViewById(R.id.textView2)) { 
//   mDisplay.setText(""); 
     } 
    } 
    */ 
    public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { 
     @Override 
     public void onReceive(Context context, Intent intent) { 
      Log.i(LOG_MSG_TAG, "onReceive"); 

      // Explicitly specify that GcmIntentService will handle the intent. 
      ComponentName comp = new ComponentName(context.getPackageName(), 
        GcmIntentService.class.getName()); 
      // Start the service, keeping the device awake while it is launching. 
      startWakefulService(context, (intent.setComponent(comp))); 
      setResultCode(Activity.RESULT_OK); 
     } 
    } 
    public class GcmIntentService extends IntentService { 
     public static final int NOTIFICATION_ID = 1; 
     private NotificationManager mNotificationManager; 
     NotificationCompat.Builder builder; 

     public GcmIntentService() { 
      super("GcmIntentService"); 
     } 

     @Override 
     protected void onHandleIntent(Intent intent) { 
      Bundle extras = intent.getExtras(); 
      GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); 
      // The getMessageType() intent parameter must be the intent you received 
      // in your BroadcastReceiver. 
      String messageType = gcm.getMessageType(intent); 

      if (!extras.isEmpty()) { // has effect of unparcelling Bundle 
       /* 
       * Filter messages based on message type. Since it is likely that GCM 
       * will be extended in the future with new message types, just ignore 
       * any message types you're not interested in, or that you don't 
       * recognize. 
       */ 
       if (GoogleCloudMessaging. 
         MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { 
        sendNotification("Send error: " + extras.toString()); 
       } else if (GoogleCloudMessaging. 
         MESSAGE_TYPE_DELETED.equals(messageType)) { 
        sendNotification("Deleted messages on server: " + 
          extras.toString()); 
       // If it's a regular GCM message, do some work. 
       } else if (GoogleCloudMessaging. 
         MESSAGE_TYPE_MESSAGE.equals(messageType)) { 
        // This loop represents the service doing some work. 
        for (int i=0; i<5; i++) { 
         Log.i(LOG_MSG_TAG, "Working... " + (i+1) 
           + "/5 @ " + SystemClock.elapsedRealtime()); 
         try { 
          Thread.sleep(5000); 
         } catch (InterruptedException e) { 
         } 
        } 
        Log.i(LOG_MSG_TAG, "Completed work @ " + SystemClock.elapsedRealtime()); 
        // Post notification of received message. 
        sendNotification("Received: " + extras.toString()); 
        Log.i(LOG_MSG_TAG, "Received: " + extras.toString()); 
       } 
      } 
      // Release the wake lock provided by the WakefulBroadcastReceiver. 
      GcmBroadcastReceiver.completeWakefulIntent(intent); 
     } 

     // Put the message into a notification and post it. 
     // This is just one simple example of what you might choose to do with 
     // a GCM message. 
     private void sendNotification(String msg) { 
      Log.i(LOG_MSG_TAG, "sendNotification"); 

      mNotificationManager = (NotificationManager) 
        this.getSystemService(Context.NOTIFICATION_SERVICE); 

      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 
        new Intent(this, ProfileActivity.class), 0); 

      NotificationCompat.Builder mBuilder = 
        new NotificationCompat.Builder(this) 
      .setSmallIcon(R.drawable.ic_launcher) 
      .setContentTitle("GCM Notification") 
      .setStyle(new NotificationCompat.BigTextStyle() 
      .bigText(msg)) 
      .setContentText(msg); 

      mBuilder.setContentIntent(contentIntent); 
      mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); 
     } 
    } 
} 

而且這個單獨的類檢查Play服務:

package com.MichaelResslerFineArt.eclipmessenger; 

import android.app.Activity; 
import android.util.Log; 
import android.widget.Toast; 

import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.GooglePlayServicesUtil; 

public class GcmApi { 

    public static final String EXTRA_MESSAGE = "message"; 
    public static final String PROPERTY_REG_ID = "registration_id"; 
    private static final String PROPERTY_APP_VERSION = "appVersion"; 
    private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; 

    String SENDER_ID = "594966827111"; // GCM Project Number 

    /** 
    * Check the device to make sure it has the Google Play Services APK. If 
    * it doesn't, display a dialog that allows users to download the APK from 
    * the Google Play Store or enable it in the device's system settings. 
    */ 
    public boolean checkPlayServices(ProfileActivity currActivity) { 
     int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(currActivity); 
     if (resultCode != ConnectionResult.SUCCESS) { 
      if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { 
       GooglePlayServicesUtil.getErrorDialog(resultCode, currActivity, 
         PLAY_SERVICES_RESOLUTION_REQUEST).show(); 
      } else { 
       Log.i(ProfileActivity.LOG_MSG_TAG, "This device is not supported."); 
       Toast.makeText(currActivity, "This device is not supported.", Toast.LENGTH_LONG).show(); 
       currActivity.finish(); 
      } 
      return false; 
     } 
     return true; 
    } 
} 

And the Android client sid Ë清單:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
package="com.MichaelResslerFineArt.eclipmessenger" 
android:versionCode="1" 
android:versionName="1.0" > 

<uses-sdk 
    android:minSdkVersion="10" 
    android:targetSdkVersion="18" /> 
<uses-permission android:name="android.permission.INTERNET"/> 
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/> 
<uses-permission android:name="android.permission.GET_ACCOUNTS"/> 
<uses-permission android:name="android.permission.WAKE_LOCK"/> 
<permission  android:name="com.MichaelResslerFineArt.eclipmessenger.permission.C2D_MESSAGE" 
    android:protectionLevel="signature" /> 
<uses-permission  android:name="com.MichaelResslerFineArt.eclipmessenger.permission.C2D_MESSAGE" />    

<application 
    android:allowBackup="true" 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme" > 
    <activity 
     android:name="com.MichaelResslerFineArt.eclipmessenger.ProfileActivity" 
     android:label="@string/app_name" > 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 

    <meta-data android:name="com.google.android.gms.version" 
       android:value="@integer/google_play_services_version" /> 
    <receiver 
     android:name=".GcmBroadcastReceiver" 
     android:permission="com.google.android.c2dm.permission.SEND" > 
     <intent-filter> 
      <action android:name="com.google.android.c2dm.intent.RECEIVE" /> 
      <category android:name="com.MichaelResslerFineArt.eclipmessenger" /> 
     </intent-filter> 
    </receiver> 
    <service android:name=".GcmIntentService" /> 

</application> 

</manifest> 
+0

你可以包含你的android清單嗎? – Eran

+0

'GcmIntentService'直接位於包'com.MichaelResslerFineArt.eclipmessenger'中嗎? – Eran

+0

發送消息時,你有什麼在logcat嗎? – Eran

回答

0

GcmBroadcastReceiverGcmIntentService不應該是你的活動類的內部類。他們應該是常規(頂級)課程。

當你聲明他們作爲您的清單.GcmBroadcastReceiver.GcmIntentService,在它們應該位於com.MichaelResslerFineArt.eclipmessenger.GcmBroadcastReceivercom.MichaelResslerFineArt.eclipmessenger.GcmIntentService手段,但因爲你實現他們作爲內部類,其實都是位於com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmBroadcastReceivercom.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmIntentService,所以它們不能被發現當信息到達您的設備時。

+0

非常感謝您的糾正。我把GcmBroadcastReceiver和GcmIntentService放在它們自己的文件中,但它仍然不起作用。 DevConsole仍然顯示Requests = 0(即使返回成功),我的Android應用程序仍然不記錄任何收到的通知。 – user1987896

+0

服務器代碼(ASP.NET Web Service)有什麼問題嗎?即使GCM返回「成功」,Google Developers Console仍然顯示Requests = 0:{「multicast_id」:8840827068984341411,「success」:1,「failure」:0,「canonical_ids」:0,「results」:[{「 message_id「:」0:1396878214408468%ed0a384ff9fd7ecd「}]} – user1987896

+0

@ user1987896您的服務器收到的響應表示成功,所以服務器端沒有問題。 – Eran