- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 GCM(Google Cloud Messaging)实现 Android 应用程序。我的特定服务器是由 PHP 编写的,我的客户端是 Android 应用程序。当应用程序运行时,推送通知消息可以很好地接收。 logcat 是这样的:
07-28 07:49:56.991: V/GCMBroadcastReceiver(13254): onReceive: com.google.android.c2dm.intent.RECEIVE
07-28 07:49:56.991: V/GCMRegistrar(13254): Setting the name of retry receiver class to com.appacoustic.android.g.gcm.GGCMBroadcastReceiver
07-28 07:49:57.011: D/GGCMBroadcastReceiver(13254): Servicio: com.appacoustic.android.g.gcm.GGCMIntentService
07-28 07:49:57.011: V/GCMBroadcastReceiver(13254): GCM IntentService class: com.appacoustic.android.g.gcm.GGCMIntentService
07-28 07:49:57.021: V/GCMBaseIntentService(13254): Intent service name: GCMIntentService-<<<MyProjectNumber>>>-1
07-28 07:49:57.026: D/GGCMIntentService(13254): Mensaje recibido: <<<MySendedMessage>>>
但是当它停止(不是暂停)时,如果我从主机发送消息,就会出现典型的 Toast 崩溃。logcat错误是这样的:
07-28 07:50:06.381: V/GCMBroadcastReceiver(13294): onReceive: com.google.android.c2dm.intent.RECEIVE
07-28 07:50:06.386: V/GCMRegistrar(13294): Setting the name of retry receiver class to com.appacoustic.android.g.gcm.GGCMBroadcastReceiver
07-28 07:50:06.386: D/GGCMBroadcastReceiver(13294): Servicio: com.appacoustic.android.g.gcm.GGCMIntentService
07-28 07:50:06.386: V/GCMBroadcastReceiver(13294): GCM IntentService class: com.appacoustic.android.g.gcm.GGCMIntentService
07-28 07:50:06.396: D/AndroidRuntime(13294): Shutting down VM
07-28 07:50:06.396: W/dalvikvm(13294): threadid=1: thread exiting with uncaught exception (group=0x4163bc50)
07-28 07:50:06.401: E/AndroidRuntime(13294): FATAL EXCEPTION: main
07-28 07:50:06.401: E/AndroidRuntime(13294): Process: com.planetdevices.android.loyalty, PID: 13294
07-28 07:50:06.401: E/AndroidRuntime(13294): java.lang.RuntimeException: Unable to instantiate service com.appacoustic.android.g.gcm.GGCMIntentService: java.lang.NullPointerException
我猜问题是关于唤醒服务,但实际上我测试了太多东西,我真的很困惑和疲惫......
我在库项目中制作了一个包来实现 GCM。代码是这样的:
GGCMIntentService.java
package com.appacoustic.android.g.gcm;
...
/**
* @author AppAcoustiC <p>
* Servicios GCM. Básicamente son todo callbacks.
*/
public class GGCMIntentService extends GCMBaseIntentService {
private static final String tag = "GGCMIntentService";
/**
* Constructor.
*/
public GGCMIntentService() {
super(SENDER_ID); // Llamamos a la superclase
} // GGCMIntentService()
/**
* Es lo que hace al registrarse el dispositivo.
*/
@Override
protected void onRegistered(Context context, String registrationId) {
Log.i(tag, "Dispositivo registrado: regId = " + registrationId);
Log.d("name: ", GGCM.name);
Log.d("email: ", GGCM.email);
displayMessage(context, getString(R.string.gcm_registered)); // Mostramos un mensaje informativo por pantalla
GServerUtilities.register(context, registrationId, GGCM.name, GGCM.email); // Nos registramos en nuestro Servidor dedicado (Planet Devices)
} // onRegistered()
/**
* Es lo que se hace al eliminar el registro.
*/
@Override
protected void onUnregistered(Context context, String registrationId) {
Log.i(tag, "Eliminado el registro del dispositivo.");
displayMessage(context, getString(R.string.gcm_unregistered)); // Mostramos un mensaje informativo por pantalla
if (GCMRegistrar.isRegisteredOnServer(context)) { // Si ya está registrado en nuestro Servidor
GServerUtilities.unregister(context, registrationId); // Se desregistra de éste
} else {
// Este callback se hace al tratar de borrar el registro desde la clase ServerUtilities cuando el registro en el Servidor falla
Log.i(tag, "Se ha ignorado el callback de anulación de registro.");
}
} // onUnregistered()
/**
* Notificación recibida.
*/
@Override
protected void onMessage(Context context, Intent intent) {
String message = intent.getStringExtra(GCommonUtilities.EXTRA_MESSAGE); // Recojo el mensaje
Log.d(tag, "Mensaje recibido: " + message);
displayMessage(context, message); // Lo mostramos por pantalla
generateNotification(context, message); // Se lo notificamos al usuario (mensaje emergente)
} // onMessage()
/**
* Cuando se borran los mensajes.
*/
@Override
protected void onDeletedMessages(Context context, int total) {
Log.i(tag, "Recibida la notificación de mensajes eliminados.");
String message = getString(R.string.gcm_deleted, total); // Recojo el string y el nº de mensajes borrados SERÁ SIEMPRE EL MISMO STRING AL COGERLO DE LOS R.STRING ???
displayMessage(context, message); // Lo mostramos por pantalla
generateNotification(context, message); // Se lo notificamos al usuario (mensaje emergente)
} // onDeletedMessages()
/**
* Al producirse un error.
*/
@Override
public void onError(Context context, String errorId) {
Log.i(tag, "Error recibido: " + errorId);
displayMessage(context, getString(R.string.gcm_error, errorId)); // Lo mostramos por pantalla
} // onError()
/**
* Al producirse un error recuperable.
*/
@Override
protected boolean onRecoverableError(Context context, String errorId) {
Log.i(tag, "Recibido error recuperable: " + errorId);
displayMessage(context, getString(R.string.gcm_recoverable_error, errorId)); // Lo mostramos por pantalla
return super.onRecoverableError(context, errorId); // Devolvemos un booleano para saber como ha ido la cosa
} // onRecoverableError()
/**
* Genera una notificación para que el usuario sea informado de que ha recibido un mensaje.
*/
private static void generateNotification(Context context, String message) {
int icon = R.drawable.ic_launcher; // Icono que aparece en la barra de notificaciones (será el de la app desde donde creemos el objeto GGCM)
long when = System.currentTimeMillis(); // Instante en el que se produce la notificación
// Creamos la notificación:
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
String title = context.getString(R.string.app_name); // Título de la notificación (Nombre de la app)
// Gestionamos el Intent de la notificación:
Intent notificationIntent = new Intent(context, GGCM.NotificationActivity); // Lo configuramos para que empiece una nueva Actividad (NotificationActivity)
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND; // Sonido de notificación por defecto
// ¡¡¡ POR SI QUEREMOS USAR NUESTRO PROPIO SONIDO PERSONALIZADO !!!
//notification.sound = Uri.parse("android.resource://" + context.getPackageName() + "your_sound_file_name.mp3");
notification.defaults |= Notification.DEFAULT_VIBRATE; // Vibra (si la vibración está activa, claro)
notificationManager.notify(0, notification); // Notificamos (con todos los parámetros incluidos)
} // generateNotification()
} // GGCMIntentService
GGCM.java
package com.appacoustic.android.g.gcm;
...
/**
* @author AppAcoustiC <p>
* Engloba todo lo que conlleva el sistema Google Cloud Messaging.
*/
public class GGCM {
private final String tag = "GGCM";
private Context context;
// Variables que meteremos en la base de datos de nuestro Servidor particular:
static String name;
static String email;
static Class<?> NotificationActivity;
AsyncTask<Void, Void, Void> mRegisterTask; // Tarea de registro
AsyncTask<Void, Void, Void> mUnregisterTask; // Tarea de eliminación del registro
/**
* Constructor.
* @param context Contexto de la Actividad.
* @param SERVER_URL Base URL del Servidor (directorio raíz).
* @param SENDER_ID ID del proyecto en cuestión registrado para usar GCM.
* @param userName Nombre del usuario de la aplicación.
* @param cls Actividad que se inicia al pulsar las notificaciones recibidas.
*/
public GGCM(final Context context,
String SERVER_URL,
String SENDER_ID,
String userName,
Class<?> cls) {
// Primero de nada, comprobamos que está todo en orden para que el GCM funcione.
// Qué el dispositivo soporta GCM y que el Manifest está bien configurado. Esto, una vez testeado se puede comentar:
//checker(context);
// Comprobacion rutinaria de que tenemos inicializadas la variables que indican el Servidor y el ID del proyecto GCM:
G_A.checkNotNull(SERVER_URL, "SERVER_URL");
G_A.checkNotNull(SENDER_ID, "SENDER_ID");
// Asignamos los parámetros a las variables que usamos dentro del paquete:
this.context = context;
GCommonUtilities.SERVER_URL = SERVER_URL;
GCommonUtilities.SENDER_ID = SENDER_ID;
name = userName;
email = G_A.getEmailAdress(context);
NotificationActivity = cls;
// Comprobamos la conexión a internet:
GConnectionDetector gCD = new GConnectionDetector(context);
if (!gCD.isConnectingToInternet()) { // Si no hay internet
// Creamos nuestro Diálogo de alerta particular
GAlertDialog.showAlertDialog(context,
"Error de conexión a Internet",
"Por favor, conéctese para poder hacer uso de la aplicación.",
false);
return; // Paramos la ejecución del programa
}
context.registerReceiver(mHandleMessageReceiver, new IntentFilter(GCommonUtilities.DISPLAY_MESSAGE_ACTION)); // Registramos el receptor
} // GGCM()
/**
* Registra el dispositivo en el que está instalada la app.
*/
public void registerDevice() {
final String regId = GCMRegistrar.getRegistrationId(context); // Obtenemos el ID de registro
if (regId.equals("")) { // Si es "" es que aún no estamos registrados
GCMRegistrar.register(context, GCommonUtilities.SENDER_ID); // Nos registramos
Log.i(tag, "Nos acabamos de registrar en el GCM.");
} else {
// El dispositivo ya está registrado. Comprobamos el Servidor:
if (GCMRegistrar.isRegisteredOnServer(context)) {
// Nos saltamos el registro en el Servidor. Simplemente mostramos un mensaje por pantalla:
G_A.showToast(context, context.getString(R.string.already_registered), Toast.LENGTH_SHORT);
Log.i(tag, "Ya está registrado en nuestro Servidor.");
} else {
// Tratamos de registrarnos de nuevo. Hay que hacerlo mediante una Tarea Asíncrona:
mRegisterTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
boolean registered = GServerUtilities.register(context, regId, name, email);
// Si llegados a este punto, todos los intentos de registrar el dispositivo a nuestro Servidor (Planet Devices) fallan,
// necesitaremos desregistrar el dispositivo desde GCM. La aplicación tratará de registrarse de nuevo cuando se reinicie.
// Hay que tener en cuenta que GCM enviará una devolución de llamada (callback) de borrado de registro al finalizar,
// pero GGCMIntentService.onUnregistered() lo ignorará:
if (!registered) {
GCMRegistrar.unregister(context); // Eliminamos el registro
}
return null;
} // doInBackground()
@Override
protected void onPostExecute(Void result) {
mRegisterTask = null; // Liberamos memoria
} // onPostExecute()
};
mRegisterTask.execute(null, null, null); // Ejecutamos la tarea
}
}
} // registerDevice()
/**
* Elimina el registro del dispositivo que tiene instalada la aplicación.
*/
public void unregisterDevice() {
final String regId = GCMRegistrar.getRegistrationId(context); // Obtenemos el ID de registro
//GServerUtilities.unregister(context, regId);
if (regId.equals("")) { // Si es "" es que aún no estamos registrados
Log.i(tag, "Nos estamos aún registrados. No se puede anular el registro.");
G_A.showToast(context, context.getString(R.string.already_unregistered), Toast.LENGTH_SHORT); // ESTO NO FUNCIONA COMO BEBERÍA !!!
} else {
// Tratamos de eliminar el registro. Hay que hacerlo mediante una Tarea Asíncrona:
mUnregisterTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
GServerUtilities.unregister(context, regId); // Nos "desregistramos"
return null;
} // doInBackground()
@Override
protected void onPostExecute(Void result) {
mUnregisterTask = null; // Liberamos memoria
} // onPostExecute()
};
mUnregisterTask.execute(null, null, null); // Ejecutamos la tarea
}
} // unregisterDevice()
/**
* Comprobamos si el dispositivo soporta GCM y que el Manifest está bien configurado.
*/
private void checker(Context context) {
try {
GCMRegistrar.checkDevice(context);
Log.i(tag, "El dispositivo soporta GCM.");
GCMRegistrar.checkManifest(context);
Log.i(tag, "El Manifest está correctamente.");
} catch (UnsupportedOperationException e) {
Log.e(tag, "El dispositivo no soporta GCM.", e);
} catch (IllegalStateException e) {
Log.e(tag, "El Manifest no está bien configurado.", e);
}
} // checker()
/**
* Manejador para recibir mensajes.
*/
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(GCommonUtilities.EXTRA_MESSAGE); // Recogemos el mensaje
GWakeLocker.acquire(context); // Mantenemos el dispositivo a la espera de que acabe de procesar todo el mensaje (para que no entre en modo de suspensión)
// SI TENEMOS ESTO PUESTO MUESTRA TOAST CADA VEZ QUE ENVIAMOS UNA NOTIFICACIÓN PUSH !!!!!:
//G_A.showToast(context, newMessage, Toast.LENGTH_SHORT); // Lo mostramos por pantalla
GWakeLocker.release(); // Liberamos para ahorrar energía (importante para optimizar los recursos de la batería)
} // onReceive()
};
/**
* Realiza las acciones pertinentes cuando se hace un callback de onDestroy desde la Actividad que llama al objeto GGCM.
*/
public void setOnDestroy() {
if (mRegisterTask != null) {
mRegisterTask.cancel(true); // Cancelamos la Tarea de registro
}
context.unregisterReceiver(mHandleMessageReceiver); // Desregistramos el manejador de mensajes ?????????????????? ESTO CREO QUE HAY QUE QUITARLO, SINO NO LLEGARÁN LOS MENSAJES EMERGENTES
GCMRegistrar.onDestroy(context); // Destruimos
} // setOnDestroy()
} // GGCM
GCommonUtilities.java
package com.appacoustic.android.g.gcm;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
* @author AppAcoustiC <p>
* Utilidades comunes a todo el paquete. Las cargamos en el contructor de GGCM al hacer la llamada.
*/
public final class GCommonUtilities {
static String SERVER_URL; // Base URL del Servidor
static String SENDER_ID; // ID del proyecto en cuestión registrado para usar GCM
// Intentos usados para mostrar un mensaje en la pantalla:
static final String DISPLAY_MESSAGE_ACTION = "com.appacoustic.android.g.gcm.DISPLAY_MESSAGE"; // Nombre del paquete
static final String EXTRA_MESSAGE = "message"; // Extra
/**
* Notifica a la interfaz de usuario (UI) que tiene que mostrar un mensaje.<p>
* Este método está definido en el Helper común porque es usado por ambos: la interfaz de usuario y el Servicio.
* @param context Contexto de la actividad.
* @param message Mensaje a mostrar.
*/
static void displayMessage(Context context, String message) {
Intent intent = new Intent(DISPLAY_MESSAGE_ACTION); // Instanciamos el intento
intent.putExtra(EXTRA_MESSAGE, message); // Añadimos el mensaje que hemos metido como parámetro
context.sendBroadcast(intent); // Hace el envío asíncrono
} // displayMessage()
} // GCommonUtilities
GGCMBroadcastReceiver.java
package com.appacoustic.android.g.gcm;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.google.android.gcm.GCMBroadcastReceiver;
/**
* @author AppAcoustiC <p>
* Implementa el receptor de GCM.<p>
* De este modo irá al paquete donde tenemos nosotros nuestro GCMIntentService.
*/
public class GGCMBroadcastReceiver extends GCMBroadcastReceiver {
private final String tag = "GGCMBroadcastReceiver";
/**
* Devuelve el nombre de la clase donde tenemos implementado el servicio.
*/
@Override
protected String getGCMIntentServiceClassName(Context context) {
String s = GGCMIntentService.class.getName();
Log.d(tag, "Servicio: " + s);
return s;
} // getGCMIntentServiceClassName()
} // GGCMBroadcastReceiver
GServerUtilities.java
package com.appacoustic.android.g.gcm;
...
import com.appacoustic.android.g.R;
import com.google.android.gcm.GCMRegistrar;
/**
* @author AppAcoustiC <p>
* Utilidades de comunicación con nuestro Servidor (Planet Devices).
*/
public final class GServerUtilities {
private static final String TAG = "GServerUtilities";
private static final int MAX_ATTEMPTS = 5; // Nº máximo de intentos
private static final int BACKOFF_MILLI_SECONDS = 2000; // Tiempo para echarse atrás
private static final Random random = new Random(); // Objeto auxiliar para generar números aleatorios
/**
* Registra el dispositivo en nuestro Servidor.
* @param context Contexto de la Actividad.
* @param regId ID de registro.
* @param name Nombre del usuario.
* @param email Email del usuario.
* @return Si el registro ha sido correcto.
*/
static boolean register(final Context context, final String regId, String name, String email) {
Log.i(TAG, "Registrando dispositivo... (regId = " + regId + ")");
String serverUrl = SERVER_URL + "/register.php"; // Dirección donde tenemos el *.php que implementa el registro
// Parámetros:
Map<String, String> params = new HashMap<String, String>();
params.put("regId", regId); // ID de registro
params.put("name", name); // Nombre
params.put("email", email); // Email
long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000); // Tiempo para echarse atrás (y un poco más cada vez)
// Una vez GCM devuelve el ID de registro, necesitamos registrar dicho ID en nuestro Servidor particular:
for (int i = 1; i <= MAX_ATTEMPTS; i++) { // Si el Servidor ha caído, lo volveremos a intentar alguna vez más
Log.d(TAG, "Intento de registro nº " + i + ".");
try {
displayMessage(context, context.getString(R.string.server_registering, i, MAX_ATTEMPTS)); // Mostramos por pantalla
post(serverUrl, params); // Hacemos el post con los parámetros hacia nuestro Servidor
GCMRegistrar.setRegisteredOnServer(context, true); // Indicamos que se registra
// Mostramos por pantalla que todo ha ido bien:
String message = context.getString(R.string.server_registered);
GCommonUtilities.displayMessage(context, message);
return true;
} catch (IOException e) {
// Aquí estamos simplificando el manejo de errores. En una aplicación real
// se debería de volver a procesar sólo los errores recuperables (como HTTP error code 503, e.g.):
Log.e(TAG, "Fallo tratando de registrarse. Nº de intento: " + i, e);
if (i == MAX_ATTEMPTS) {
break; // Si ya hemos gastado todos los intentos, paramos la aplicación
}
try {
Log.d(TAG, "Parando por " + backoff + " ms antes de volver a intentar.");
Thread.sleep(backoff); // Retardo
} catch (InterruptedException e1) {
Log.d(TAG, "Hilo interrumpido: Abortamos los restantes intentos.");
Thread.currentThread().interrupt(); // Actividad terminada antes de que la hayamos completado
return false;
}
backoff *= 2; // Incrementamos exponencialemente el tiempo de espera
}
}
// Si hemos llegado hasta aquí es porque hemos condumido todos los intentos.
// Mostramos que ha habido un error:
String message = context.getString(R.string.server_register_error, MAX_ATTEMPTS);
GCommonUtilities.displayMessage(context, message);
return false;
} // register()
/**
* Anula el registro del par "cuenta-dispositivo" en nuestro Servidor.
* @param context Contexto de la Actividad.
* @param regId ID de registro.
*/
static void unregister(final Context context, final String regId) {
Log.i(TAG, "Anulando el registro del dispositivo... (regId = " + regId + ")");
String serverUrl = SERVER_URL + "/unregister.php"; // Archivo *.php encargado de implementar el proceso
// Parámetros:
Map<String, String> params = new HashMap<String, String>();
params.put("regId", regId); // Sólo hace falta el ID de registro para eliminarlo
try {
post(serverUrl, params); // Hacemos el post con los parámetros hacia nuestro Servidor
GCMRegistrar.setRegisteredOnServer(context, false); // Indicamos que se desregistra
// Mostramos por pantalla que todo ha ido bien:
String message = context.getString(R.string.server_unregistered);
GCommonUtilities.displayMessage(context, message);
} catch (IOException e) {
// En este punto, el dispositivo está desregistrado de GCM, pero permanece registrado en nuestro Servidor de Planet Devices.
// Podríamos tratar de anular el registro de nuevo, pero no es necesario, ya que si el Servidor trata de enviar un mensaje al dispositivo,
// se generará un mensaje de error tipo "No registrado" y se debería de anular el registro del dispositivo.
// Mostramos que ha habido un error anulando el registro:
String message = context.getString(R.string.server_unregister_error, e.getMessage());
GCommonUtilities.displayMessage(context, message);
}
} // unregister()
/**
* Realiza un POST a nuestro Servidor.
* @param endpoint Dirección del POST.
* @param params Parámetros de la solicitud.
* @throws IOException
*/
private static void post(String endpoint, Map<String, String> params) throws IOException {
URL url; // Dirección del POST
try {
url = new URL(endpoint); // La inicializamos
} catch (MalformedURLException e) {
throw new IllegalArgumentException("URL incorrecta: " + endpoint); // La dirección no es correcta
}
// Creamos un StringBuilder para cargar el contenido del POST:
StringBuilder bodyBuilder = new StringBuilder();
Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
// Construimos el cuerpo del POST usando los parámetros:
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
if (iterator.hasNext()) {
bodyBuilder.append('&');
}
}
String body = bodyBuilder.toString(); // Pasamos a un único String todo el StringBuilder
Log.v(TAG, "Enviando '" + body + "' a " + url);
byte[] bytes = body.getBytes(); // Convertimos a bytes para que puedan viajar por la red mdiante un Stream
HttpURLConnection conn = null; // Conexión
try {
// Abrimos la conexión y la configuramos:
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
// Posteamos la solicitud:
OutputStream out = conn.getOutputStream(); // Instanciamos el flujo
out.write(bytes); // Enviamos los datos
out.close(); // Cerramos el Stream
// Manejamos la respuesta:
int status = conn.getResponseCode(); // Obtenemos el código de respuesta desde el Servidor (el 200 indica que todo ha ido bien)
if (status != 200) { // Si no es el código 200, es porque ha habido un error
throw new IOException("Envío fallido con error de código: " + status);
}
} finally {
if (conn != null) {
conn.disconnect(); // Al final desconectamos
}
}
} // post()
} // GServerUtilities
我编写了一个 Android 应用程序,它像这样调用该库:
Call in onCreate()
// Creamos nuestro objeto GCM:
gGCM = new GGCM(context,
Att.SERVER_URL,
Att.SENDER_ID,
Att.userName,
NotificationActivity.class);
//gGCM.registerDevice(); // Nos registramos directamente
//gGCM.unregisterDevice();
我有一个NotificationActivity,它是当您单击推送通知等时引发的事件...
最后我的 list 在这里:
AndroidMainfest.xml
...
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="10" />
<uses-permission android:name="android.permission.INTERNET"/>
<!-- GCM requiere de una cuenta Google -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Se activa cuando se recibe un mensaje -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Crea un permiso específico para este paquete. Sólo esta app puede recibir estos mensajes -->
<permission
android:name="com.planetdevices.android.loyalty.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.planetdevices.android.loyalty.permission.C2D_MESSAGE" />
<!-- Esta aplicación tiene permiso para registrarse y recibir mensajes de datos de Google -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Pemisos del estado de la red para detectar el status de Internet -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Vibración -->
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="Splash"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/Splash" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="MainActivity" android:screenOrientation="portrait"></activity>
<activity android:name="NotificationActivity" android:screenOrientation="portrait"></activity>
...
<!-- Receptor -->
<receiver
android:name="com.appacoustic.android.g.gcm.GGCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.appacoustic.android.g.gcm" />
</intent-filter>
</receiver>
<!-- Servicio -->
<service android:name="com.appacoustic.android.g.gcm.GGCMIntentService" />
</application>
</manifest>
非常感谢您的帮助!
最佳答案
我更改了对 GGCMIntentService 中父类(super class)构造函数的调用。我已直接将 SENDER_ID 作为字符串及其值放入。我这样做是因为当我停止应用程序时,当调用该构造函数时,会抛出 nullPointerException,因为字符串“SENDER_ID”未初始化。
public GGCMIntentService() {
//super(SENDER_ID); // Llamamos a la superclase
super("<<<ProjectNumber>>>"); // NEW WAY
} // GGCMIntentService()
之后,出于类似的原因,我也直接在同一个类中的generateNotification()方法中启动Activity,如下所示:
Class<?> cls = null;
try {
cls = Class.forName("<<<packageName>>>.NotificationActivity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
我已将此实例放在下面几行中:
Intent notificationIntent = new Intent(context, cls);
这不是最好的解决方案,但它在任何时候都可以完美工作。 :-)
关于service - 应用程序停止时 Android GCM 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24995077/
我是 C 语言新手,我编写了这个 C 程序,让用户输入一年中的某一天,作为返回,程序将输出月份以及该月的哪一天。该程序运行良好,但我现在想简化该程序。我知道我需要一个循环,但我不知道如何去做。这是程序
我一直在努力找出我的代码有什么问题。这个想法是创建一个小的画图程序,并有红色、绿色、蓝色和清除按钮。我有我能想到的一切让它工作,但无法弄清楚代码有什么问题。程序打开,然后立即关闭。 import ja
我想安装screen,但是接下来我应该做什么? $ brew search screen imgur-screenshot screen
我有一个在服务器端工作的 UDP 套接字应用程序。为了测试服务器端,我编写了一个简单的 python 客户端程序,它发送消息“hello world how are you”。服务器随后应接收消息,将
我有一个 shell 脚本,它运行一个 Python 程序来预处理一些数据,然后运行一个 R 程序来执行一些长时间运行的任务。我正在学习使用 Docker 并且我一直在运行 FROM r-base:l
在 Linux 中。我有一个 c 程序,它读取一个 2048 字节的文本文件作为输入。我想从 Python 脚本启动 c 程序。我希望 Python 脚本将文本字符串作为参数传递给 c 程序,而不是将
对于一个类,我被要求编写一个 VHDL 程序,该程序接受两个整数输入 A 和 B,并用 A+B 替换 A,用 A-B 替换 B。我编写了以下程序和测试平台。它完成了实现和行为语法检查,但它不会模拟。尽
module Algorithm where import System.Random import Data.Maybe import Data.List type Atom = String ty
我想找到两个以上数字的最小公倍数 求给定N个数的最小公倍数的C++程序 最佳答案 int lcm(int a, int b) { return (a/gcd(a,b))*b; } 对于gcd,请查看
这个程序有错误。谁能解决这个问题? Error is :TempRecord already defines a member called 'this' with the same paramete
当我运行下面的程序时,我在 str1 和 str2 中得到了垃圾值。所以 #include #include #include using namespace std; int main() {
这是我的作业: 一对刚出生的兔子(一公一母)被放在田里。兔子在一个月大时可以交配,因此在第二个月的月底,每对兔子都会生出两对新兔子,然后死去。 注:在第0个月,有0对兔子。第 1 个月,有 1 对兔子
我编写了一个程序,通过对字母使用 switch 命令将十进制字符串转换为十六进制,但是如果我使用 char,该程序无法正常工作!没有 switch 我无法处理 9 以上的数字。我希望你能理解我,因为我
我是 C++ 新手(虽然我有一些 C 语言经验)和 MySQL,我正在尝试制作一个从 MySQL 读取数据库的程序,我一直在关注这个 tutorial但当我尝试“构建”解决方案时出现错误。 (我正在使
仍然是一个初学者,只是尝试使用 swift 中的一些基本函数。 有人能告诉我这段代码有什么问题吗? import UIKit var guessInt: Int var randomNum = arc
我正在用 C++11 编写一个函数,它采用 constant1 + constant2 形式的表达式并将它们折叠起来。 constant1 和 constant2 存储在 std::string 中,
我用 C++ 编写了这段代码,使用运算符重载对 2 个矩阵进行加法和乘法运算。当我执行代码时,它会在第 57 行和第 59 行产生错误,非法结构操作(两行都出现相同的错误)。请解释我的错误。提前致谢:
我是 C++ 的初学者,我想编写一个简单的程序来交换字符串中的两个字符。 例如;我们输入这个字符串:“EXAMPLE”,我们给它交换这两个字符:“E”和“A”,输出应该类似于“AXEMPLA”。 我在
我需要以下代码的帮助: 声明 3 个 double 类型变量,每个代表三角形的三个边中的一个。 提示用户为第一面输入一个值,然后 将用户的输入设置为您创建的代表三角形第一条边的变量。 将最后 2 个步
我是新来的,如果问题不好请见谅 任务:将给定矩阵旋转180度 输入: 1 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 输出: 16 15 14 13 12 11
我是一名优秀的程序员,十分优秀!