OnReceive C2DMBroadcastReceiver не выполняется (для регистрации) - PullRequest
5 голосов
/ 15 июля 2011

Я занимаюсь разработкой приложения для обмена сообщениями C2DM. В этом случае я получаю регистрационный идентификатор, используя классы C2DMBroadcastReceiver, C2DMBaseReceiver и C2DMMessaging. Я буду 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, после этого я ничего не получаю в onReceive () C2DMBroadcastReceiver .

Это код, который я получил от vogille.de . Это работает нормально, если я использую это как таковое, но когда я использую это в моем приложении, эта проблема приходит.

Я прошёл кое-какую публикацию в поиске чего-то, в котором обнаружил, что проблема может быть в manifest файле.

Я не знаю, где я не прав. Может кто-нибудь помочь с этим?

Ответы [ 3 ]

7 голосов
/ 19 июля 2011

По непонятной причине ваш класс получателя, C2DMReceiver, должен находиться в корне вашего пакета, который объявлен как имя вашего пакета в манифесте.Это единственный способ, которым мне удалось заставить это работать ... поэтому вместо:

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

try

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>
4 голосов
/ 18 июля 2011

Я чувствую вашу боль, я изо всех сил пытался заставить C2DM работать, а также смотрел на vogille.de среди других сайтов.В итоге мне удалось использовать файл C2DM.jar, созданный затмением «Проект Android App Engine Connected» (в меню «Файл»> «Создать»> «Проект»> 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) {
    }
}

Образец сгенерированного кодавключает приложение AppEngine на основе Java.Я использую 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 и фактически устанавливаю пароль с помощью прямого манипулирования хранилищем данных вместо того, чтобы использовать его в открытом тексте в коде.Даже если кто-нибудь знает лучший способ управления аутентификацией, я бы хотел услышать об этом.

Надеюсь, это поможет!

3 голосов
/ 19 июля 2011

Позвольте мне начать с того, что ваша настройка IDE здесь не является проблемой, и этому процессу даже не нужен сервер (пока) с механизмом приложений, который добавляет параметр Google в файл> Создать> Проект

Android-устройство связывается с серверами Google C2DM, чтобы получить регистрационный идентификатор, если процесс завершается успешно, Google отвечает регистрационным идентификатором, который позже вы можете отправить на свой сервер, а сейчас мы попытаемся заставить процесс работать и получить регистрационный идентификатор на устройстве, потом вы можете справиться с этим.

Как я понял, вы создали проект Android, взяли классы Google, которые они использовали в своем примере с chrome для телефона, который vogella.de предоставил в своем уроке

после этого при запуске вы вызвали метод C2DMMessaging.register(this, "example@gmail.com);

есть ряд вещей, которые могут быть не правы здесь: постскриптум они упорядочены в соответствии с их вероятностью в соответствии с моими стандартами, а также я исключил некоторые случаи, которые, по-видимому, не относятся к упомянутым вами фрагментам.

  1. Вы не зарегистрировали свою учетную запись электронной почты в Google.
    Пожалуйста, перейдите на http://code.google.com/android/c2dm/signup.html и сделайте следующее принять лицензию после прочтения внимательно
    Заполните всю необходимую информацию как можно точнее и обратите внимание на следующие поля:
    "Имя пакета вашего приложения Android" и "Электронная почта учетной записи роли (отправителя)"
    эта электронная почта роли, которую вы будете использовать в методе C2DMMessaging.register и на сервере вашего приложения позже
  2. Вы тестируете на эмуляторе Android, который неправильно настроен для этого теста.
    Чтобы ваш эмулятор Android был настроен для этой задачи, необходимо выполнить следующее:
    Создайте новое AVD (виртуальное устройство Android), щелкнув меню «Окно»> Android SDK и AVD Manager> Виртуальные устройства> «Новое»
    . выберите целевой API Google (Google Inc.) - API уровня 8 (если этот параметр недоступен, загрузите его из списка Доступные пакеты> Дополнительные модули сторонних производителей> Google Inc.)
    наполни остальное как пожелаешь
    запустите новый AVD и перейдите в «Настройки»> «Аккаунты и синхронизация»> «Добавить аккаунт»> «Google»> любой аккаунт Google.
    повторить тест
  3. Прежде чем я смогу заполнить другие случаи, мне нужно увидеть остальные используемые вами классы Google и созданный вами C2DMReceiver службы


я не могу комментировать из-за низкой репутации пользователя

...