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

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

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

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

安卓 list :

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

主要 Activity :

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");
}
}

Intent 服务

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();
        }
    });
}
}

消息端点:

@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();
}

}

注册记录

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

appengine-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 key ,并将 IPS 设置为允许的任何 IP。

最佳答案

唯一的问题是我在 GCM 的主页 url 中有 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);
}

关于android - GCM + AppEngine - 应用程序不接收推送通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24746740/

相关文章:

ios - 延迟推送通知并检查用户是否启用了它

google-app-engine - 如何禁用 `gcloud preview app run` 的健康检查

android - 单个设备突然停止从我的服务器获取 C2DM 推送

android - 在 Android 上制作交互式触摸对象

android - 为 Android 应用程序创建 SQLite 数据库

android - GoogleJsonResponseException : 401 Unauthorized calling endpoint with OAuth2 protection

python - 如何使用服务帐户确定 Google BigQuery 作业的状态?

node.js - 如何使用 Node.js 的 web-push 包向多个订阅者发送通知?

android - 保存图片到sdcard android目录问题

java - 带 fragment 的选项卡布局