DP5 7.0 – ¿Causa la adición de extras a una intención pendiente?

Cómo añadir el problema vinculado en el rastreador: https://code.google.com/p/android/issues/detail?id=216581&thanks=216581&ts=1468962325

Así que instalé el DP5 Android 7.0 en mi Nexus 5X hoy. He estado trabajando en una aplicación que programa las notificaciones locales en momentos específicos usando la clase AlarmManager de Android. Hasta este lanzamiento, el código ha funcionado muy bien en los dispositivos que ejecutan KitKat, Lollipop y Marshmallow.

A continuación se muestra cómo programo las alarmas:

Intent intent = new Intent(context, AlarmManagerUtil.class); intent.setAction(AlarmManagerUtil.SET_NOTIFICATION_INTENT); intent.putExtra(AlarmManagerUtil.REMINDER_EXTRA, Parcels.wrap(reminders)); intent.putExtra("time", when.getMillis()); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); if (alarmManager != null) { if (Build.VERSION.SDK_INT >= 23) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when.getMillis(), pendingIntent); } else if (Build.VERSION.SDK_INT >= 19) { alarmManager.setExact(AlarmManager.RTC_WAKEUP, when.getMillis(), pendingIntent); } else { alarmManager.set(AlarmManager.RTC_WAKEUP, when.getMillis(), pendingIntent); } 

