2013年12月17日火曜日

【Android】BundleにでかいStringをあげたら死んだ

Activityをまたいである程度の大きさのリストを共有したい.
そこで,一番手っ取り早そうな方法を使った.
具体的には

====ActivityA
ArrayList<Hoge> bigList;
String json = Json.encode(bigList);
Intent intent = new Intent (this, ActivityB.class);
intent.putExtra ("BIGLIST", json);
startActivityForResult (intent, 666);

つまりActivityAクラスで大きなリストをJSON文字列化して
それをインテントに乗せてActivityBクラスへ渡し,
ActivityBクラス内でJSONからリストに戻していた.
このリストが(1件のデータはさほどの大きさではないが)500件ほどになると
以下のようなエラーを吐いて爆死した.

12-17 17:41:54.349: E/JavaBinder(282): !!! FAILED BINDER TRANSACTION !!!
12-17 17:41:54.349: W/ActivityManager(282): Exception when starting activity jp.mau.dummy/.activity.ActivityB
12-17 17:41:54.349: W/ActivityManager(282): android.os.TransactionTooLargeException
12-17 17:41:54.349: W/ActivityManager(282): at android.os.BinderProxy.transact(Native Method)
12-17 17:41:54.349: W/ActivityManager(282): at android.app.ApplicationThreadProxy.scheduleLaunchActivity(ApplicationThreadNative.java:705)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityStack.realStartActivityLocked(ActivityStack.java:702)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityStack.startSpecificActivityLocked(ActivityStack.java:811)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1759)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1393)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1141)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityStack.activityPaused(ActivityStack.java:1039)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:4309)
12-17 17:41:54.349: W/ActivityManager(282): at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:381)
12-17 17:41:54.349: W/ActivityManager(282): at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:1616)
12-17 17:41:54.349: W/ActivityManager(282): at android.os.Binder.execTransact(Binder.java:367)
12-17 17:41:54.349: W/ActivityManager(282): at dalvik.system.NativeStart.run(Native Method)

この現象自体は前々から把握しており,
そろそろ対策するかと何度も再現検証して初めて出たログである.
リストを大きくすると死ぬから原因自体は容易に想像できるが,
このTransactionTooLargeExceptionという例外クラス名を見れたのは大きい.

対策はまだしていないが,以下のような方法が考えられる.

1.リストが絶対に100件程度で収まる場合
無対策でそのままJson化して受け手側で展開すればOK.
しかし受け手側で展開したリストと送り手が持っていたリストは別物であり
メモリ上に重複しているということを覚えておく必要がある.
巨大なデータを2つも持ちたくはないし,
受け手側でした変更を送り手側に反映するためには
受け手側で再度JSON化して送り手側に返す必要がある.

2.リストがまぁそれなりの大きさで済んでくれる場合
メモリ上にリストを全部展開しても生き残れる程度に確実に収まる場合は
調べたところではSerializableを継承したクラスにリストを配置し,
それを受け渡すことで回避できるらしい.
ただ,やってることは1.とさほど変わらず,
Stringというプリミティブオブジェクトを渡すのか,
Serializeされたストリームを渡すのかの違いで
受け手側で展開して多重化されるのは変わらないらしい.

3.リストが青天井の場合
ArrayListなんてつかわずにおとなしくDB化しよう.
インテントもDBのURL渡すだけでいい.
ArrayAdapterとか使ってたら改造が大変だけど,
やる実は大きいと思う.
DBは使わなくなったら棄てるようにしよう.


…と今日考えたこと.
まだ(改造がめんどくさくて)実装していないがこれからDB化していく.
はぁ…

【Android】にこちゃんねる ニコニコ風2ちゃんブラウザ

小生の拙作,にこちゃんねる というAndroidアプリを公開した
いや,少し前にしていたので紹介させていただきたく.


にこちゃんねる -ニコニコ風2ちゃんねるブラウザ-
2013年12月2日 Ver.1 公開

にこちゃんねる は簡単にいうと
ニコニコ動画のようにレスがスクロール表示される2ちゃんブラウザ
です.


他のアプリの上にスクロール表示できるので,
ゲームをしながらレスを流し読みしたり
ツイッターアプリを表示したままレスの確認をしたりできます.

ワンセグやフルセグ対応機種であれば
テレビを見ながら実況板を表示することで
実況民の感想を即座に見ることができます.

スレは4スレまで一度に巡回できます.
それぞれ背景色で分けているので
レスがどのスレについたものか判別できます.
巡回中スレッドの横にアイコンが表示されますが,それと同じ背景色でスクロールします.

自由に板を登録できるテレビリモコン配置のショートカットもあります.
初期配置ではご覧のとおりの設定になっております.
べっ…別に実況専用ではないんだからね!


さあ,これはべんり!楽しい!今すぐダウンロード!
にこちゃんねる -ニコニコ風2ちゃんねるブラウザ-