C2DMBroadcastReceiver的onReceive未执行(For Registration)

时间:2011-07-15 10:49:41

标签: android broadcastreceiver android-c2dm

开发C2DM Messaging应用程序。在那里,我命令使用C2DMBroadcastReceiverC2DMBaseReceiverC2DMMessaging类接收注册ID。我将在我的包中C2DMReceiver扩展C2DMBaseReceiver

这是我的代码段

C2DMMessaging.java

package com.google.android.c2dm;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;

public class C2DMessaging {
    public static final String EXTRA_SENDER = "sender";
    public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
    public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
    public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
    public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
    public static final String BACKOFF = "backoff";
    public static final String GSF_PACKAGE = "com.google.android.gsf";

    // package
    static final String PREFERENCE = "com.google.android.c2dm";

    private static final long DEFAULT_BACKOFF = 30000;

    /**
     * Initiate c2d messaging registration for the current application
     */
    public static void register(Context context,
            String senderId) {
        Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
        registrationIntent.setPackage(GSF_PACKAGE);
        registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
                PendingIntent.getBroadcast(context, 0, new Intent(), 0));
        registrationIntent.putExtra(EXTRA_SENDER, senderId);
        context.startService(registrationIntent);
        Log.e("C2DM Services","Service Started");

    }

    /**
     * Unregister the application. New messages will be blocked by server.
     */
    public static void unregister(Context context) {
        Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
        regIntent.setPackage(GSF_PACKAGE);
        regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context,
                0, new Intent(), 0));
        context.startService(regIntent);
        Log.e("C2DM Services","unregister");
    }

    /**
     * Return the current registration id.
     *
     * If result is empty, the registration has failed.
     *
     * @return registration id, or empty string if the registration is not complete.
     */
    public static String getRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        String registrationId = prefs.getString("dm_registration", "");
        Log.e("C2DM Services","get registration id");
        return registrationId;

    }

    public static long getLastRegistrationChange(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Log.e("C2DM Services","getlastregchange");
        return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
    }

    static long getBackoff(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Log.e("C2DM Services","getbackoff");
        return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
    }

    static void setBackoff(Context context, long backoff) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putLong(BACKOFF, backoff);
        editor.commit();
        Log.e("C2DM Services","setbackoff");
    }

    // package
    static void clearRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", "");
        editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
        editor.commit();
        Log.e("C2DM Services","clearregid");
    }

    // package
    static void setRegistrationId(Context context, String registrationId) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", registrationId);
        editor.commit();
        Log.e("C2DM Services","setregid");
    }
}

C2DMBroadcastReceiver.java

package com.google.android.c2dm;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class C2DMBroadcastReceiver extends BroadcastReceiver {

    @Override
    public final void onReceive(Context context, Intent intent) {
        // To keep things in one place.
           Log.e("C2DM Broadcast receiver","onReceive");
        C2DMBaseReceiver.runIntentInService(context, intent);
        setResult(Activity.RESULT_OK, null /* data */, null /* extra */);        
    }

}

清单文件

<permission android:name="com.sample.gt.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
 <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.sample.gt.permission.C2D_MESSAGE" />
    <!-- Permissions -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <service android:name="com.sample.gt.c2dm.C2DMReceiver" />

    <!--
        Only C2DM servers can send messages for the app. If permission is not
        set - any other app can generate it
    -->
    <receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND">
        <!-- Receive the actual message -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="com.sample.gt.c2dm" />
        </intent-filter>
        <!-- Receive the registration id -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="com.sample.gt.c2dm" />
        </intent-filter>
    </receiver>

C2DMMessaging.java

public class C2DMessaging {
    public static final String EXTRA_SENDER = "sender";
    public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
    public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
    public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
    public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
    public static final String BACKOFF = "backoff";
    public static final String GSF_PACKAGE = "com.google.android.gsf";


    // package
    static final String PREFERENCE = "com.google.android.c2dm";

    private static final long DEFAULT_BACKOFF = 30000;

    /**
     * Initiate c2d messaging registration for the current application
     */
    public static void register(Context context,
            String senderId) {
        Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
        registrationIntent.setPackage(GSF_PACKAGE);
        registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
                PendingIntent.getBroadcast(context, 0, new Intent(), 0));
        registrationIntent.putExtra(EXTRA_SENDER, senderId);
        context.startService(registrationIntent);
        Log.e("C2DM Services","Service Started");
   }
   ..........

}

现在我的问题是,

我通过传递上下文从我的活动中调用C2DMMessaging的注册,该服务是在C2DMMessaging中创建的,之后我没有收到 C2DMBroadcastReceiver的onReceive()

这是我从vogille.de获得的代码。如果我这样使用它,这工作正常,但是当我在我的应用程序中使用它时,这个问题就会出现。

我已经搜索了一些用谷歌搜索的内容,我发现manifest文件中的问题可能

我不知道我错在哪里。 有人可以提供帮助吗?

3 个答案:

