- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
因此,Google 曾经有一个不错的页面,他们在该页面上提供了有关如何在该页面上实现 GCM Xmpp 服务器的代码示例: https://developers.google.com/cloud-messaging/ccs
每当我想查看实现时,我都已将此答案添加为书签 Confused about Google GCM XMPP
但是该答案中链接的所有内容都消失了。 Google 删除了它们。
但是现在,他们删除了所有内容,并添加了一个页面来解释它的作用。当然可以,但是示例 Java 代码会很好。我试图访问他们链接到的 github 上的示例站点 https://github.com/google/gcm
但它只提供了 HTTP GCM 服务器版本的示例。
有人可以指点我一个有效的代码示例吗?或者是否有一个可以与 c# 一起使用的库?如果没有,我也会选择 Java 版本。
谢谢。
最佳答案
这是我为类示例编写的一个(删除了一些非 XMPP 部分以使其足够小以便我可以发布代码,但完整的服务器位于 http://people.eku.edu/styere/GcmPushServer.java ):
// derived from https://developer.android.com/google/gcm/ccs.html
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
//import javax.swing.JScrollPane;
//import javax.swing.SwingUtilities;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.json.simple.JSONValue;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Random;
import java.net.*;
import java.io.*;
import java.util.*;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.HttpsURLConnection;
// list of all users and associated devices
class UserList {
// (--deleted--)
// constructor/read existing data from file
public UserList() {
// (--deleted--)
}
// add a new device to an existing user (called from networking thread)
// duplicate IDs are quietly accepted
public synchronized void addDevice( String uname, String newRegToken ) {
// (--deleted--)
}
// generate a group address/notification key
// !!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!
// If the group addr/notification key is lost, it currently CANNOT be recovered/rebuilt
public String createNotificationKey( String nKeyName, String addr ) {
String[] idset = new String[1];
idset[0] = addr;
String newGroupAddr;
try {
// create a https connection to create the notification_key
URL url = new URL( "https://android.googleapis.com/gcm/notification" );
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
// send as a POST request
conn.setRequestMethod("POST");
// add request parameters
conn.addRequestProperty( "project_id", ""+GcmPushServer.senderId );
conn.addRequestProperty( "Authorization", "key="+GcmPushServer.password );
// use "set" since this may already exist
conn.setRequestProperty( "Content-Type", "application/json" );
// create data to send with request
Map<String,Object> sendData = new HashMap<>();
// create a new notification key
sendData.put( "operation", "create" );
sendData.put( "notification_key_name", (--deleted--) );
sendData.put( "registration_ids", (--deleted--) );
String strData = JSONValue.toJSONString(sendData);
// Send post request
conn.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes( strData );
wr.flush();
wr.close();
// successful?
int rc = conn.getResponseCode();
//System.out.println("ResponseCode = " + rc );
// read the response
Scanner input = new Scanner( conn.getInputStream() );
String resp="";
// read/display lines
while( input.hasNextLine() ) {
String s = input.nextLine();
resp += s;
}
//System.out.println("Response content: " + resp);
if ( rc == HttpsURLConnection.HTTP_OK ) {
JSONObject obj = (JSONObject) JSONValue.parse(resp);
newGroupAddr = (String) obj.get( "notification_key" );
//System.out.println(" new notification_key: " + newGroupAddr );
return newGroupAddr;
} else {
}
} catch ( Exception e ) {
//System.out.println("-- Exception: " + e.getMessage() );
}
return null;
}
// add/delete a single address to/from a notification key
// (add if doAdd is true, delete otherwise)
// removing final address will quietly delete the key
public static void modifyNotificationKey( String nKeyName, String nKey,
String addr, boolean doAdd ) {
String[] idset = new String[1];
idset[0] = addr;
try {
// create a https connection to create the notification_key
URL url = new URL( "https://android.googleapis.com/gcm/notification" );
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
// send as a POST request
conn.setRequestMethod("POST");
// add request parameters
conn.addRequestProperty( "project_id", ""+GcmPushServer.senderId );
conn.addRequestProperty( "Authorization", "key="+GcmPushServer.password );
// use "set" since this may already exist
conn.setRequestProperty( "Content-Type", "application/json" );
// create data to send with request
Map<String,Object> sendData = new HashMap<>();
// create a new notification key
sendData.put( "operation", doAdd? "add" : "remove" ); // add or delete key?
sendData.put( "notification_key_name", (--deleted--) );
sendData.put( "notification_key", (--deleted--) );
sendData.put( "registration_ids", (--deleted--) );
String strData = JSONValue.toJSONString(sendData);
//System.out.println("genGroupAddress POST data: " + strData );
// Send post request
conn.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes( strData );
wr.flush();
wr.close();
//conn.openConnection();
// successful?
int rc = conn.getResponseCode();
//System.out.println("ResponseCode = " + rc );
Scanner input = new Scanner( conn.getInputStream() );
// read/display lines
while( input.hasNextLine() ) {
System.out.println("Response content:");
String s = input.nextLine();
System.out.println( s );
}
} catch ( Exception e ) {
// do nothing
}
}
}
// text area for log messages
class LogTextArea extends JTextArea {
// update the "log" with the specified message
public void logMessage( String msg ) {
// (--deleted--)
}
}
class CcsServer {
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
// display/log area
LogTextArea logArea;
UserList users;
CcsServer( LogTextArea lta, UserList u ) {
logArea = lta;
users = u;
}
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
new PacketExtensionProvider() {
@Override
public PacketExtension parseExtension(XmlPullParser parser) throws
Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it
* will not accept any new downstream messages.
*/
protected volatile boolean connectionDraining = false;
/**
* Sends a downstream message to GCM.
*
* @return true if the message has been successfully sent.
*/
public boolean sendDownstreamMessage(String jsonRequest) throws
NotConnectedException {
if (!connectionDraining) {
send(jsonRequest);
return true;
}
logArea.logMessage("Dropping downstream message since the connection is draining");
return false;
}
/**
* Returns a random message id to uniquely identify a message.
*
* <p>Note: This is generated by a pseudo random number generator for
* illustration purpose, and is not guaranteed to be unique.
*/
public String nextMessageId() {
return "m-" + UUID.randomUUID().toString();
}
/**
* Sends a packet with contents provided.
*/
protected void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
/**
* Handles an upstream data message from a device application.
*
* <p>This sample echo server sends an echo message back to the device.
* Subclasses should override this method to properly process upstream messages.
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
@SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
// what type of upstream message is this?
if ( ! payload.containsKey( "my_action" ) )
{
// Hmmm - this shouldn't happen!
logArea.logMessage( "handleUpstreamMessage - incoming message is missing my_action" );
// just ignore the message
return;
}
// what action do they want?
String my_action = (String) payload.get( "my_action" );
if ( my_action.equals( "edu.eku.styere.gcmpushclient.REGISTER" ) ) {
// registration request
String username = (String) payload.get( "username" );
logArea.logMessage( "Registration request: user=" + username + ", ID/Token=" + from );
// save the information
users.addDevice( username, from );
return;
} else {
// take default action of echoing the message
payload.put("ECHO", "Application: " + category);
// Send an ECHO response back
String echo = createJsonMessage(from, nextMessageId(), payload,
"echo:CollapseKey", null, false);
try {
sendDownstreamMessage(echo);
} catch (NotConnectedException e) {
logArea.logMessage( "Not connected anymore, echo message is not sent: " + e.getMessage() );
}
}
}
/**
* Handles an ACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle ACKs.
*/
protected void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logArea.logMessage( "handleAckReceipt() from: " + from + ", messageId: " + messageId );
}
/**
* Handles a NACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logArea.logMessage( "handleNackReceipt() from: " + from + ", messageId: " + messageId );
}
protected void handleControlMessage(Map<String, Object> jsonObject) {
logArea.logMessage( "handleControlMessage(): " + jsonObject );
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logArea.logMessage( "Unrecognised control type: " + controlType + ". This could "+
"happen if new features are " + "added to the CCS protocol." );
}
}
/**
* Creates a JSON encoded GCM message.
*
* @param to RegistrationId of the target device (Required).
* @param messageId Unique messageId for which CCS sends an
* "ack/nack" (Required).
* @param payload Message content intended for the application. (Optional).
* @param collapseKey GCM collapse_key parameter (Optional).
* @param timeToLive GCM time_to_live parameter (Optional).
* @param delayWhileIdle GCM delay_while_idle parameter (Optional).
* @return JSON encoded GCM message.
*/
public static String createJsonMessage(String to, String messageId,
Map<String, String> payload, String collapseKey, Long timeToLive,
Boolean delayWhileIdle) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("to", to);
if (collapseKey != null) {
message.put("collapse_key", collapseKey);
}
if (timeToLive != null) {
message.put("time_to_live", timeToLive);
}
if (delayWhileIdle != null && delayWhileIdle) {
message.put("delay_while_idle", true);
}
message.put("message_id", messageId);
message.put("data", payload);
return JSONValue.toJSONString(message);
}
/**
* Creates a JSON encoded ACK message for an upstream message received
* from an application.
*
* @param to RegistrationId of the device who sent the upstream message.
* @param messageId messageId of the upstream message to be acknowledged to CCS.
* @return JSON encoded ack.
*/
protected static String createJsonAck(String to, String messageId) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("message_type", "ack");
message.put("to", to);
message.put("message_id", messageId);
return JSONValue.toJSONString(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* @param senderId Your GCM project number
* @param apiKey API Key of your project
*/
public void connect(long senderId, String apiKey)
throws XMPPException, IOException, SmackException {
ConnectionConfiguration config =
new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
connection = new XMPPTCPConnection(config);
connection.connect();
connection.addConnectionListener( new LoggingConnectionListener() );
// Handle incoming packets
connection.addPacketListener(new PacketListener() {
@Override
public void processPacket(Packet packet) {
logArea.logMessage( "Received: " + packet.toXML() );
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket =
(GcmPacketExtension) incomingMessage.
getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
@SuppressWarnings("unchecked")
Map<String, Object> jsonObject =
(Map<String, Object>) JSONValue.
parseWithException(json);
// present for "ack"/"nack", null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
handleControlMessage(jsonObject);
} else {
logArea.logMessage( "Unrecognised message type: " +
messageType.toString() );
}
} catch (ParseException e) {
logArea.logMessage( "Error parsing JSON " + json );
} catch (Exception e) {
logArea.logMessage( "Failed to process packet" );
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
connection.addPacketInterceptor(new PacketInterceptor() {
@Override
public void interceptPacket(Packet packet) {
logArea.logMessage( "Sent: " + packet.toXML());
}
}, new PacketTypeFilter(Message.class));
connection.login(senderId + "@gcm.googleapis.com", apiKey);
}
//---------- support classes ----------
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super( GCM_ELEMENT_NAME, GCM_NAMESPACE);
this.json = json;
}
public String getJson() {
return json;
}
@Override
public String toXML() {
return String.format("<%s xmlns=\"%s\">%s</%s>",
GCM_ELEMENT_NAME, GCM_NAMESPACE,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
class LoggingConnectionListener implements ConnectionListener {
@Override
public void connected(XMPPConnection xmppConnection) {
logArea.logMessage( "Connected." );
}
@Override
public void authenticated(XMPPConnection xmppConnection) {
logArea.logMessage( "Authenticated." );
}
@Override
public void reconnectionSuccessful() {
logArea.logMessage( "Reconnecting.." );
}
@Override
public void reconnectionFailed(Exception e) {
logArea.logMessage( "Reconnection failed.. " + e.getMessage() );
}
@Override
public void reconnectingIn(int seconds) {
logArea.logMessage( "Reconnecting in " + seconds + " secs" );
}
@Override
public void connectionClosedOnError(Exception e) {
logArea.logMessage( "Connection closed on error." );
}
@Override
public void connectionClosed() {
logArea.logMessage( "Connection closed." );
}
}
}
// a non-editable combo (drop-down) box for destination addresses
class DestComboBox extends JComboBox<String> {
// (deleted to save space)
}
// screen panel for sending a message
class SendMessagePanel extends JPanel implements ActionListener, ItemListener {
// Message Types
protected final int MSGTYPE_NOTIFICATION = 1;
protected final int MSGTYPE_NOTIFICATION_DATA = 2;
protected final int MSGTYPE_DATA_COLLAPSE = 3;
protected final int MSGTYPE_DATA_NONCOLLAPSE = 4;
// log window
LogTextArea msgs;
// server class
CcsServer ccsServer;
// constructor
SendMessagePanel( UserList u, LogTextArea m, CcsServer c ) {
// (deleted to save space)
}
// respond to the button
@Override
public void actionPerformed(ActionEvent e) {
String toAddr = "zzzzzz"; // destination address
// may be device, notification_key, or topic address
ttl = 2419200; // time-to-live in seconds
// what type of message?
int msgTypeIndex = (--deleted--);
// create the message
Map<String, Object> message = new HashMap<String, Object>();
message.put("to", toAddr);
if ( msgTypeIndex == MSGTYPE_NOTIFICATION ||
msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ||
msgTypeIndex == MSGTYPE_DATA_COLLAPSE ) {
// create a collapse key
message.put("collapse_key", "ck"+msgTypeIndex );
}
message.put("time_to_live", ttl);
message.put("message_id", ccsServer.nextMessageId());
// notification included?
if ( msgTypeIndex == MSGTYPE_NOTIFICATION ||
msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ) {
// create the notification payload
HashMap<String, String> notePayload = new HashMap<>();
notePayload.put( "title", "Gcm Push Message Example" );
notePayload.put( "body", (--deleted--) );
// identify which notifications should replace older versions
notePayload.put( "tag", "ntag" + msgTypeIndex );
notePayload.put( "icon", "@drawable/new_picture" ); // notification icon
// additional stuff if we also have data
if ( msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ) {
// what to do when the user opens the notification
notePayload.put( "click_action", "OPEN_MAIN_ACTIVITY" );
}
message.put( "notification", notePayload );
}
// data included?
if ( msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ||
msgTypeIndex == MSGTYPE_DATA_COLLAPSE ||
msgTypeIndex == MSGTYPE_DATA_NONCOLLAPSE ) {
HashMap<String, String> dataPayload = new HashMap<>();
dataPayload.put( "contents", (--deleted--) );
message.put("data", dataPayload);
}
// actually send the message
try {
ccsServer.sendDownstreamMessage( JSONValue.toJSONString(message) );
} catch (NotConnectedException enc ) {
msgs.logMessage( "Not connected anymore, echo message is not sent: " + enc.getMessage() );
}
}
}
class BorderPanel extends JPanel {
// (--deleted--)
}
public class GcmPushServer
{
// (--deleted--)
}
关于java - Google GCM XMPP 代码示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32060099/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!