GCM推送消息编码

7
我正在尝试使用以下代码发送推送通知:

我正在尝试使用以下代码发送推送通知:

    Message message = new Message.Builder().addData("appName", appData.name)
.addData("message", pushData.message).build();

在接收端,我的代码如下:
String message = intent.getStringExtra("message");

当消息是英文、拉丁字符集时,一切正常。但是,当我尝试其他语言或字符 ç 时,它们会以问号的形式出现或从字符串中删除。

注意:它已经编码为 utf-8。


我有几乎相同的代码 intent.getExtras().getString("message"); 但它可以工作,这让我想知道是否信息已经损坏。你可能需要检查你正在使用的编码方式。 - Gabriel Netto
你使用哪个服务器发送推送通知? - Gabriel Netto
我也遇到了同样的问题。我查看了Android源代码,发现com.google.android.gcm.server.Sender.sendNoRetry(Message, List<String>)方法执行以下操作:HttpURLConnection conn = post(GCM_SEND_ENDPOINT, "application/json", requestBody)。这似乎没有指定“application/json; charset=utf-8”,这意味着默认平台字符集将会生效。这不是一个很大的问题吗? - Yepher
深入挖掘后,我发现com.google.android.gcm.server.Sender.post(String, String, String)使用byte[] bytes = body.getBytes()。他们为什么要使用平台字符集? - Yepher
@Yepher,请查看下面的解决方案。 - Japes
显示剩余4条评论
5个回答

9

Java服务器

Message messagePush = new Message.Builder().addData("message", URLEncoder.encode("your message éèçà", "UTF-8")))

安卓应用程序

String message = URLDecoder.decode(intent.getStringExtra("message"), "UTF-8");

这个方法不适用于主题消息。只适用于一般的GCM消息。你有类似主题消息的解决方案吗? - Japes

2

我遇到了同样的问题。在 Android 客户端接收时,非 ASCII 字符会出现损坏。我个人认为这是 Google GCM 服务库实现中的问题。

在 Android GCM 库中,我看到了以下方法:

com.google.android.gcm.server.Sender.sendNoRetry(Message, List<String>) 

这个方法执行以下操作:

HttpURLConnection conn = post(GCM_SEND_ENDPOINT, "application/json", requestBody) 

他们至少应该指定“application/json; charset=utf-8”或使用的任何编码,或者最好强制使用UTF-8。这不是一个大问题吗?
更深入地挖掘,我发现了这种方法:
com.google.android.gcm.server.Sender.post(String, String, String) 

这个功能会:

byte[] bytes = body.getBytes()

为什么他们使用平台默认字符集?尤其是因为它不太可能与设备的默认字符集对齐。

解决方法

在JVM中传递以下属性作为参数 "-Dfile.encoding=UTF-8",这指示Java在执行诸如 "blah".getBytes() 之类的操作时使用UTF-8作为平台默认字符集。虽然这是一种不好的做法,但当它是别人的库时,你还能怎么办呢?


他们更新了gcm-server库,和你建议的一样。然而,对我来说仍然无法工作,我不断收到损坏的消息。有什么想法吗? - Gokhan Arik

2
我遇到了与gcm-server库类似的问题。我的解决方法是使用自定义发送器覆盖post方法,并在getBytes()调用中使用UTF8编码。它适用于Google App Engine。
自定义发送器类的代码:
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.logging.Level;

import com.google.android.gcm.server.Sender;

/**
 * Workaround to avoid issue #13 of gcm-server
 * @see https://code.google.com/p/gcm/issues/detail?id=13&q=encoding
 * 
 * @author rbarriuso /at/ tribalyte.com
 *
 */
public class Utf8Sender extends Sender {

    private final String key;

    public Utf8Sender(String key) {
        super(key);
        this.key = key;
    }

    @Override
    protected HttpURLConnection post(String url, String contentType, String body) throws IOException {
        if (url == null || body == null) {
            throw new IllegalArgumentException("arguments cannot be null");
        }
        if (!url.startsWith("https://")) {
            logger.warning("URL does not use https: " + url);
        }
        logger.fine("Utf8Sender Sending POST to " + url);
        logger.finest("POST body: " + body);
        byte[] bytes = body.getBytes(UTF8);
        HttpURLConnection conn = getConnection(url);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setFixedLengthStreamingMode(bytes.length);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", contentType);
        conn.setRequestProperty("Authorization", "key=" + key);
        OutputStream out = conn.getOutputStream();
        try {
            out.write(bytes);
        } finally {
            close(out);
        }
        return conn;
    }

    private static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                // ignore error
                logger.log(Level.FINEST, "IOException closing stream", e);
            }
        }
    }

}

1
这些是一些好的解决方案,但是它们并没有帮助我,因为我正在使用主题消息来发送通知。根据this
HTTP标头必须包含以下标头: 授权:key = YOUR_API_KEY 对于JSON,Content-Type:application/json; 对于纯文本,application/x-www-form-urlencoded; charset = UTF-8。 如果省略Content-Type,则假定格式为纯文本。
然而,由于我正在使用云端终端和我的应用程序(已经在野外)期望JSON格式,所以将请求格式化为纯文本不可行。解决方案是忽略上面的文档,并在我的后端将http请求头格式化为以下形式:
 connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");

就像魔术一样,突然间所有的特殊字符(即:日文)都可以通过我的应用程序进行传输,而无需进行前端更改。我的完整http post代码如下(其中payload = Cloud端点模型对象,通过Gson转换为json<String>):
 try {
        URL url = new URL("https://gcm-http.googleapis.com/gcm/send");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
        connection.setRequestProperty("Authorization", "key=" + API_KEY);
        connection.setRequestMethod("POST");

        byte[] bytes=payload.getBytes("UTF-8");
        OutputStream out = connection.getOutputStream();
        try {
            out.write(bytes);
        } finally {
            out.close();
        }

        if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
            // OK
            log.warning("OK");
        } else {
            // Server returned HTTP error code.
            log.warning("some error "+connection.getResponseCode());
        }
    } catch (MalformedURLException e) {
        // ...
    }

1
谢谢Japes。即使将“Content-Type”设置为“application/json”,out.write(payload.getBytes("UTF-8"))对我来说也是有效的。 - Pioneer

0

使用调试器中断程序并查看您认为正在发送的消息的字节


有趣的是,我怎么没想到那个!它只是告诉我我的数据是错误的... - hungary54

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接