我正在为我的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 »</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。
答案 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);
}