GCM 控制台在成功后显示 0(零)个请求

Posted

技术标签:

【中文标题】GCM 控制台在成功后显示 0(零)个请求【英文标题】:GCM Console shows 0 (zero) requests after a successful 【发布时间】:2014-03-03 20:43:03 【问题描述】:

通过我的 ASP.NET Web 服务并使用我的 GCM 浏览器 API 密钥,我可以成功发送 GCM 推送通知,并获得以下成功响应:

"multicast_id":4623804699821154941,"success":1,"failure":0,"canonical_ids":0,"results":["message_id":"0:1393876717064721%59cd098ff9fd7ecd"

两个问题:

1) 即使在多次“发送”和浏览器刷新控制台页面后,Google 开发者控制台仍显示 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://***.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;
    

Android 客户端清单:

<?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>

【问题讨论】:

你能包含你的android清单吗? GcmIntentService是否直接位于包com.MichaelResslerFineArt.eclipmessenger中? 发送消息时,您是否在 logcat 中得到任何信息? 是的,GcmIntentService 在 ProfileActivity (main) 中,这是唯一的活动。我得到的唯一 logcat 是注册 ID,然后我在服务器应用程序中使用它。我没有从 IntentService 收到任何 logcat 消息,也没有在 Intent 中遇到任何断点。 【参考方案1】:

GcmBroadcastReceiverGcmIntentService 不应该是活动类的内部类。它们应该是常规(***)类。

当您在清单中将它们声明为.GcmBroadcastReceiver.GcmIntentService 时,意味着它们应该位于com.MichaelResslerFineArt.eclipmessenger.GcmBroadcastReceivercom.MichaelResslerFineArt.eclipmessenger.GcmIntentService,但是由于您将它们实现为内部类,它们实际上位于@987654328 @ 和 com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmIntentService,因此当消息到达您的设备时无法找到它们。

【讨论】:

非常感谢您的指正。我将 GcmBroadcastReceiver 和 GcmIntentService 放在他们自己的文件中,但它仍然不起作用。 DevConsole 仍然显示 Requests=0(即使成功返回),我的 Android 应用仍然没有记录任何收到的通知。 服务器代码(ASP.NET Web Service)有问题吗?即使 GCM 返回“成功”,Google 开发者控制台仍然显示 Requests=0 : "multicast_id":8840827068984341411,"success":1,"failure":0,"canonical_ids":0,"results":[" message_id":"0:1396878214408468%ed0a384ff9fd7ecd"] @user1987896 你的服务器得到的响应表示成功,所以服务器端没有问题。 那么你会认为控制台会显示 Requests=1。我假设 GCM 通知是通过 Internet 连接,对于此设备,该连接是通过无线集线器(加拿大 IP 地址)。此设备没有蜂窝数据计划。它成功获取了服务器通知请求使用的 GCM 设备 ID。接下来我可以尝试什么? @user1987896 这是this url,虽然我从未尝试过。您必须注册 Google Play 并支付 25 美元才能登录。

以上是关于GCM 控制台在成功后显示 0(零)个请求的主要内容,如果未能解决你的问题,请参考以下文章

控制台显示修改成功,但页面跳转不了

请求Ajax成功后刷新表

获取请求后无法更新状态

vue中axios请求成功了如何把数据渲染到页面上?

从零开始搭建Webpack+react框架

abap语言的alv程序中,如何控制单元格的零显示空