8

リンクされた課題をトラッカーに追加: https://code.google.com/p/android/issues/detail?id=216581&thanks=216581&ts=1468962325

そこで、今日、DP5 Android 7.0 リリースを 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);
                }

「SET_NOTIFICATION_INTENT」の私の AlarmManagerUtil @onReceive は次のようになります。

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 デバイスでのみここで null ですが、timeToFire が正しいことです。

Parceler Library と何か関係があると思いますか? Java 1.8 を使用してコンパイルし、Android API 24 をターゲットにしています。

私は間違いなくこれに対する答えを求めてネットを見回しましたが、コードはAndroidの以前のすべてのバージョン(Nプレビュー以下のすべて)で100%動作するため、私のケースは少し独特です...だから私は以下の答えに従っています私ができる限り:

一意のエクストラを保留中のインテントに正しく渡すにはどうすればよいですか?

他の誰かがこの問題を抱えていますか?

4

3 に答える 3

8

ここで AlarmManager に頭を悩ませている (まだあきらめて JobScheduler に行っていない) 人のために、プロダクション API 24 ビルドの Google は Parcelable オブジェクトを AlarmManager に渡すことをサポートしていません。

私がこれを回避した方法: リスト (または単一のオブジェクト) を AlarmManager に送信する必要がある場合は、その項目を String として SharedPreferences に格納します。(Gson.toJson(object, type)) オブジェクトがインターフェイスの場合、多くのインターフェイス アダプター ソリューションが存在します。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;
}
}

アダプターをセットアップしたら、ある種の DI フレームワーク (つまり Dagger2) を使用している場合は、TypeAdapter を使用して毎回 GS0N をセットアップする必要はありません...

@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");

典型的な List object = gson.fromJson(json, type) を実行するとうまくいくはずです。

乾杯。

于 2016-08-31T19:49:58.427 に答える
2

Parcelableカスタムオブジェクトとシステム サービス (例: )で、この種の動作が以前に報告されているのを見たことがありますNotificationManager。システムが を使用しようとしているように見えますが、PendingIntentその一部として、何らかの理由で を取り外そうとします。システムにクラスがないため、これは失敗します。しばらくの間、誰かがこれに遭遇したとは聞いていませんが、Android N に回帰があり、それが再導入された可能性は十分にあります。ParcelParcelable

LogCat をくまなく調べて、アラーム イベントに関連していると思われるシステム (アプリではなく) からのメッセージ (さらにはスタック トレース) があるかどうかを確認することもできます。

再現可能なテスト ケースを作成できる場合は、Android Issue Trackerに問題を報告してください。思いついたら、ちょっと覗いてみたいので、ここにリンクを貼ってください。

回避策としては、次の 2 つが考えられます。

  1. そこには入れないParcelableでください。代わりに、必要に応じて情報を検索するために使用できる ID を入力します。これは、メモリ内キャッシュ (プロセスがまだ残っている場合) または永続的なデータ ストアからのものです。

  2. Parcelable私や他の人が「バンドル可能」と呼んでいるものに切り替えて、オブジェクトをBundle. 基本的には、OS 定義のクラスのみに固執し、カスタム クラスは使用しません。その後、システムは安全に de- Parceltheを実行できますBundle(理由は何であれ)。Parcelableもちろん、これは単に注釈プロセッサを使用して実装を作成するよりもはるかに面倒です。

于 2016-07-19T19:43:52.583 に答える