Mi AlarmManagerUtil @onReceive del "SET_NOTIFICATION_INTENT" tiene este aspecto:

 public void fireNotification(Context context, Intent intent) { List<Reminder> reminderToFire = Parcels.unwrap(intent.getParcelableExtra(REMINDER_EXTRA)); long timeToFire = intent.getLongExtra("time", 0L); //.... } 

Lo que es extraño es que "reminderToFire" es nulo aquí sólo en dispositivos Android N, pero el timeToFire es correcto.

¿Creo que tiene algo que ver con la Biblioteca Parceler? Estoy compilando usando Java 1.8 y apuntando a la API de Android 24.

He mirado definitivamente alrededor de la red para una respuesta a esto, pero mi caso es un poco único ya que el código 100% funciona en todas las versiones anteriores de Android (todo debajo de la vista previa N) … así que estoy siguiendo las siguientes respuestas como Tanto como pueda:

¿Cómo puedo pasar correctamente los extras exclusivos a una intención pendiente?

¿Alguien más tiene este problema?

    Para cualquier persona que termina aquí tirando de su pelo a través de AlarmManager (y no se han dado por vencido y se fue a JobScheduler todavía), Google en la producción de la API 24 de construcción no es compatible con pasar un objeto Parcelable en el AlarmManager.

    La manera que conseguí alrededor de esto: Si usted necesita enviar una lista (o solo objeto) en el AlarmManager, almacene el artículo en SharedPreferences como cadena. (Gson.toJson (objeto, tipo)) Si el objeto es una interfaz, hay un número de soluciones de adaptador de interfaz por ahí. Uno que encontré flotando alrededor de S / O:

     public final class InterfaceAdapter<T> implements JsonSerializer<T>, JsonDeserializer<T> { public JsonElement serialize(T object, Type interfaceType, JsonSerializationContext context) { final JsonObject wrapper = new JsonObject(); wrapper.addProperty("type", object.getClass().getName()); wrapper.add("data", context.serialize(object)); return wrapper; } public T deserialize(JsonElement elem, Type interfaceType, JsonDeserializationContext context) throws JsonParseException { final JsonObject wrapper = (JsonObject) elem; final JsonElement typeName = get(wrapper, "type"); final JsonElement data = get(wrapper, "data"); final Type actualType = typeForName(typeName); return context.deserialize(data, actualType); } private Type typeForName(final JsonElement typeElem) { try { return Class.forName(typeElem.getAsString()); } catch (ClassNotFoundException e) { throw new JsonParseException(e); } } private JsonElement get(final JsonObject wrapper, String memberName) { final JsonElement elem = wrapper.get(memberName); if (elem == null) throw new JsonParseException("no '" + memberName + "' member found in what was expected to be an interface wrapper"); return elem; } } 

    Una vez que haya configurado el adaptador, no tendrá que configurar GS0N cada vez con el TypeAdapter si está utilizando algún tipo de marco DI (es decir, Dagger2) como …

     @Singleton @Provides public Gson providesGson() { return new GsonBuilder() .registerTypeAdapter(YourInterfaceClass.class, new InterfaceAdapter<YourInterfaceClass>()) .create(); 

    Así que todo lo que tendrás que hacer es correr …

     /** * stores yourInterfaceClass in shared prefs */ public void setNextReminder(List<YourInterfaceClass> yourInterfaceClass) { Type type = new TypeToken<List<YourInterfaceClass>>() {}.getType(); sharedPrefs.edit().putString(YOUR_KEY, gson.toJson(yourInterfaceClass, type)).apply(); } 

    Espero que esto ayude. Por supuesto, cuando necesitas sacar este objeto de prefs compartidos ….

     String json = sharedPrefs.getString(YOUR_KEY, "No object found"); 

    Hacer el objeto List típico = gson.fromJson (json, type) debería funcionar.

    Aclamaciones.

    He visto este tipo de comportamiento informado antes, con objetos personalizados Parcelable y servicios del sistema (por ejemplo, NotificationManager ). Lo que parece suceder es que el sistema intenta usar el PendingIntent , y como parte de eso por alguna razón intenta un- Parcel el Parcelable . Esto falla, porque el sistema no tiene sus clases. No he oído hablar de alguien que se está metiendo en esto en un tiempo, pero es totalmente posible que hay una regresión en Android N que reintrodujo.

    Usted puede investigar a través de LogCat para ver si hay mensajes – o, mejor aún, apilar los rastreos – del sistema (no su aplicación) que parecen pertenecer a su evento de alarma.

    Si puede crear un caso de prueba reproducible, archive un problema en el rastreador de incidencias de Android . Si piensas en ello, publica un enlace a él aquí, ya que me gustaría echarle un vistazo.

    En términos de soluciones, puedo pensar en dos:

    1. No ponga el Parcelable allí. En su lugar, coloque un ID que puede utilizar para buscar la información según sea necesario, ya sea desde una caché en memoria (si el proceso sigue estando alrededor) o desde cualquier almacén de datos persistente.

    2. Cambie de Parcelable a lo que yo y otros hemos denominado "bundle-able", donde usted convierte su objeto hacia y desde un Bundle . Básicamente, se adhieren únicamente a las clases definidas por el sistema operativo, sin clases personalizadas. Entonces, el sistema puede Parcel el Bundle manera segura (por la razón que lo haga). Esto, por supuesto, es mucho más doloroso que simplemente usar un procesador de anotación para crear la implementación Parcelable .

    He encontrado que envolver el Parcelable en un paquete funciona.

     // When setting up the PendingIntent for the AlarmManager: Intent intent = new Intent(context, MyService.class); MyParcelable myParcelable = new MyParcelable(); Bundle b = new Bundle(); b.putParcelable(EXTRA_MY_PARCELABLE, myParcelable); intent.putExtra(EXTRA_BUNDLE, b); PendingIntent.getService(0, intent, 0); // From the Service (or Activity, BroadcastReceiver, etc.): Bundle b = intent.getExtra(EXTRA_BUNDLE); MyParcelable myParcelable = b.getParcelableExtra(EXTRA_MY_PARCELABLE); 

    Sin embargo, no estoy seguro de cómo el futuro es este enfoque. He comentado el problema en el rastreador de errores android: https://code.google.com/p/android/issues/detail?id=209422#c11 pero dudo que vaya a recibir una respuesta ya que el problema ya ha Sido marcado cerrado.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.