GCM + AppEngine - 应用程序不接收推送通知

时间:2014-07-14 22:01:19

标签: android google-app-engine push-notification google-cloud-messaging

我正在为我的appengine服务器开发一个app客户端,该服务器支持推送通知服务。 我已经设置并部署了服务器部分并且能够注册我的设备,但是一旦我发送了推送通知,它就永远不会出现在应用程序中。

我正在为应用和后端使用Android Studio,我按照此处的步骤操作:https://github.com/manfredzab/gradle-appengine-templates/tree/master/GcmEndpoints

AndroidManifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp" >

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

        <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.example.myapp" />
            </intent-filter>
        </receiver>

        <service android:name=".GcmIntentService" />
    </application>

    <permission
        android:name="com.example.myapp.permission.C2D_MESSAGE"
         />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <!-- <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" /> -->
    <uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.example.myapp.c2dm.permission.RECEIVE" />

</manifest>

MainActivity:

package com.example.myapp;

public class MyActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my);
    new GcmRegistrationAsyncTask().execute(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.my, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}
}

广播接收器

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
    // 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);
    Log.i("blabla","notification received");
}
}

IntentService

public class GcmIntentService extends IntentService
{

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

@Override
protected void onHandleIntent(Intent intent) {
    Log.i("blabla", "notification received");
    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 != null && !extras.isEmpty()) {  // has effect of unparcelling Bundle
        // Since we're not using two way messaging, this is all we really to check for
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
            Logger.getLogger("GCM_RECEIVED").log(Level.INFO, extras.toString());

            showToast(extras.getString("message"));
        }
    }
    GcmBroadcastReceiver.completeWakefulIntent(intent);
}

protected void showToast(final String message) {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        }
    });
}
}

MessagingEndpoint:

@Api(name = "messaging", version = "v1", namespace = @ApiNamespace(ownerDomain =     "myapp.example.com", ownerName = "myapp.example.com", packagePath=""))
public class MessagingEndpoint {
private static final Logger log = Logger.getLogger(MessagingEndpoint.class.getName());

/** Api Keys can be obtained from the google cloud console */
private static final String API_KEY = System.getProperty("gcm.api.key");

public void sendMessage(@Named("message") String message) throws IOException {
    if(message == null || message.trim().length() == 0) {
        log.warning("Not sending message because it is empty");
        return;
    }
    // crop longer messages
    if (message.length() > 1000) {
        message = message.substring(0, 1000) + "[...]";
    }
    Sender sender = new Sender(API_KEY);
    Message msg = new Message.Builder().addData("message", message).build();
    List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(1000).list();
    for(RegistrationRecord record : records) {
        Result result = sender.send(msg, record.getRegId(), 10);
        if (result.getMessageId() != null) {
            log.info("Message sent to " + record.getRegId());
            String canonicalRegId = result.getCanonicalRegistrationId();
            if (canonicalRegId != null) {
                // if the regId changed, we have to update the datastore
                log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
                record.setRegId(canonicalRegId);
                ofy().save().entity(record).now();
            }
        } else {
            String error = result.getErrorCodeName();
            if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
                log.warning("Registration Id " + record.getRegId() + " no longer registered with GCM, removing from datastore");
                // if the device is no longer registered with Gcm, remove it from the datastore
                ofy().delete().entity(record).now();
            }
            else {
                log.warning("Error when sending message : " + error);
            }
        }
    }
}
}

OfyService:

public class OfyService {

static {
    ObjectifyService.register(RegistrationRecord.class);
}

public static Objectify ofy() {
    return ObjectifyService.ofy();
}

public static ObjectifyFactory factory() {
    return ObjectifyService.factory();
}
}

注册端点

@Api(name = "registration", version = "v1",
 namespace = @ApiNamespace(ownerDomain = "myapp.example.com", ownerName = "myapp.example.com", packagePath = ""))
public class RegistrationEndpoint
{

private static final Logger log = Logger.getLogger(RegistrationEndpoint.class.getName());

/**
 * Register a device to the backend
 *
 * @param regId The Google Cloud Messaging registration Id to add
 */
@ApiMethod(name = "register")
public void registerDevice(@Named("regId") String regId)
{
    if (findRecord(regId) != null)
    {
        log.info("Device " + regId + " already registered, skipping register");
        return;
    }
    RegistrationRecord record = new RegistrationRecord();
    record.setRegId(regId);
    ofy().save().entity(record).now();
}

/**
 * Unregister a device from the backend
 *
 * @param regId The Google Cloud Messaging registration Id to remove
 */
@ApiMethod(name = "unregister")
public void unregisterDevice(@Named("regId") String regId)
{
    RegistrationRecord record = findRecord(regId);
    if (record == null)
    {
        log.info("Device " + regId + " not registered, skipping unregister");
        return;
    }
    ofy().delete().entity(record).now();
}

/**
 * Return a collection of registered devices
 *
 * @param count The number of devices to list
 * @return a list of Google Cloud Messaging registration Ids
 */
@ApiMethod(name = "listDevices")
public CollectionResponse<RegistrationRecord> listDevices(@Named("count") int count)
{
    List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(count).list();
    return CollectionResponse.<RegistrationRecord>builder().setItems(records).build();
}

private RegistrationRecord findRecord(String regId)
{
    return ofy().load().type(RegistrationRecord.class).filter("regId", regId).first().now();
}

}

RegistrationRecord

@Entity
public class RegistrationRecord {

@Id
Long id;

@Index
private String regId;
// you can add more fields...

public RegistrationRecord() {}

public String getRegId() {
    return regId;
}

public void setRegId(String regId) {
    this.regId = regId;
}
}

的index.html

<!DOCTYPE html>
<html>
<head>
    <title>Hello, Google Cloud Messaging!</title>
    <link rel="stylesheet"     href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet"     href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" >    </script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js">    </script>