答案 0 :(得分:7)

由于一个不明原因,您的接收器类C2DMReceiver必须位于程序包的根目录中,该程序包在清单中声明为您的程序包名称。 这是我设法让这个工作的唯一方法...所以而不是:

service android:name="com.sample.gt.c2dm.C2DMReceiver" 

尝试

service android:name=".C2DMReceiver"

这是我的清单摘录(权限除外)

 <!--C2DM -->
    <service android:name=".C2DMReceiver" />
    <receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND">
        <!-- Receive the actual message -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="my.package" />
        </intent-filter>
        <!-- Receive the registration id -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="my.package" />
        </intent-filter>
    </receiver>

答案 1 :(得分:4)

我感觉到你的痛苦,我努力让C2DM在其他网站中看到vogille.de。最终为我工作的是使用eclipse“App Engine Connected Android Project”创建的C2DM.jar文件(在File&gt; New&gt; Project&gt; Google下)。

注意:在撰写本文时,您必须安装该插件的测试版才能拥有此选项! http://code.google.com/eclipse/beta/docs/download.html

我的清单文件的相关部分:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      ...
>
    <permission
        android:name="my_package_name.permission.C2D_MESSAGE"
        android:protectionLevel="signature"
    />
    <uses-permission android:name="my_package_name.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application
       ...
    >
        <!-- Only C2DM servers can send messages for the app. If permission is not set - any other app can generate it --> 
        <receiver
            android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND"
        >
            <!-- Receive the actual message -->
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="my_package_name" />
            </intent-filter>
            <!-- Receive the registration id -->
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="my_package_name" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

以下是我用来与C2DM服务进行交互的代码:

package my_package_name

import com.google.android.c2dm.C2DMBaseReceiver;
import com.google.android.c2dm.C2DMessaging;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

/**
 * Receive C2DM state changes.
 * 
 * Be careful: the various onX receivers may be called from a mysterious
 * context -- in particular they cannot safely create new AsyncTask objects.
 */
public class C2DMReceiver extends C2DMBaseReceiver {

    // GMail account associated with the C2DM application.  Must agree with
    // what the 3rd party server uses to authenticate with C2DM.
    private static final String C2DM_SENDER = "my_email@gmail.com";

    // -----------------------------------------------------------------

    /**
     * Ask this device to register for C2DM messaging.  Will trigger
     * onRegistered (or onError) when finished.
     */
    public static void register(Context context) {
        C2DMessaging.register(context, C2DM_SENDER);
    }

    /**
     * Unregister this device from further C2DM messaging.
     */
    public static void unregister(Context context) {
        C2DMessaging.unregister(context);
    }

    // -----------------------------------------------------------------

    public C2DMReceiver() {
        super(C2DM_SENDER);
    }

    @Override
    protected void onMessage(Context context, Intent intent) {
        // Extras contains whatever your server put into the C2DM message.
        final Bundle extras = intent.getExtras();
    }

    @Override
    public void onError(Context context, String error_id) {
    }

    @Override
    public void onRegistered(Context context, String registration_id) {
    }

    @Override
    public void onUnregistered(Context context) {
    }
}

生成的示例代码包括基于Java的AppEngine应用程序。我正在使用python,这是完成这篇文章的相关代码:

class C2dmAuthToken(db.Model):
    """Maintain an auth token used to talk to the C2DM service.  There is at
    most one of these records."""
    role_email = db.StringProperty(indexed=False, default='my_email@gmail.com')
    passwd = db.StringProperty(indexed=False, default='my_password')
    token = db.TextProperty(indexed=False, default='')

class C2dmRegistration(db.Model):
    """Map from user to the C2DM registration id needed for the C2DM
    service to send messages to the registered device."""
    user_id = db.IntegerProperty(required=True)
    registration_id = db.StringProperty(indexed=False)

class RegisterHandler(MyRequestHandler.MyRequestHandler):
    def post(self):
        # Parse arguments.
        user_id = self.parseId('user_id')
        registration_id = self.parseStr('registration_id')

        # Create or update the device record.
        record = C2dmRegistration.gql('WHERE user_id = :1', user_id).get()
        if record == None:
            record = C2dmRegistration(user_id=user_id)
        record.registration_id = registration_id
        record.put()

class UnregisterHandler(MyRequestHandler.MyRequestHandler):
    def post(self):
        # Parse arguments.
        user_id = self.parseId('user_id')

        # Unregister this device.
        record = C2dmRegistration.gql('WHERE user_id = :1', user_id).get()
        if record != None:
            record.delete()

