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

时间:2014-03-03 20:43:04

标签: android push-notification google-api google-cloud-messaging

从我的ASP.NET网络服务和使用我的GCM浏览器API密钥,我可以成功发送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;
    }
}

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>

1 个答案:

答案 0 :(得分:0)

GcmBroadcastReceiverGcmIntentService不应该是您的活动类的内部类。它们应该是常规(顶级)课程。

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