</head>
<body role="document" style="padding-top: 70px;">
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Hello, Google Cloud Messaging!</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-    toggle="dropdown">Documentation <b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li><a     href="https://developers.google.com/appengine/docs/java/">Google App Engine</a></li>
                        <li><a     href="https://developers.google.com/appengine/docs/java/endpoints/">Google Cloud     Endpoints</a></li>
                        <li><a href="http://developer.android.com/google/gcm/">Google Cloud Messaging</a></li>
                        <li><a href="https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints">Connecting your Android application to this backend</a></li>
                    </ul>
                </li>
                <li><a href="/_ah/api/explorer">Google Cloud Endpoints API Explorer</a></li>
                <li><a href="https://console.developers.google.com">Google Developers Console</a></li>
            </ul>
        </div>
    </div>
</div>

<div class="container theme-showcase" role="main">
<!--
  Output from GCM call.
-->
<div class="alert alert-success" style="visibility: collapse;" id="outputAlert"></div>

<!--
  A form that takes a message text and submits it to "messaging" Endpoints API,
  access to this Endpoints API is enabled once the client is loaded below.
-->
<div class="jumbotron">
    <div class="row">
        <div class="col-lg-12">
            <h1>Hello, Google Cloud Messaging!</h1>
            <p>Enter your message below and press "Send Message" button to send it over Google Cloud Messaging to all registered devices.</p>
            <form>
                <div class="input-group">
                    <input type="text" class="form-control input-lg" placeholder="Message text" id="messageTextInput" />
                      <span class="input-group-btn">
                         <button class="btn btn-default btn-primary btn-group btn-lg" type="submit" id="sendMessageButton">Send Message &raquo;</button>
                      </span>
                </div>
            </form>
            <br/>
            <p>If you need step-by-step instructions for connecting your Android application to this backend module, see <a href="https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints">"App Engine Backend with Google Cloud Messaging" template documentation</a>.</p>
            <p>
                    <small>
                    For more information about Google App Engine for Java, check out the <a href="https://developers.google.com/appengine/docs/java/">App Engine documentation</a>.<br />
                    To learn more about Google Cloud Endpoints, see <a href="https://developers.google.com/appengine/docs/java/endpoints/">Cloud Endpoints documentation</a>.<br />
                    Similarly, for more information about Google Cloud Messaging, see     <a href="http://developer.android.com/google/gcm/">Cloud Messaging documentation</a>.<br />
                    If you'd like to access your generated Google Cloud Endpoints APIs directly, see the <a href="/_ah/api/explorer">Cloud Endpoints API Explorer</a>.
                    </small>
                </p>
            </div>
        </div>
    </div>
</div>

<script type="text/javascript">
// A function that attaches a "Send Message" button click handler
function enableClick() {
  document.getElementById('sendMessageButton').onclick = function() {
    var message = document.getElementById('messageTextInput').value;
    if (!message) {
      message = '(Empty message)';
    }

    gapi.client.messaging.messagingEndpoint.sendMessage({'message': message}).execute(
      function(response) {
        var outputAlertDiv = document.getElementById('outputAlert');
        outputAlertDiv.style.visibility = 'visible';

        if (response && response.error) {
          outputAlertDiv.className = 'alert alert-danger';
          outputAlertDiv.innerHTML = '<b>Error Code:</b> ' + response.error.code + ' [' + response.error.message +']';
        }
        else {
          outputAlertDiv.className = 'alert alert-success';
          outputAlertDiv.innerHTML = '<b>Success:</b> Message \"' + message + '\" sent to all registered devices!</h2>';
        }
      }
    );
    return false;
  }
}

// This is called initially
function init() {
  var apiName = 'messaging'
  var apiVersion = 'v1'
  // set the apiRoot to work on a deployed app and locally
  var apiRoot = '//' + window.location.host + '/_ah/api';
  var callback = function() {
    enableClick();
  }
  gapi.client.load(apiName, apiVersion, callback, apiRoot);
}
  </script>
<!--
 Load the Google APIs Client Library for JavaScript
 More info here : https://developers.google.com/api-client-    library/javascript/reference/referencedocs
-->
<script src="https://apis.google.com/js/client.js?onload=init"></script>
</body>
</html>

应用服务引擎-web.xml中

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>app-name-from-developers-console</application>
<version>1</version>
<threadsafe>true</threadsafe>

<system-properties>
    <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>


    <property name="gcm.api.key" value="gcm-SERVER-api-key-from-dev-console"/>
</system-properties>

我不知道还要添加什么,后端似乎运行良好,因为注册已成功进行。我没有任何错误,没有。我错过了什么?某处肯定会有一点错误,我无法发现它。我认为,由于全部生成推送通知,因此可以立即发送推送通知。我正确设置了服务api密钥,并且已将IPS设置为允许的任何IP。

1 个答案:

答案 0 :(得分:1)

唯一的问题是我在GCM的主页面网址中使用了http而不是https。改变这个解决了我的问题。

在index.html中我改变了

// This is called initially
function init() {
  var apiName = 'messaging'
  var apiVersion = 'v1'
  // set the apiRoot to work on a deployed app and locally
  var apiRoot = '//' + window.location.host + '/_ah/api';

  var callback = function() {
    enableClick();
  }
  gapi.client.load(apiName, apiVersion, callback, apiRoot);
}

// This is called initially
function init() {
  var apiName = 'messaging'
  var apiVersion = 'v1'
  // set the apiRoot to work on a deployed app and locally
  var apiRoot = 'https://' + window.location.host + '/_ah/api';

  var callback = function() {
    enableClick();
  }
  gapi.client.load(apiName, apiVersion, callback, apiRoot);
}