如何在android中实现C2DM?
Posted
技术标签:
【中文标题】如何在android中实现C2DM?【英文标题】:How to implement C2DM in android? 【发布时间】:2011-06-25 08:35:55 【问题描述】:我正在搜索如何使用 C2DM 发送通知。我发现了一些东西并使用它可以生成Registration Key
以及Authentication Key
。
但在那之后,在ServerSimulator
类(服务器端代码)中,我得到了 401 错误(401 未经授权)。现在我手动传递了用户名和密码,并在我的 android 设备中同步了它们。我得到和以前一样的错误。
当我点击发送消息按钮时,我遇到了这个问题..
我被这个查询卡住了。有没有人设法做到这一点?
public class ServerSimulator extends Activity
private SharedPreferences prefManager;
private final static String AUTH = "authentication";
private static final String UPDATE_CLIENT_AUTH = "Update-Client-Auth";
public static final String PARAM_REGISTRATION_ID = "registration_id";
public static final String PARAM_DELAY_WHILE_IDLE = "delay_while_idle";
public static final String PARAM_COLLAPSE_KEY = "collapse_key";
private static final String UTF8 = "UTF-8";
// Registration is currently hardcoded
private final static String YOUR_REGISTRATION_STRING = "APA91bFkxmtIj5XiBU-Cze64s0gXNb7OmiWWZg-qLKibpLsVXaWq1X7hoRV9Ld9COYXirZAgkYegZBdBfUGt3lgtuhNJopvHB0KJ5ZyJ6Kt_HYRrZhgdJ1Y";
private SharedPreferences prefs;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
prefManager = PreferenceManager.getDefaultSharedPreferences(this);
@Override
public boolean onCreateOptionsMenu(Menu menu)
getMenuInflater().inflate(R.menu.mymenu, menu);
return super.onCreateOptionsMenu(menu);
@Override
public boolean onOptionsItemSelected(MenuItem item)
switch (item.getItemId())
case R.id.menuitem_user:
Intent intent = new Intent(this, UserPreferences.class);
startActivity(intent);
break;
default:
break;
return false;
public void getAuthentification(View view)
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(this);
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(
"https://www.google.com/accounts/ClientLogin");
try
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
nameValuePairs.add(new BasicNameValuePair("Email","myEmail id")));
nameValuePairs.add(new BasicNameValuePair("Passwd","my password")));
nameValuePairs.add(new BasicNameValuePair("accountType", "GOOGLE"));
nameValuePairs.add(new BasicNameValuePair("source","Google-cURL-Example"));
nameValuePairs.add(new BasicNameValuePair("service", "ac2dm"));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null)
Log.e("HttpResponse", line);
if (line.startsWith("Auth="))
Editor edit = prefManager.edit();
edit.putString(AUTH, line.substring(5));
edit.commit();
String s = prefManager.getString(AUTH, "n/a");
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
catch (IOException e)
e.printStackTrace();
public void showAuthentification(View view)
String s = prefManager.getString(AUTH, "n/a");
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
public void sendMessage(View view)
try
Log.e("Tag", "Started");
String auth_key = prefManager.getString(AUTH, "n/a");
// Send a sync message to this Android device.
StringBuilder postDataBuilder = new StringBuilder();
postDataBuilder.append(PARAM_REGISTRATION_ID).append("=")
.append(YOUR_REGISTRATION_STRING);
// if (delayWhileIdle)
// postDataBuilder.append("&").append(PARAM_DELAY_WHILE_IDLE)
// .append("=1");
//
postDataBuilder.append("&").append(PARAM_COLLAPSE_KEY).append("=")
.append("0");
postDataBuilder.append("&").append("data.payload").append("=")
.append(URLEncoder.encode("Lars war hier", UTF8));
byte[] postData = postDataBuilder.toString().getBytes(UTF8);
// Hit the dm URL.
URL url = new URL("https://android.clients.google.com/c2dm/send");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
conn.setRequestProperty("Content-Length",
Integer.toString(postData.length));
conn.setRequestProperty("Authorization", "GoogleLogin auth="
+ auth_key);
OutputStream out = conn.getOutputStream();
out.write(postData);
out.close();
int responseCode = conn.getResponseCode();
Log.e("Tag", String.valueOf(responseCode));
// Validate the response code
if (responseCode == 401 || responseCode == 403)
// The token is too old - return false to retry later, will
// fetch the token
// from DB. This happens if the password is changed or token
// expires. Either admin
// is updating the token, or Update-Client-Auth was received by
// another server,
// and next retry will get the good one from database.
Log.e("C2DM", "Unauthorized - need token");
// Check for updated token header
String updatedAuthToken = conn.getHeaderField(UPDATE_CLIENT_AUTH);
if (updatedAuthToken != null && !auth_key.equals(updatedAuthToken))
Log.i("C2DM",
"Got updated auth token from datamessaging servers: "
+ updatedAuthToken);
Editor edit = prefManager.edit();
edit.putString(AUTH, updatedAuthToken);
String responseLine = new BufferedReader(new InputStreamReader(
conn.getInputStream())).readLine();
// NOTE: You *MUST* use exponential backoff if you receive a 503
// response code.
// Since App Engine's task queue mechanism automatically does this
// for tasks that
// return non-success error codes, this is not explicitly
// implemented here.
// If we weren't using App Engine, we'd need to manually implement
// this.
if (responseLine == null || responseLine.equals(""))
Log.i("C2DM", "Got " + responseCode
+ " response from Google AC2DM endpoint.");
throw new IOException(
"Got empty response from Google AC2DM endpoint.");
String[] responseParts = responseLine.split("=", 2);
if (responseParts.length != 2)
Log.e("C2DM", "Invalid message from google: " + responseCode
+ " " + responseLine);
throw new IOException("Invalid response from Google "
+ responseCode + " " + responseLine);
if (responseParts[0].equals("id"))
Log.i("Tag", "Successfully sent data message to device: "
+ responseLine);
if (responseParts[0].equals("Error"))
String err = responseParts[1];
Log.w("C2DM",
"Got error response from Google datamessaging endpoint: "
+ err);
// No retry.
throw new IOException(err);
catch (Exception e)
throw new RuntimeException(e);
【问题讨论】:
【参考方案1】:您目前正在以较旧的方式实施 C2DM。您正在做的方式提供了很多可定制性,但没有多少人需要这种可定制性。有了新的 Google Plugin 2.4 Beta,他们实际上已经开始将其全部集成,这非常好。
我强烈建议您观看 Google IO Android + AppEngine。他们展示了如何非常简单地将 Android、AppEngine 和 C2DM 集成在一起。您可以创建他们称之为“AppEngine Connected Android Project”的东西。因此,您无需制作 ServerSimulator,而是实际使用可以免费设置的真实服务器。
http://www.youtube.com/watch?v=M7SxNNC429U
如果您想移动到另一台服务器,则必须重新编写服务器端代码,然后只需更改它发布到的 URL。很简单。我强烈建议您查看此示例并使用股票代码,因为它可以让您立即使用:
仅供参考,在注册到真正托管的 appengine 服务器时,代码中目前存在一个错误(因为它在 beta atm 中)(在本地一切正常)。它尝试发送过期的注册令牌。我一直在与谷歌开发人员联系,他们为我提供了修复。让我知道你是否达到了你需要的程度。
【讨论】:
嗨 spierce7,感谢您的回答。但我不明白你到底想说什么。你能给我示例代码吗?谢谢 嗨 spierce7 - 我现在就是这个时候 - 有没有机会发布你的修复? 他们已经更新了 2.4 测试版(我什至不确定 2.4 GPE 是否仍处于测试阶段)。不管怎样,随着我的更新,我创建了一个新项目,他们给我的更改现已添加到官方项目中。以上是关于如何在android中实现C2DM?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 C2DM 消息显示为推送通知(在 GCMIntentService 类的 onMessage() 函数中接收)