8

Добавление связанного вопроса на трекере: https://code.google.com/p/android/issues/detail?id=216581&thanks=216581&ts=1468962325DP5 7.0 - Устраняет ли добавление дополнительных ожидающих намерений?

Так я установил 7.0 релиз DP5 Android на моем Nexus 5X сегодня. Я работаю над приложением, которое рассылает локальные уведомления в определенное время с помощью Android AlarmManager. До этого релиза код отлично работает на устройствах, работающих с KitKat, Lollipop и Marshmallow.

Ниже, как я планирования тревоги:

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); 
       } 

Мои AlarmManagerUtil @onReceive из "SET_NOTIFICATION_INTENT" выглядит следующим образом:

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

Что странно это "reminderToFire" равна нулю здесь только на устройствах Android N, но timeToFire правильный.

Я думаю, что это связано с библиотекой Parceler? Я компилирую с использованием Java 1.8 и ориентируясь на Android API 24.

Я определенно оглядел сеть, чтобы ответить на этот вопрос, но мой случай немного уникален, поскольку код 100% работает со всеми предыдущими версиями Android (все ниже N предпросмотр) ... так что я следую ниже ответы столько, сколько я могу:

How can I correctly pass unique extras to a pending intent?

Кто-нибудь еще есть эта проблема?

+0

Вы посмотрели, возвращает ли 'Parcels.wrap (напоминания)' null '? Вы посмотрели, возвращает ли 'intent.getParcelableExtra (REMINDER_EXTRA)' null ', прежде чем передать это значение в 'Parcels.unwrap()'? Вы пробовали наполнить некоторые другие «Parcelable» в «Intent», чтобы узнать, выживет ли он в поездке (например, «Point»)? – CommonsWare

+0

@CommonsУбедитесь, что 'Intent', переданный в аргумент' PendingIntent' выше, загружается объектом 'mExtras' с' mMap', содержащим два объекта: объект Long LongToFire и Parceled reminder. После распаковки в 'AlarmManager onReceive', Long является действительным, но объект« напоминание »имеет значение null. Я попробую Parcel другой объект, как «Point», и посмотрим, как это происходит. 'Напоминание' правильно настроено с помощью' @ Parcel' – Aceofspadez44

+0

«При распаковке в AlarmManager onReceive длинный действителен, но объект напоминания имеет значение null» - но, является ли 'intent.getParcelableExtra (REMINDER_EXTRA)' 'null'? Это означает, что ценность теряется. Если 'intent.getParcelableExtra (REMINDER_EXTRA)' не 'null', но' Parcels.unwrap (intent.getParcelableExtra (REMINDER_EXTRA)) 'is' null', это говорит о том, что «Parcels» испытывает трудности с восстановлением объектов. – CommonsWare

ответ

8

Для тех, кто в конечном итоге здесь вытягивать волосы над AlarmManager (и не отказались и пошли в JobScheduler пока), Google в производстве API 24 билда не поддерживает прохождение Parcelable объекта в AlarmManager.

Способ, которым я обходился: Если вам нужно отправить список (или отдельный объект) в AlarmManager, сохраните элемент в SharedPreferences в виде строки. (GSS.toJson (объект, тип)). Если объект является интерфейсом, существует ряд решений для интерфейсных адаптеров. Один я обнаружил, плавающие вокруг 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; 
} 
} 

После того, как вы адаптер установлен, вам не нужно будет настроить GS0N каждый раз с TypeAdapter, если вы используете какой-то рамки DI (т.е. Dagger2) как так ...

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

так все, что вам придется сделать, это запустить ....

/** 
* 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(); 
} 

Надеются, что это помогает. Конечно, когда вам нужно получить этот объект из общих префов ....

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

Doing типичный объект List = gson.fromJson (JSON, тип) должен работать.

Cheers.

+1

Вероятно, более быстрый подход состоял в том, чтобы преобразовать 'Parcelable' в' byte [] ', который кто-то указал мне, когда [я писал о блоге этот вопрос] (https://commonsware.com/blog/2016/07/22/be-careful-where-you-use-custom-parcelables.html). См. Http://stackoverflow.com/a/18000094/115145 для примера реализации. – CommonsWare

+0

@CommonsWare, хорошо. Это, безусловно, способ сделать это. Риск по-прежнему существует при работе с Parcelable, хотя мое решение зависит от использования GSON (или другого сериализатора). В любом случае любое решение, безусловно, работает. – Aceofspadez44

+2

Я не думаю, что вам нужно пройти через общие настройки. На самом деле базовые типы все еще работают, вы можете передать строку json через намерение и проанализировать ее с помощью Gson при ее получении. Также я не заметил никаких проблем с прохождением Bitmap ... (который реализует Parcelable) – ilansas

2

Я видел такое поведение, о котором сообщалось ранее, с пользовательскими Parcelable объектами и системными услугами (например, NotificationManager). Кажется, что система пытается использовать PendingIntent, и как часть этого по какой-то причине она пытается отключить Parcelable. Это не удается, потому что в системе нет ваших классов. Я не слышал о том, что кто-то сталкивается с этим некоторое время, но вполне возможно, что в Android N есть регресс, который повторно представил его.

Вы можете перерыть через LogCat, чтобы узнать, есть ли какие-либо сообщения — или, еще лучше, следы стека — из системы (а не вашего приложения), которые, как представляется, относятся к вашему событию будильника.

Если вы можете создать воспроизводимый тестовый пример, напишите об ошибке the Android issue tracker. Если вы думаете об этом, напишите ссылку на него здесь, так как я хотел бы заглянуть в нее.

С точки зрения обходные пути, я могу думать о двух:

  1. Не помещайте Parcelable там. Вместо этого поместите идентификатор, который вы можете использовать для поиска информации по мере необходимости, независимо от того, из кэша в памяти (если ваш процесс все еще существует) или из вашего постоянного хранилища данных.

  2. Переключитесь с Parcelable на то, что я и другие назвали «bundle-able», где вы конвертируете свой объект в Bundle. В принципе, придерживайтесь только OS-определенных классов, без пользовательских классов. Затем система может безопасно деактивировать Bundle (по какой бы то ни было причине).Это, конечно, гораздо больнее, чем просто использование обработчика аннотаций для создания реализации Parcelable.

+1

Вы, безусловно, прав, я обязательно отправлю вопрос на трекер. Это должно быть что-то с источниками N (которые мы еще не видим, так как он официально не выпущен) – Aceofspadez44

+0

@ Aceofspadez44: источники NDP5 еще недоступны, но [NDP4 is] (https://android.googlesource.com/platform/каркасы/основание/+/андроид-н-просмотр-4). – CommonsWare

+0

приятно! Я тоже скомпилировал этот проект против NDP4 и получил те же результаты. Спасибо еще раз за помощь. Мне придется связать его ИЛИ прикрепить объект в общих привилегиях с помощью ключа и обработать его позже. – Aceofspadez44

1

Я нашел, что обертывание Parcelable в Bundle работает.

// 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); 

Однако я не уверен, насколько перспективным является такой подход. Я прокомментировал эту проблему на трекере по ошибке Android: https://code.google.com/p/android/issues/detail?id=209422#c11, но я сомневаюсь, что он получит ответ, так как проблема уже отмечена закрытой.

 Смежные вопросы

  • Нет связанных вопросов^_^