def getAuthToken():
    """Return an auth token associated with the role account.  Login to
    Google and store the auth token if needed."""
    token_record = C2dmAuthToken.all().get()
    if token_record == None:
        token_record = C2dmAuthToken()

    if len(token_record.token) > 0:
        return token_record.token

    form_fields = {
        'accountType' : 'GOOGLE',
        'Email' : token_record.role_email,
        'Passwd' : token_record.passwd,
        'service' : 'ac2dm',
        'source' : 'my_source_name',
    }
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded',
    }
    result = urlfetch.fetch(url='https://www.google.com/accounts/ClientLogin',
                            payload=urllib.urlencode(form_fields),
                            method=urlfetch.POST,
                            headers=headers)
    if result.status_code != 200:
        logging.warning('getAuthToken: client login http error %d' % result.status_code)
        return None

    for line in result.content.split('\n'):
        if line.startswith('Auth='):
            token_record.token = line[5:]

    if len(token_record.token) == 0:
        logging.warning('getAuthToken: no token')
        return None

    logging.info('getAuthToken allocated new token %s' % token_record.token)
    token_record.put()
    return token_record.token

def setAuthToken(token):
    """Update the auth token."""
    token_record = C2dmAuthToken.all().get()
    if token_record == None:
        token_record = C2dmAuthToken()
    token_record.token = token
    token_record.put()

def sendMessage(dst_user_id, message):
    """Send a message to the dst user's device using C2DM."""

    registration_record = C2dmRegistration.gql('WHERE user_id = :1', dst_user_id).get()
    if registration_record == None:
        logging.warning('SendMessage: no such dst_user_id %ld' % dst_user_id)
        return False

    # Use http and not https to avoid an invalid certificate error.
    # Since the request originates inside google hopefully it is
    # never snoop-able to the outside world, and does not contain
    # critically secure info (such as the role password).
    form_fields = {
        'registration_id' : registration_record.registration_id,
        'collapse_key' : '%d' % int(time.time() * 1000),
        'data.message' : message,
    }
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded',
        'Authorization': 'GoogleLogin auth=%s' % getAuthToken(),
    }
    result = urlfetch.fetch(url='http://android.apis.google.com/c2dm/send',
                            payload=urllib.urlencode(form_fields),
                            method=urlfetch.POST,
                            headers=headers)
    if result.status_code != 200:
        logging.warning('sendMessage: http error %d' % result.status_code)
        return None
    if 'Update-Client-Auth' in result.headers:
        logging.info('updating auth token')
        setAuthToken(result.headers['Update-Client-Auth'])
    return True

def main():    
    application = webapp.WSGIApplication([
        ('/c2dm/register', RegisterHandler),
        ('/c2dm/unregister', UnregisterHandler),
       ], debug=True)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()

您的Android应用应该调用/ c2dm / register和/ c2dm / unregister方法来设置和清除设备c2dm令牌和后端。其他后端代码应调用sendMessage,要求Google将消息中继到设备。

此代码包含您的Gmail密码。我使用一次性gmail地址来满足我的c2dm需求,并通过直接数据存储区操作实际设置密码,而不是在代码中使用纯文本。即使有人知道更好的管理身份验证的方式,我也很乐意听到它。

我希望这有帮助!

答案 2 :(得分:3)

首先让我说你的IDE设置不是问题,这个过程甚至不需要服务器(现在)使用app引擎,这是在文件&gt;下添加Google选项的原因。新&gt;项目

Android设备联系谷歌C2DM服务器获取注册ID如果流程成功谷歌回复注册ID,以后您可以发送到您的服务器,现在我们将尝试让该过程工作并拥有在设备上注册ID,然后你可以处理它。

据我了解,您创建了一个Android项目,将他们在Chrome到手机示例中使用的Google课程,他们的教程中提供了vogella.de

在您的启动活动中执行此操作后,您调用了方法C2DMMessaging.register(this, "example@gmail.com);

这里有很多错误: 附:根据我的标准,这些是根据他们的可能性排序的,我也排除了一些似乎不适用于你提到的片段的案例。

  1. 您尚未在谷歌注册角色电子邮件帐户。
    请转到http://code.google.com/android/c2dm/signup.html并执行以下操作 仔细阅读后接受许可证 尽可能准确地填写所有必需的信息,并注意以下字段:
    “您的Android应用包名称”和“角色(发件人)帐户电子邮件”
    此角色电子邮件是您将在C2DMMessaging.register方法中使用的电子邮件,以及稍后在您的应用程序服务器上使用的电子邮件
  2. 您正在测试未正确配置此测试的Android模拟器 为您的Android模拟器配置此任务,应执行以下操作:
    单击菜单Window&gt;创建一个新的AVD(Android虚拟设备)。 Android SDK和AVD Manager&gt;虚拟设备&gt;新
    选择目标Google API(Google Inc.) - API级别8(如果此选项不可用,请从可用软件包&gt;第三方插件&gt; Google Inc.下载) 请随意填写其余部分 启动新的AVD并导航到设置&gt;账户&amp;同步&gt;添加帐户&gt; Google&gt;任何谷歌帐户。
    重复你的测试
  3. 在我填写其他案例之前,我需要查看您正在使用的其他Google课程以及您创建的服务C2DMReceiver

  4. 由于是一个声誉低的用户,我无法发表评论