LoginSignup
16
8

More than 5 years have passed since last update.

Android NではPengindIntent経由のカスタムSerializable/ParcelableのインスタンスをgetExtraで取得できない

Last updated at Posted at 2017-05-17

こんにちはsekitakaです。

Android開発のPendingIntent周りで少しハマったので現象と解決方法を共有します。

現象

明確なソースを見つけられていないのですが、Android 7系で以下のコードのように独自のSerializableクラスのインスタンスをputExtraで渡し,PendingIntentにしてAlarmManagerに登録したところ、getSerializableExtraではnullが返ってきてしまいます。
Android 4,5,6では動作していたので、Android 7特有の問題かなと思います。

PendingIntent登録

Intent intent = new Intent(this, Receiver.class);
SerializablePlayer serializablePlayer = new SerializablePlayer(1, "Seki");
intent.putExtra("serializable_player", serializablePlayer);
intent.putExtra("result_function_id", 1); // 結果表示関数用
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 23, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = ((AlarmManager) getSystemService(Context.ALARM_SERVICE));
// 1秒後にbroadcastする
alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000 * 1, pendingIntent);

結果

serializable_player:null

もっと悪いことにカスタムSerializable/ParcelableのインスタンスをputExtraすると、他のputExtraの値も失われてしまいます。
次の例ではextra_stringでputした値がnullになってしまいました。

PendingIntent登録

Intent intent = new Intent(this, Receiver.class);
SerializablePlayer serializablePlayer = new SerializablePlayer(1, "Seki");
intent.putExtra("serializable_player", serializablePlayer);
intent.putExtra("result_function_id", 2); // 結果表示関数用
intent.putExtra("extra_string", "foo"); // nullになってしまう
intent.putExtra("extra_int", 23); // 値は渡される
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = ((AlarmManager) getSystemService(Context.ALARM_SERVICE));
// 1秒後にbroadcastする
alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000 * 1, pendingIntent);

結果

serializable_player:null
extra_string:null
extra_int:23

原因と対策

原因のはっきりしたところは分かりませんが、PendingIntentとしてAlarmManagerに登録されてBroadcastされる過程で、IntentがAndroid OS側で管理されるようなタイミングがあるのかと。その時にアプリの独自のクラスをOSから参照できずにnullとされてしまうのかなと思います。(他の値は失ってほしくないですが)

対策

Serializable/Parcelableは使用しないで、一度byte[]に変換してputExtraします。
さいわいApache Commonsのライブラリに、Serializablebyte[]の相互変換のメソッドがあります。
それを利用して以下のようにすることで、無事Serializableのオブジェクトを渡すことができました。

PendingIntent登録

Intent intent = new Intent(this, Receiver.class);
SerializablePlayer serializablePlayer = new SerializablePlayer(1, "Seki");
// byte[]化する
byte[] byteSerializablePlayer = SerializationUtils.serialize(serializablePlayer);
intent.putExtra("byte_serializable_player", byteSerializablePlayer); // 値は渡される
intent.putExtra("result_function_id", 3); // 結果表示関数用
intent.putExtra("extra_string", "foo"); // 値は渡される
intent.putExtra("extra_int", 23); // 値は渡される
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 3, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = ((AlarmManager) getSystemService(Context.ALARM_SERVICE));
// 1秒後にbroadcastする
alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000 * 1, pendingIntent);

結果表示コード

deserializeします。

// deserializeする
Log.d(TAG, "byte_serializable_player:" + SerializationUtils.deserialize(intent.getByteArrayExtra("byte_serializable_player")));
Log.d(TAG, "extra_string:" + intent.getStringExtra("extra_string"));
Log.d(TAG, "extra_int:" + intent.getIntExtra("extra_int", Integer.MAX_VALUE));

結果

byte_serializable_player:{1:Seki}
extra_string:foo
extra_int:23

まとめ

Parcelable/Serializableの値がnullになるのはいいとしても、他の値もnullになってしまうのはちょっといただけないですね。
このせいでbyte[]までnullになってしまい、なかなか今回の解決方法を見つけることができませんでした。

検証に使用したコードはGitHubで公開しています。

16
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
8