如何在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() 函数中接收)

如何在 PHP 中构建一个 android 推送通知服务器

如何在android中实现svm模型?

如何在android中实现BottomAppBar?

如何在 Xamarin.Android 中实现 RewardedAdLoadCallback?

如何在 Android Studio 中实现 razorpay 定期付款?