擬似乱数というかarc4random()の話。
ダイスの出目が必ず奇数と偶数の繰り返しになるという、地味ながらゲームバランスの根幹を破壊するバグが話題となりました。しかしその話題から得るべき教訓は、
その論調は一様に、サイコロすらまともに作れないなんて馬鹿すぎる、担当プログラマが低脳すぎて笑える、といったような物だった。やがてそのような書き込みの中に、Cコードを示して「サイコロなんかたったこれだけで作れるのに」と発言する者が何人か現れた。そしてこれが最も重要な点だが、そのようにして示されたコードは、なぜか全部カルドセプトサーガのプログラマが犯したのと同じミスを犯していた。
擬似乱数の特性について、正確に把握しているプログラマはあまりにも少ない、という事実だと思います。
とあるiOSの入門書では、arc4random()
の呼び出しにsrand()
が必要かのような記述が放置されていました。
その本はiOSのバージョンが上がる度に追従して改訂している本だったため、いつこの記述が修正されるのか毎年楽しみにしていたのですが、結局サンプルコードがSwiftに書き直されるまでの3~4年、間違った記述のまま直されることはありませんでした…。
乱数の種類と特性
良い乱数・悪い乱数を読めば終わる話なので深入りはしません。
真性乱数
現実世界でサイコロを振った場合の出目は、本当の意味のランダムなので、計算機が作り出す乱数と比較して、真性乱数と呼ばれたりします。
高度なセキュリティを要求されるケースでは、真性乱数を生成するために専用のハードウェアが用いられることがあります。
擬似乱数
計算機が算出するものは「次の値が予測可能である」という点において乱数とは言えないので、擬似乱数と区別して呼ばれます。
Cの標準ライブラリであるrand()
が生成する乱数列は、ほとんどが線形合同法を利用した低い品質のものであることが多いのです。これはC99標準が乱数の強度に関する規定を行っていないこと、また線形合同法があらゆるハードウェアで高速で動作する実装であることなどが理由かと思われます。
計算機による擬似乱数は、再現性のある乱数列を生み出せるという特徴を持ちます。これは一見すると欠点のように思えますが、特定のシードを与えることでゲームの難易度を調整できたり、シードとユーザーの入力を記録するだけでリプレイデータを作れるなど、うまく使えば利点にもなります。
ただし質の悪い線形合同法によるrand()
の実装では、下位1ビットが同じものが出続けるか、0と1の繰り返しになることがあります。ゲームでの乱数として用いる場合、下位ビットを切り捨てて使うのが当たり前でした。
これがカルドセプトサーガが犯した過ちですが、意外にプログラマをやっていてもこうした「お約束」を理解している人は少ないのではないでしょうか。rand()
がエントロピーを凌駕して、真性乱数を生成すると思い込んでいる方が少なくないように思います。
メルセンヌツイスター教に入信しておけば、おおむね問題ないと思います。
暗号論的擬似乱数
乱数の品質は計算量や出目の偏り、周期の長さで評価されることが多いのですが、それとは別に暗号用途で利用できるかどうかという軸があります。
通常の擬似乱数は、例え乱数列として品質が良くとも、暗号用途としては決して使ってはいけません。
両者を区別する、面倒な数学的な定義がありますが、「現実的な時間を掛けても、それが真の乱数列なのかアルゴリズムによって生成された擬似乱数なのか識別できない場合に暗号論的擬似乱数と呼ばれる」…程度のゆるふわな認識でいい気がします。
厳密な定義は人によって様々で怖いです。(メルセンヌツイスタで作った擬似乱数に暗号学的ハッシュ関数を掛けたものは暗号論的擬似乱数と言えるのか?とか)
arc4random()って何?
iOSでの開発では、arc4random()
関数を用いて乱数を生成することが多いと思います。
これはお手軽に暗号論的擬似乱数列を得られます。
この関数はFreeBSD由来と言われることが多いのですが、manを読めば分かるように大本はRC4です。
RC
ロナルド・リン・リベスト氏という暗号分野で著名な研究者がおり、RC4は氏の作成した暗号アルゴリズムです。RCとは「リベスト暗号(Rivest Cipher)」の略だと言われており、RC1、RC3など開発中に解読されて欠番となったものもありますが、RC4やRC5、RC6などシリーズ化しています。
このネーミングでピンときた方がいるかもしれませんが、MD2やMD4、MD5の作者でもあります。
ARCFOUR
RC4は1987年にRSAデータセキュリティ社で開発され、そのアルゴリズムは長らく企業秘密でした。
しかし1990年代末に、ニュースグループにてその実装が解説が流出したことで、インターネットで公知なアルゴリズムとなりました。
こうして、RC4のオープン実装である、ARCFOUR(Alleged-RC4、「RC4以外の何か」)が普及しました。arc4random()
もその実装の一つでした。
RC4の終焉とarc4random
現在ではRC4への攻撃方法が発見され、もはやセキュアではないといわれており(HTTPS通信におけるRC4の使用が禁止されるなど)、NetBSDなどのarc4random()
の実装も、ChaCha20によるものに置き換えられています。
OSXやiOSのarc4random()
は、まだRC4モドキらしいですが…。
そんなわけで、玄人はきっと/dev/random
から暗号論的乱数列を取得できるSecRandomCopyBytes()
を使っているんだと思います。これがまた超使いにくいんですよね…。
追記
上の内容は、OSX 10.10のターミナルでman arc4random
した内容(/dev/urandom
でS-Boxを埋めてから、ARC4ストリーム暗号を使って乱数生成している)を元に書いていますが、Appleが公開しているLibc-997.90.3
のソースコードではS-Boxの生成に/dev/random
を利用しているみたいです。
その後はARC4を使っていますが、脆弱性対策として最初の1024byteを捨てたあと、1600000byte毎に/dev/random
から再度シードを与えてるっぽい?
iOS/OSXのカーネルにおいて/dev/random
の実装がどうなっているのかも興味があるところです(FreeBSDだと/dev/random
は/dev/urandom
のシンボリックリンクだったりするので。英語版のWikipediaの/dev/random
の記事ではiOS/OSXはYarrowアルゴリズムを使っているとあるけれど、この記述は古いような気がします)
iOS9 SDKでDeprecatedになるもの。
UPDATE(2015/09/26):この文書はDeprecatedという単語の使い方が雑なのでDeprecatedになりました。
こちらの記事を参照した方が得るものがあるかと思います。
新しいAPIの誕生より、APIの滅びの方が好きです。なぜならそこには失敗があり、学びがあるからです。
…とか適当に言ってみましたが、iOS9のAPI Diffは、Swift2.0絡みの変更点(主にNSError
に対応するenumの追加と、後述するビットマスクの扱いの変更)だらけで、本当に変わった場所がどこなのか分かりにくいので、ちゃんと調べてないです。
AudioUnitがOSXに追いついたCoreAudio周りが熱いような…。CFunctionPointer
の魔窟だったCoreMIDIなんかは刷新と言っていいレベルで変わっていますね。でも今のAppStoreでオーディオ系のアプリに対価を出す人がいるのかっていうと、微妙なんですよねぇ。
ところで、APIDiffはFrameworksとModulesに分かれているのですが(両方読む必要があります)、Frameworks側の記述はヘッダーファイルレベルの記載なのでObjective-Cになっていて、Modules側はSwiftで書かれていて、読み辛いことこのうえないです。
なぜこんなことになっているのかというと、フレームワークのヘッダは、SwiftとObj-C向けで共用するために、Obj-Cの形式になっているからですかねー。(Swift用のヘッダは、XCodeが動的に変換を掛けている)
RawOptionSetType
NS_ENUM
マクロで定義されたenumはSwiftでも素直にenumになるけれど、NS_OPTIONS
マクロで定義されている場合にはRawOptionSetType
構造体に変換される、という特殊ルールについてご存知でしょうか。
Swiftのswitch-caseのパターンマッチングの強力さと、enumを組み合わせた安全性は言語仕様の魅力のひとつなのですが、素朴なビットマスクには弱いのです。
しかしご安心下さい。Swift2.0ではOptionSetType
構造体がその座に取って代わります。これはSetAlgebraType
プロトコル(SetAlgebraは集合代数の意味)に準拠しており、ビットマスクを扱いやすくするメソッドが追加されています。また独自のビットマスクを定義することも簡単になるみたいです。
独自のRawOptionSetType
や、RawOptionSetType
のextensionを作っている人は今のうちにこっそり削除しましょう。
AddressBook/AddressBookUI
滅亡します。
これはSwiftから使うのが辛そうな(そもそも可能だったんでしょうか?)APIだったので、順当に死にましたねーって感想。
Contactフレームワークがaddedになっているけれど、実際にはAddressBook内部で使われていたCNContact
が機能拡張されて表に出てきて、AddressBookUIに対応するContactsUIが追加される形になっているような気がします。
新しいAPIはモダンで綺麗で素敵なんだろうなー!!と思ってみてみたら、NSPredicate
を使うのかなこれ…。
stringByAddingPercentEscapesUsingEncoding
誰もが一度はこのメソッドに騙され、URLエンコードに失敗したのではないでしょうか。
:
や/
をエンコードしてくれない存在意義がいまいち良く分からないメソッドでしたが、ようやくiOS9で廃止されます。
今後は、iOS7以降のみ使える、stringByAddingPercentEncodingWithAllowedCharacters
が推奨されます。これも正しく使わないと変換できませんが。
iOS6以前からやっていた人や、互換性を意識していた人はCoreFoundationのCFURLCreateStringByAddingPercentEscapes()
関数を利用していたかと思いますが、これもiOS9で廃止されますので注意が必要です。
結構使われてそうな関数なのでちょっと意外です。ARCの概念を知らない人が使うとメモリリークするから?
Java版Foundationのこと、覚えてる?
素朴な疑問があったので投稿してみました。これはその補足みたいなもの。
CoreData
CoreDataって凄く独特な世界観を持っていて扱いづらいと思った人が多いのではないでしょうか。
なんでこんなのがXCodeやInstrumentsのテンプレートに居座っているのか理解できない、関連張ると動作が重くなりすぎて使い物にならない、CoreData内部の良く分からないエラーに悩まされている、普通にSQL書きたいからFMDB使ってます、幻滅しましたRealmに移行します、そんな雰囲気だと思います。
CoreDataの原型は、NeXTのFoundationが用いられた最初の製品であるEnterprise Objects Frameworkです。
20年前にデータベース製品の学習コストの高さと、オブジェクト指向言語とのインピーダンスミスマッチの問題に取り組んだこの製品は、普及はしませんでしたが思想的な面などでは高い評価を得たそうです。AndroidのORMは栄枯盛衰でどれを使えばいいのか悩むのですが、iOSにはEOFの血脈を受け継いだCoreDataがいるのです。感動的ですね。
WebObjects
まぁCoreDataは本気でどうでもよくて、その原点たるEOFを発展させた世界初のWebアプリケーションフレームワーク、WebObjectsというNeXTの主力製品がありました。
これは「WWW経由でRDBMSからオブジェクトをフェッチし、HTMLにレンダリングする」という雰囲気の、こちらも概念としては面白いフレームワークで、主にエンタープライズ向けの製品だったので一般にはほとんど普及しなかったにも関わらず、コアなファンがいるとかいないとか。いないかも。
Apple内製では未だに現役みたいで、例えば今でもAppStoreはWebObjectsで動いています。
Java Foundation
WebObjectsもNeXT由来なのでObjective-Cで書かれていたのですが、Javaに完全移行した時代がありました。
全てをFoundationに依存していたWebObjectsが、Javaへ移行するために取った手段が、Foundationのクラス群をそのまま同名でJavaに再実装するという暴挙でした。
これは現存するJavaDocを読むと面白いです。中核技術だったKeyValueCodingの扱いとか、セレクタはReflectionだったりとか、カテゴリに相当する機能は実現できなかったりとか、ArrayList
ではなくNSArray
使わなければならないとか。(KVOプロトコルのリファレンスに、「キー値監視はJava Applicationでは使えない」って注意書きがあるのもこの辺の名残だと思います、これいつ消えるんだろう…)
com.webobjects.foundation (WebObjects 5.4.2 Reference)
この前例を見る限り、「Foundationが徐々にSwiftのように洗練されていく」という可能性は低いかなーと思っています。
他言語に移植・移行するには、Foundationの持つ機能は、Objective-Cの言語仕様そのものすぎるのです。また、依存している多くのフレームワークが影響を受け互換性を失うデメリットと釣り合わないと思いますし、Swiftが型推論による安全な静的型付け言語であることを選択し、Smalltalk由来の動的型付けの機能を喪失したことも、移行を阻害する大きな要因になると思っています。
とはいえ
今からObjective-Cを書くのは半分くらいは技術的負債だと思っています。
KVCのせいで実質的にprivateなインスタンス変数は作れない、高速列挙プロトコルとか記憶から抹消した方がマシ、blocksにGCD、weakにnullableにGenericsと詰め込みすぎてもう意味が分からない言語になってます。
一方で、Core系のAPIを使うにはまだまだC互換言語の知識が捨てられません。
Swiftは当初、「Objective-C without the C」と大々的に宣伝されました。自分はこのフレーズにとても期待しましたが、Swiftでも関数ポインタが絡むAPIの辛さは軽減されないどころかむしろ酷くなっていて、特にクロージャを関数ポインタに逆変換する術がなくて詰むことが多いです。CFuntionPointer
やUnsafeMutablePointer
で頑張るくらいなら普通にObjective-Cで書いた方がはるかにマシという感じです…。
とはいえSwift移行の大号令は近いうちにかかるでしょう。ただしそのとき殺す相手は、LLVM中間コードによるネイティブ並の動作を謳う、RubyMotionやXamarinのようなプロダクトなんじゃないかなぁ、その方がAppleらしさを感じます。
Nexus5にAndorid M Preview入れた。
あるいはAndroidのデータ領域についての話。もしくはLollipopのバッテリーイーターに対する愚痴。
「Previewを入れるには全てのデータが消える覚悟が必要」みたいな話をよく聞きますが、Androidのデータ構造を理解せずに、batやシェルスクリプトを使ってアップデートをしている人の勘違いです。
前提条件はありますが、ユーザーデータ領域を残したままM Previewを入れることはできます。
Androidのデータ領域について
fastboot
コマンドはおまじないではありません。データ領域の意味が分かれば、何をしているのか理解できます。
データ領域名 | 説明 |
---|---|
bootloader | fastbootモードで起動するのがこのブートローダ…みたいな認識で合ってるんでしょうか。通常oemロックにより読み取り専用領域となっていますが、Nexus端末はアンロックできます。ブートローダーのアンロック時には端末データが全部消し飛ぶので、一度アンロックしたら放置してます。 |
radio | 通信領域に関わるファームウェア。「ベースバンド」という言葉が良く使われます。 |
boot | boot領域です。Linuxカーネルと初期RAMディスクに相当します。bootとbootloaderはよく混同されてる気がする。 |
recovery | リカバリ起動を行うための領域です。OSが起動不可能になった場合に、修復を行えるようにするため、OSとは別系統のシステムが存在するのです。必要最小限の構成のため、中身はほぼbootと同じらしいです。root化とカスタムリカバリのインストールもよく混同されてる気がする。 |
system | Android OSのうちカーネルを除く部分(要はユーザーランド)です。システムはユーザー権限で動くので、root化するにはカスタムリカバリを用いてsu をインストールする方法が採られます。 |
userdata | ユーザーデータ領域です。この領域を消さなければ、OSのみをアップデートすることができます。初回のOS起動時に既存のユーザーデータの最適化を行ってくれますが、挙動がおかしくなる場合もあるようです。その場合は諦めましょう。 |
cache | 一時的なキャッシュとして利用される領域です。 |
flash-allがやっていること
fastboot
にはいろいろコマンドがありますが、書き込みを行うflash
と、削除を行うerase
だけ覚えとけば問題ない気がします?
flash-all.sh
の中身を見ると以下のような感じです。
fastboot flash bootloader bootloader-hammerhead-HHZ12h.img
fastboot reboot-bootloader
sleep 5
fastboot flash radio radio-hammerhead-M8974A-2.0.50.2.26.img
fastboot reboot-bootloader
sleep 5
fastboot -w update image-hammerhead-MPZ44Q.zip
まずブートローダーを書き換えて、続いてベースバンドバージョンを上げます。
fastboot -w update
というコマンドが出てきます。-w
オプションは、userdata
とcache
をerase
することを意味します。その上でupdate
コマンドで、zipファイルの中身をflash
します。
image-hammerhead-MPZ44Q.zip
の中身を見ると、
boot.img
recovery.img
system.img
userdata.img
cache.img
があります。それぞれが上表のデータ領域に相当しています。
ユーザーデータ領域を残したままAndroid Mに入れ替える
前提として、ブートローダーをアンロックしたまま利用している必要があります。ブートローダーのアンロックに伴う端末初期化は回避する方法がなさそうです。
fastboot flash bootloader bootloader-hammerhead-HHZ12h.img
fastboot reboot-bootloader
fastboot flash radio radio-hammerhead-M8974A-2.0.50.2.26.img
fastboot reboot-bootloader
ここまでは公式の方法と同じです。あとはimage-hammerhead-MPZ44Q.zip
を解凍して、
fastboot flash boot boot.img
fastboot flash recovery recovery.img
fastboot flash system sytem.img
だけやれば、データを移行してOSだけアップデートできました。
cache
領域のerase
は、やった方がよさそうですが、今のところやらなくても支障はありません。root権限があるなら、fastboot
コマンドを使わなくても、単に/cache
の中身を消せばいいです。
Lollipopのバッテリー問題
「Android Mはバッテリーの持ちがよくなる」みたいな話があります。アップデートした理由はほぼこれに尽きます。
条件が謎なのですが、Lollipopは一旦バッテリーイーターなモードに入ると、何もしなくても大体10~12時間くらいで電池が尽きるようになるのです。
頻繁に通信しそうなアプリは、Gmailを除いて消したのですがそれでもこれです。「その他」が電源をガリガリ削っています。同じ条件のiPhone 5Sは90%くらい残ってます。
当初、バッテリーがへたれたのかと思って新品に換えたけど意味がありませんでした。Nexus 5の電池交換はY字精密ドライバと、両面テープで張り付いてるバッテリーを剥がす根気が必要で、無駄に面倒です。
Android Mにして様子見てる感じでは上の通り真っ当な電池の減りです(Wifiのアイコンが塗りつぶされてるのはバグっぽい…iOS7で良く見たやつだ…)。バッテリー効率が改善されたというよりも、L Previewから放置されてきたバグをやっと直した、って感じがします。
AppCompatDelegateの話。
Toolbar
は正直ないわーって感じなんですけど、皆様は適合されたんでしょうか。
DroidKaigiなるイベントがあったらしく、Toolbar
が受け入れられるのかディスられるのかが気になっていたのですけど、「BaseActivity
の是非」が一番のトピックだったっぽい?個人的には、Intent
などのAndroidのルールを隠蔽するために、
nextScreen(ActivityFactory.createFromId(GAMEN_ID_HOGE));
みたいなオレオレ画面遷移メソッドを実装しているBaseActivity
は爆破したくなりますが、それ以外で不満を持ったことはない感じです。
さて、AppCompatDelegate
の話です。ActionBarActivity
は死んでAppCompatDelegate
の時代が来ました。それをざっくり説明すると以下の通りです。
今までのサポートライブラリ
Fragment
やActionBar
の概念が導入されたのはAndroid3.0。このうちFragment
はAPIレベル4まで、ActionBar
はAPIレベル7までバックポートされています。
Fragment
のバックポートの責務はFragmentActivity
に、ActionBar
のバックポートの責務はActionBarActivity
にあったため、上のような継承構造となっていました。
どう変わったか
AppComatDelegate
にActionBar
のバックポートや、マテリアルデザインのための諸々の責務を全て委譲するように変わりました。
実際のところ、少し前からActionBarActivityDelegate
という名前で、ほぼ現在と近い実装になっていました。22.1というマイナーアップデートでお披露目となっているのもそのためだと思っています。
AppCompatActivity
は単なるガワです。継承しなくても互換性のあるアプリを作ることができます。
これまでの継承構造では、PreferencesActivity
が死にクラスになっていたとしばしば批判されていました。AppCompatPreferenceActivity
というサンプルを見るのが分かりやすいのですが、この変更によって、どのような継承構造のActivityであっても、AppComatDelegate
に処理を委譲することで互換性を維持できるのです!!
というのは嘘です。
確かに公式ブログにも、
However, this wasn’t just a rename. In fact, the internal logic of AppCompat is now available via AppCompatDelegate - a class you can include in any Activity, hook up the appropriate lifecycle methods, and get the same consistent theming, color tinting, and more without requiring you to use AppCompatActivity (although that remains the easiest way to get started).
私訳:しかしながら、この変更は単なる改名ではありません。AppCompatの内部ロジックは全てAppCompatDelegateにより今すぐ利用可能です。このクラスはAppCompatActivityを必要とせず、あらゆるActivityに取り込むことができ、ライフサイクルメソッドと適切に連携することで、一貫性のあるテーマ、色の変異(color tinting)などを実現できます。(それを開始する最も簡単な方法として、AppCompatActivityも残されています)
というようなことが書いてあります。
しかし、AppCompatActivity
がFragmentActivity
を継承していることを忘れないで下さい。
FragmentActivity
が扱っているのはv4のFragment
(偽)です。普通のActivity
が扱うFragment
(真) とは別物です。
AppComatDelegate
が解決する問題はマテリアルデザインとActionBar
(と便宜的に呼ばれ続けているToolbar
)の領域だけなのです。そのため、独自のActivity
に組み込んでもいいケースは、Fragment
を使う機会が全くないか、もしくはandroid.support.v4.app.Fragment
とandroid.app.Fragment
が混在しても支障がない場合に限られるということです。同様のことはLoader
にも言えます。
AppCompatActivity
は必ず継承した方が無難だと思います。
また、AppCompatDelegate
が扱うToolbar
はv7のToolbar
(偽)であり、普通のActivity
が扱うToolbar
(真)とは別物なのも微妙なトラップなのでご注意下さい。
Androidのタスクとプロセスの話。
あるいは、「Androidのstatic変数は勝手にクリアされる」という迷信についての話。
タスク
Tasks and Back Stack | Android Developers
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack), in the order in which each activity is opened.
Androidでは、ユーザーが特定の作業を行うときに、相互作用を持つActivity
の集まりをタスクと定義しています。これはバックスタックと呼ばれる、Activity
を起動した順序で保持したスタック構造で管理されます。
タスクはいつ始まるのか
ホーム画面もまたActivity
です。
- タスクとはいつ始まるのか
- タスクを管理しているのは何なのか
と疑問に思うはずです。
You can set up an activity as the entry point for a task by giving it an intent filter with "android.intent.action.MAIN" as the specified action and "android.intent.category.LAUNCHER" as the specified category.
タスクのエントリポイントとなるのは、お馴染みandroid.intent.action.MAIN
とandroid.intent.category.LAUNCHE
のIntent
です。ランチャーから起動されたActivity
はタスクのルートになります。
そして、現在のタスクはActivityManager
によって管理されます。
Android4.Xの「最近使ったアプリ」に表示されるものがタスクだという認識でおおよそ問題ないはずです。
タスクはいつ終わるのか
If the user leaves a task for a long time, the system clears the task of all activities except the root activity. When the user returns to the task again, only the root activity is restored.
ユーザーが一定時間タスクを離れた場合、システムは自動的にタスクをクリアし、再びルートのActivity
を起動します。
alwaysRetainTaskState
フラグを使うことで、この自動的にクリアする挙動を防げます。逆に、clearTaskOnLaunch
フラグを使うことでランチャーからアプリを起動した場合に必ずタスクをクリアすることができます。
また押さえるべきポイントとして、タスクのクリアとキルは別物という点があります。「最近使ったアプリ」からタスクを消去した場合、そのアプリのプロセス(後述)も終了しますが、タスクがクリアされてもアプリのプロセスが保持される ことがあります。
タスクは複数のアプリケーションで構成される
タスクという概念が抽象化しているのは、その名の通り「ユーザーの作業」です。それは複数のアプリケーションで構成されることがあります。
例えば、写真を撮影し、それを編集し、メールで投稿する場合を考えてみると、
- カメラアプリ
- 画像加工アプリ
- メーラーアプリ
の3つのアプリケーションのActivityを横断することがあります。
もし他アプリケーションの呼び出しが「ユーザーの一連の作業の一部」であるならば、単純にstartAcitvity()
で呼び出します。
そうでない場合、他アプリケーションのタスクを明示的に開始することを示すためにFLAG_ACTIVITY_NEW_TASK
のフラグを利用します。
プロセス
Processes and Threads | Android Developers
When an application component starts and the application does not have any other components running, the Android system starts a new Linux process for the application with a single thread of execution.
実行中の他のコンポーネントのない、アプリケーションのコンポートネントが起動された場合、Androidのシステムは単一の実行スレッドを持つLinuxプロセスを開始します。
タスクがActivity
の集合であり、複数のアプリケーションを含めることができたのに対して、プロセスはアプリケーション単位で一つだけ存在します。
does not have any other components running という点も重要です。通常、実行中のアプリケーションのコンポーネントはプロセスを共有します。
複数のプロセスを持つアプリも作れますがそれはどうでもいいので省略。
Androidのstatic変数が消えるとき
最近のAndroid端末はスペックが良くなったのでこの現象に遭遇することもなくなりましたが、古い記事を見ると「良く分からないけどAndroidのstatic変数は勝手に消えるから使うべきではない」みたいな情報があります。
これは正しくは、「タスクは生きているが、プロセスが死んだ」状態で発生します。
他のアプリケーションがメモリに負荷をかけるなどした場合、Androidシステムは自動的に余計なプロセスをkillします。一方、ActivityManager
がタスクをクリアする条件は「ユーザーがタスクを離れてから一定時間経過した場合」なので、killされたプロセスのActivity
を含む中途のタスクが、ActivityManager
に残るケースがあります。
このとき、タスクを再開するとそのアプリケーションのプロセスが新規に開始され、「static変数がクリアされた状態」となります。
解放されたstatic変数のオブジェクトにアクセスして、NullPointerException
というのが出来の悪いアプリでよくあるバグです。ほとんどの場合、原因はルートのActivity
で初期化処理をやっているせいです。初期化処理は適切な場所で行いましょう。
よくある勘違い
「Activityを保持しない」で、メモリがない状況を再現できる
あのオプションはActivity
を強制的にfinish()
しているだけなので、アプリケーションのプロセス消失を再現することはできません。
タスクキラーを使えば、長時間放置した状態を再現できる
タスクキラーの種類にもよります。
プロセスを殺すタイプだとタスクも消去されます。メモリを大量に確保するタイプのものを使いましょう。
今年入ってから読んだ本とか。
NFC Hacks
- 作者: 株式会社ブリリアントサービス
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/11/30
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る
2015年になって、ようやくNFCについて真面目に学びました。
iBeaconは流行した頃に触ったんですけど、NFCは「どうせiOSで使えないし~」という理由でスルーしていたのです。
NFCとiBeaconを同じカテゴリに入れてるところからして間違いでした。全然別物ですね。
この本はおおまかに、
という広いレイヤでNFCを扱っています。
ただ、この本を漫然と読んでも、書いてあることの意味を理解できないと思います。また、以下の点に注意が必要です。
- ページ的に仕方ないですが、コードは割と基本的なことしか書いていないです。また、出版時期の関係上、Android 4.4のHost card emulationのような比較的新しいAPIには触れていません。
- 標準規格寄りで書かれています。
「APIがあって、データの保持規格に共通仕様があるなら、簡単に読み書きできるんでしょ?」という甘い考えはあっさり崩壊しました。例えば交通系カードの履歴を読み込みたければFeliCaの仕様書を読んでコマンドコードを調べて、バイナリと格闘し、タグと通信する必要があります。
タグを買って実際にコードを書いて、そのとき副読書としてこの本を読むと、非常に的確な解説だと唸らされます。
ハイパフォーマンス ブラウザネットワーキング
ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化
- 作者: Ilya Grigorik,和田祐一郎,株式会社プログラミングシステム社
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/05/16
- メディア: 大型本
- この商品を含むブログ (2件) を見る
ブラウザネットワーキングとあるように、どちらかというとJavaScriptでフロントエンド最前線にいたり、インフラ系エンジニアに向けた本なのですが、「HTTP/1.1の何が通信のボトルネックになっていて、HTTP/2にすることでどう早くなるのか」について学ぶ上でこれを超える本はないと思います。
もし「SPDYに対応した通信ライブラリを使えば、それで勝手に高速になるんじゃないの?」とか思っているなら、とっとと読んだ方がいいです。
モバイルネットワークの歴史、どのようにネットワークを利用することが電力的に効率的なのかなどの章も必読です。原著はアメリカで採用された3G、4G規格を前提に話をしていますが、日本語版は「日本ではどうだったのか?」という部分をフォローしているので(それだけに限らず全体的に訳注が非常に丁寧)、日本語版がお奨めです。
Web SocketやWebRTCの章は流し読みしましたごめんなさい。ネイティブ開発の片手間のJavaScriptでやるには流行り廃りが激しすぎです…。
APIデザインの極意
APIデザインの極意 Java/NetBeansアーキテクト探究ノート
- 作者: Jaroslav Tulach,柴田芳樹
- 出版社/メーカー: インプレスジャパン
- 発売日: 2014/05/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る
これは個人的な娯楽として読みました。
読み解くには知識と恐らく経験が必要で、しかも凄まじい密度があり、かなり人を選ぶ本だと思っています。少なくとも、私は後方互換性に厳密であるべきAPIを公開した経験がないので、正しく解釈できている自信がありません。
なので感想として言えることは、これほど面白い本が邦訳されてくれてありがたいです、ということくらいです。
挿絵のセンスはないと思います。