@numa08 猫耳帽子の女の子

明日目が覚めたら俺達の業界が夢のような世界になっているとイイナ。

DJI Tello のビデオストリーム iOS でデコードする

できあがったものがこちら

github.com

4月の終わりにDJIから発売されたトイドローン、Tello。発売と同時に購入したけど、前評判通りに高い安定性を発揮してくる上、風がなければ屋外でも遊べるので、今までとは違うアングルからの写真撮影が楽しめて面白い。

そんな Tello は今のところスマホWifiで接続をして操作することしかできない。この値段帯のトイドローンならだいたいプロポが付いて来るんだけど、そういうのを削って本体のコストにしたってことなのかな?

あと、一応UDPで通信をするAPIもあるのだけれど、純正アプリと同じことはできない。純正アプリではカメラの画像をリアルタイムにプレビューできるし、前後左右上下の操作以外にもフリップや360度回転、手のひら着地なんかのコマンドを実行できる。一方で公開されているAPIは前後左右上下の操作を行うことしかできない。

そういった部分に不満を覚えつつ、夜な夜なパケットキャプチャしたデータを眺めていたら、すでに同じようなことをしていた人が海外にはいたらしい。go言語で書かれたiot用ライブラリのgobotにtelloの隠しインターフェースが追加されて公開されていた。

Hello, Tello - Hacking Drones With Go

日本版が出る前に公開されたエントリーなので、最初にこのブログをみつければパケットを開くこともなかっただろうな。

このエントリーのサンプルコードを実行するだけで、 mplayer を利用して動画のプレビューをPCから行うことができる。さらに、動画の仕様に関しては次のように書かれていた。

The streaming video is also sent to the ground station on a different UDP port. Unfortunately the Tello does not just use one of the existing standards for streaming video such as RTSP. Instead, the raw video packets are sent via UDP and need to be reassembled and decoded before they can be viewed.

Since the size of a single video frame is larger than the size of a single UDP packet, the Tello breaks apart each frame, and sends the packets with header of 2 bytes to indicate how they need to be reassembled.

Position Usage 0 Sequence number 1 Sub-sequence number 2-n Video data The video data itself is just H264 encoded YUV420p. Using this information, it is possible to decode the video using standard tools such as ffmpeg, once you remove the header bytes.

H264でエンコードされたYUV420pのピクセルデータだけど、いわゆる動画配信用のプロトコルには則っていなくて、生のバイナリをUDPで送っているので適当にデコードする必要があるよ!!って感じ。

H264でエンコードされた動画だったら、ffmpeg + lib264 とかでデコードできそうだなと思ったので、iOS 上でやってみることにした。

コード解説

ffmpeg や libx264 のビルドはすでにビルド用のスクリプトを作ってくれた人がいたので、それを利用した。

github.com

github.com

実際に動画のフレームをデコードする実装の抜粋が以下。

gistb93addb5b78d3a6417df2e7d77412655

コードの全体はこちら

Turkey/ImageDecoder.mm at 430f060486b902ee0f9178b4dc20bdc7de2242e8 · numa08/Turkey · GitHub

ffmpeg にバイナリを与える

利用しているのは ffmpeg , libx264 、そしてさっきの gobot を iOS 用にラップしたライブラリ。gobot は tello が送ってくる動画のバイナリをコールバックで渡してくるので、バイナリを処理する必要がある。アプリ側はバイナリを得ることはできるのだけれど、どうやらffmpegのインターフェースを利用する場合、直接バイナリを渡すには AVIOContext インスタンスを作って、コールバックを利用してバイナリを返す必要があるのだけれど、ちょっと考えることが多くて面倒くさい。

そこで、 NSPipe というAPIを利用することにした。

NSPipe - Foundation | Apple Developer Documentation

Linuxpipe(8)をラップしたAPIで、 pipe の作成、そして書き込みと読み込み用の file descriptor を作ってくれる。また、linux は pipe を作ると pipe:<file descriptor> というファイル名でアクセスをすることができるようになる(これの仕様ってどこに書かれてるんだ?)。この仕組みを利用することにした。

init[NSPipe pipe] を使って NSPipeインスタンスを生成する。 Tello と通信を行い動画のバイナリを取得する onNewFrame: では [self.fifoPipe.fileHandleForWriting writeData:packet]; を行うことで動画のフレームを書き込んでいる。 ffmpeg を利用する captureInFFMpeg では

int fileDescriptor = self.fifoPipe.fileHandleForReading.fileDescriptor;
const char* file = [NSString stringWithFormat:@"pipe:%d", fileDescriptor].UTF8String;
// 中略
ret = avformat_open_input(&format_context, file, input_format, &format_dictionary);

として pipe:<file descriptor> というパスのファイルをパラメータにして ffmpeg に与えている。こうすることでファイルの入力なら簡単にできる ffmpeg にファイルに見せかけたバイナリを渡す仕組みを実現することができた。

正しいバイナリを作る

Tello から取得したバイナリを ffmpeg に与えることができたのであとは ffmpegAPIを使ってデコーダーを実装していく。これはそんなに面白くないので割愛。

しかし、実は Tello から送られてくるバイナリは厳密には動画の生のバイナリではない。実は1つのフレームのバイナリを8個の送信に分割して送ってくる。今回、アプリを作り始めるまで知らなかったのだけれど udp は送信容量に上限がある。

UDPパケットサイズと転送レートの関係:プログラマー社長のブログ:オルタナティブ・ブログ

Ethernetフレームは最大サイズが1518バイトですので、そこから、Ethernetヘッダ(14バイト)とFCS(4バイト)を除いた、1500バイトが送出できる最大サイズになります。IPv4UDPを送出するには、IPv4ヘッダ(20バイト以上)とUDPヘッダ(8バイト)を除き、1472バイト以下がデータサイズになります。

今まで心を無にしてHTTPを触ってきたので、こんな仕様があることを知らなかった。動画のバイナリは 1472 バイトを余裕で超えるので、分割されるというわけ。どうやら8個のパケットに分割されているようなので、8という数字を決め打ちにしてしまって実装を進めることにした。

動画のフレームを受け取るonNewFrame: で、8個分の NSDayaNSArray を作り、マージして書き込みを行う。

[self.packetArray addObject:frame];
if (self.packetArray.count == 8) {
  NSMutableData *packet = [NSMutableData data];
  for (NSData *d in self.packetArray) {
    [packet appendData:d];
  }
[self.fifoPipe.fileHandleForWriting writeData:packet];
self.packetArray = [NSMutableArray array];
}

先頭に正しい動画のフレームを挿入する

ここまで実装をしてどうにか iPhone の画面上に Tello の動画をプレビューすることができた。

そのしばらく後

コードを全く変えていないのに h264 がデコードに失敗するようになった。なんで??なんで??

どうやら、先頭のフレームに含まれているヘッダー情報を正しく読み込むことができなくて失敗しているように思えたので、じゃあ正しい情報を与えてやればいけるのでは・・・?と思い実装。

PCで取得をした Tello の動画のフレームの先頭をプロジェクトに加えて、Telloが動画フレームを返すより前に ffmpeg に渡してやることにした。コードではopenのあたり。

// 正しいフレーム情報をもった動画ファイルを読み込んで、
// avformat_find_stream_info を成功させる
dispatch_async(writeFifoQueue, ^{
        NSString *p = [[NSBundle mainBundle] pathForResource:@"movie" ofType:@"mov"];
        FILE *i_file = fopen(p.UTF8String, "r");
        if (i_file == NULL) {
            NSLog(@"failed fopen");
            return;
        }
        int buffer_size = 10 * 1024;
        char buff[buffer_size];
        size_t size;
        while (true) {
            size = fread(buff, buffer_size, 1, i_file);
            if (size == 0) {
                break;
            }
            write(self.fifoPipe.fileHandleForWriting.fileDescriptor, buff, buffer_size);
        }
});

ちょっとパワーの有る方法だったけどこれで確実に成功するようになった。めでたい!!ちゃんとgcdを利用していたので、変なブロック処理やwaitを挟む必要が無いのも偉い。

感想

iOSシステムコールって読んで良いんだ・・・?

最近、LinuxシステムコールAPIに関する本を読んで勉強し直したところだったのでちゃんと役立てられて嬉しい。

numa08.hateblo.jp

C言語最高!!これで行きていける!!!みたいなことを考えることもなく、それでもswiftとかkotlinとかレイヤーの高いところで生きていきたいのだけれど、とは言え触ることができるっていうのは良いことだと思う。

あと、go言語で書かれたライブラリを利用できたのも良かった。個人的にgo言語は好きな言語で、CLIのツールを作ってgithubのスターを稼いだこともあったけど、しばらくご無沙汰だったため、久しぶりに利用できたことが素直に嬉しい。はじめ、gobotのtello関連のモジュールだけをswiftに移植しようかと考えていたけど、流石に面倒だったのでやめた。gomobile を利用することでiOSAndroid向けのアプリやライブラリをビルドすることができることは知っていたけど試したことはなかったので、良い挑戦だった。と言っても、gobotが依存しているライブラリも少ないためか、特に何もしなくてもそのまま素直にビルドをすることができた。時代はgo言語って感じ。

今後

この手順で最終的に UIImage インスタンスを取得することができたので、例えば Vision framework なんかを利用して自動操縦の自撮りドローンなんかを実装できそうだなーって考えている。

ふつうのLinuxプログラミング 第2版 を読んだ

今年の頭からLinuxAPIシステムコール、libcなんかについてちゃんと勉強をしようという気持ちになったので、それ関連の本を読んでた。

numa08.hateblo.jp

前回読んだ「[試して理解] Linuxのしくみ」という本も良い本で、LinuxというOSがどういう仕組で動いているのかという触りを理解するためにかなり役立った。そこから一歩踏み込んで、ちゃんとLinuxシステムコールやlibcのAPIを使えるようになりたいと思ったので、次の本を読んだ。

実は、第1版が家にあったんだけど(たぶん、奥さんが買った)、利用しているOSにFedora Coreとあったので、内容が古いなと感じたため第2版を購入した。久しぶりにこういう本を読みながら写経をしていたのだけど、やっぱり紙の本が良いなと思ってしまった。

初めてやってみるのにはちょうどいい構成の本だった

あんまりLinuxの基本的な仕組みについては詳しくない自分でも読み進めることができる内容だった。本の構成としてはシステムコールAPIの解説の後に headcat と言ったコマンドを模したプログラムを作る例題がある。そのあとで、少しレベルの上がった練習問題が掲載されている。練習問題の解答はサポートサイトにある。

練習問題の内容は本の内容から見るとちょっと難しいので、自分の理解度に応じて挑戦をしてみるのが良いと思った。実際、自分もちょっと後回しにして本を読むことを優先していった。ただ、最初の方で man コマンドの利用方法や読み方の解説があるので自分で関連するAPIを調べながら進めることができた。

練習問題の内容は多少レベルが高いかもしれないけれど、例題や解説の書き方は非常に親切で分かりやすかった。manページは情報が全て書かれているけど、その分、文章が分かりにくくかったりするので、本の内容を読んでからmanを見ると「あ〜なるほど〜」ってなる。

章の構成はストリームやファイルシステム、シグナルの取扱を一通り学んだあとで最終的にネットワークプログラミングへと進んでいく。ネットワークプログラミングと言っても、TCPのソケット通信を行うくらいでページとしては少ない。そして、最後に総仕上げのおさらいとして、HTTPサーバーの構築を行う。この組み合わせが面白かった。

HTTPサーバーは、ファイルシステム上のファイルの読み込みに加えて、シグナルの取扱やマルチプロセス、ソケット通信を行う必要があることから本の内容のおさらいとしてはぴったりの内容だと感じた。

また、この本を読む前に[試して理解]Linuxのしくみを読んでおくと、登場するAPIシステムコールが何を行っているものなのかという事前知識を持って挑むことができるので、ちょうど良かった。

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

この本を読んだあと

「詳解Unixプログラミング」のような分厚い本に手を出しても良いかもしれないと考えている。その一方で、ここまでで蓄えた知識を使って、なんかのアプリケーションを作ってもいいなーっとも思っている。戯れに、二酸化炭素濃度センサーを購入してみた。

ja.aliexpress.com

せっかく raspberry pi もあるので、 Linux の仕組みの上でこういったセンサー類を利用しやすくする何かを作っても良いかもしれない。

[試して理解]Linuxのしくみ を読んだ

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

普段使っているOS, Linux がどういう仕組みで成り立ち動作をしているのかをデバイスのレイヤーを含めて解説してくれている本。

以前、「Androidを支える技術I, II」を読んだときにLinuxに関する知識が全然足りてないなぁと感じたこともあって手に取った。

numa08.hateblo.jp

numa08.hateblo.jp

Androidを支える技術II」の後半では、アプリケーションのプロセス管理の仕組みを解説する章がある。その中では、Linuxのプロセス管理がどうなっているのかを解説した上で、Androidがどういうふうにその仕組をカスタマイズしたのかの解説が行われていた。この章を始めとして、「Androidを支える技術」の本の中では、AndroidがLinuxOSであるという仕組み上、Linuxの仕組みを知っておいたほうが理解が進むなと感じる章立てとなっていた。

自分はLinuxをわりと日常の中でもよく利用している。アプリケーションの運用こそは最近は行っていないけれど、1年前は業務の中でやっていた。他にもアプリケーションの開発環境をLinux上で行うことはよくあるし、今もやっている。その中で、よく発生するのが性能低下であったり、可用性の確保をしなければならないシーンだ。

アプリケーションの性能が低下したときに、一体何が起こっているのかをまずは解析しなければならない。幸い、Linuxには様々なOSの状態を表示するためのコマンドが揃っているが、それらが表示してくれる数字を理解するのはまだ人間の仕事だ。freeコマンドでメモリの使用量が出るが、様々な数字があって何を意味しているのかを知らなければならない。sarコマンドで様々な数字が出るが、一体この数字をどう読んで何をしなければならないのかを知っておかなければならない。

この本は、そう言ったアプリケーションの運用をする上でLinuxがどういう仕組でOSを動作させ、プロセスを実行し、デバイスを利用しているのかを丁寧に解説してくれる本だ。コマンドの利用方法ではなく、仕組みの解説を行うための本となっている。OSを動作させ、プロセスを実行し、システムコールを発行し処理して、デバイスにアクセスを行うことはそれだけでオーバーヘッドとなりシステムの性能低下を招く。従って、まずは仕組みを正しく理解しておくことで、どこでどういうオーバーヘッドが発生し、何を利用すればそれを発見することができるのかが分かる。発見ができたら次は対策だ。

この本は、更にサンプルコードを利用した実験もある。システムコールの発行やプロセスの実行、デバイスアクセスと言った動作を自分の書いたコードを使って実験し、観測することができる。言語としては主にC言語、ときどきPythonまたはシェルスクリプトと言った感じ。言語に関する詳しい解説はないので、一応C言語くらいは利用できる状態で読みたい本だ。

また、この本は良くも悪くも初心者向けの本だけれどLinuxを全く触ったことが無い場合にはしんどいかもしれない。基本的なファイル操作やエディタの操作、パッケージ管理ソフトの導入に関する部分については知っておいたほうが良さそう。そして、この本を読んでさらに次のレベルにステップアップをしたいと思える内容だった。

本の後ろの方に、次に読むべき本がいくつかリストアップされていたので手を出してみたい。

Linuxを使ってアプリケーションの開発や運用を行う上で、絶対に読んでおきたい本だった。

京都行った

大阪に引っ越しをして半年くらいが経過して、そう言えば以前から行きたかった京都鉄道博物館にまだ行っていないことを思い出したので行ってきた。

混むことは分かっていたので、平日を狙っていったけどそれでも入館は並ぶことになった。

ホテルが良かった

www.jalan.net

京都駅の南、徒歩5分くらいで九条の駅の手前くらいにあるホテルを今回は利用した。ここが思った以上に良かった。

内装や外装は落ち着きのある、都会的なおしゃれデザインに統一されていた。ロビー部分にはソファーはもちろん、焚き火を囲むことができるようになっていた。落ち着いた照明でリラックスできるし、しかも面積はそこまで無いけれど空まで吹き抜けになっていたので開放感があった。

宿泊者はウェルカムドリンクをサービスしてもらえるのもポイントが高い。焚き火を囲んでソファーに座り込んでハイボールを飲むと、それだけで一日の疲れが解消されていく。

お風呂上がりにはフリードリンクコーナーもあり、これもまた落ち着きのあるカフェのようなラウンジスペースでコーヒーやジュースを頂くことができた。眠り前にホッと一息をつくことができて、しっかりと休むことができたと思う。

部屋も適度に広くて清潔感のあるベッドだった。

これで、一人晩御飯抜きで6,500円だったのでかなりリーズナブル。費用対効果が最高のホテルだった。

晩御飯はリッチに決めた

ホテルの料金を抑えることができたし、今回は京都往復で交通費もそんなにかからない事を考えて、ちょっと良いものを食べようと計画していた。色々と案を出す中で上がったのが、三嶋亭のすき焼き。

www.mishima-tei.co.jp

名前は聞いたことがあったし、四条の方に遊びに行ったときに前を通ったことはあったけれど、実際に入ろうとは思ったことがなかった。でも、確実に美味しいだろうなと思ったので予約。

外食ですき焼きを食べることなんてあまりないし、外食の中でも今までにないくらいにリッチなやつなのでかなり期待をしていった。

これが、かなり最高。お肉や野菜は適量を焼いてもらえて食べるタイミングを完全にコントロールしてもらえる。最高のタイミングで食べることができた。そして、肉もでかくて分厚くてそれでいて美味しい。焼いてない状態で手のひらくらいの大きさのお肉だったけど、口に入れると柔らかくて溶けていくようだった。

これが、良い肉・・・

本命、京都鉄道博物館

先にホテルとか晩御飯のことを書いたのはこの部分の記事が長すぎるから。今回、京都旅行は1泊2日を予定していて、1日目は京都鉄道博物館に、2日目は適当に京都観光の予定だった。その予定だったのだけれども、京都鉄道博物館を回りきることができなくて2日目も鉄道博物館に行った。まさか、2日連続で行くことになるとは思わなかった。

博物館を回りきることができなくて2日連続で行くという自体が自分の中で初めてのことだったけれど、さらに2日連続で回っても回りきることができなくて、後日改めて行くことにした。

公式サイトによれば2時間くらいで回ることができると言うけれど、2日じゃ足りなかったのでかなり回り方が下手だったというか、スローペースだったと言うか。

そんなところだったけれど、その分充実した時間を過ごすことができた。

面白かったものの写真を幾つか掲載してみる。

例えば、0系新幹線の自動運転関連の装置。昔の写真で見る超大型の計算機って感じで興味深かった。運転台のところの壁の一面がこの計算機になっているのだけれど、これより前の世代の車両にはこんなものは無かったし、後の世代の車両にも無かった。後の世代の車両では、たぶん小型化に成功してもっと小さなものになったんだろうと思うけど、前の世代の車両ではそもそもこんな計算装置が無くて、いかに新幹線という乗り物が他の乗り物と一線を画する特別なものだったのかが分かるようだった。

他にも、昔から憧れたトワイライトエクスプレスが展示されている部分もあった。超高級列車として存在感があったけど、結局、一度も乗ることもなくその運行が終わってしまった。ブルートレインの全滅もあって、子供の時に寝台列車に乗れなかったことがただただ惜しいと思う。

あと、500系新幹線のエヴァコラボのものが展示されている。通常時は普通の500系新幹線になるのかな?絨毯の上に新幹線が置いてあるという状況が面白い。デザインの格好良さは圧倒的だよなぁ。

車両以外でも様々な展示があって、例えば通票閉塞機の実物なんかもあるし、しかもサイド部分が透明になっていて内部の構造を眺められるのが楽しい。この閉塞機は大宮の鉄道博物館とか大洗駅なんかにもあって何度も見たけれど、内部の構造が分かるものは無かった。

閉塞機の後ろには、実際の信号やポイントをコントロールできるシミュレーターもあって、正しく入力を行えばポイントを動かすことができる。

こういうものを全部見ていたら結局時間がなくなったので、2日目に持ち越した。

2日目で思ったことは、1日目に吸収をした知識の復習ができる環境が整っていること。入り口に展示されている0系新幹線蒸気機関車C61なんかのブレーキの構造や連結器の種類なんかを全て眺めながら見ていくことになったので、やっぱり時間が足りなかった。

2日間かけて館内は全部見たけれど扇形車庫を見るには至らなかった。

記憶も曖昧な年の頃、まだ梅小路蒸気機関車館だったころに扇形車庫は見たのだけれど今の年齢になって見るものとはまた違うんだろうなぁと思いを馳せた。

そんなわけで、まだ見足りないし、2日間かけて蓄えた知識もあるのでそれらの復習もかねて何度も何度も通いたいと思える場所だった。年間パス無いかなぁ・・・。

アルテミスを読んだ

アルテミス(上) (ハヤカワ文庫SF)

アルテミス(上) (ハヤカワ文庫SF)

アルテミス 下 (ハヤカワ文庫SF)

アルテミス 下 (ハヤカワ文庫SF)

近未来の月面を舞台に、密輸業を営む主人公の女性が不可解な依頼を受けたことをきっかけに、微妙な人間関係の人々と手を組んで月都市を巻き込む陰謀に立ち向かうストーリー。

物語の登場人物が多いのだけれど、その一人ひとりがストーリーの終盤に絡んでくる構成にワクワクした。

作者は火星の人と同じ、アンディ・ウィアー氏。火星の人のときもそうだったけれど、困難な状況に面していながらも明るい語り口と科学知識で課題を解決していく軽快なノリが楽しい作品だ。

主人公は溶接の技師でもあって、物語中に何度かこのテクニックが発揮されるシーンが登場する。月面という低重力、ほぼ真空空間中での溶接作業なんて今までの人生の中で全く想像したこともないようなテクニックを発揮する。この中でやはり作者の持つ化学の知識が発揮され、何が危ないのか、何が困難のか丁寧な解説が行われる。それでも、専門書のように小難しい内容になるのではなくテンポよくストーリーが進行していく。 主人公も魅力いっぱいだ。月面都市のアルテミスのアンダーグラウンドというか、そっち系に顔の効く彼女は誰からも愛されるタイプのキャラクターだ。関係が悪化しても殺したいほどの子ではない、いつまでたっても悪ガキのような少女のような。設定年齢よりも少し幼く感じた。あんまりリッチではないけれど、精一杯生きている感じ。そんな彼女だからこそ、街を誰よりも愛していて、それゆえに皆をまとめることができたんだろうな。

知らなかったけれど、この作品も映画化が決定しているらしい。楽しみ。

作者の構想としては、月面都市アルテミスを舞台とした別のストーリーを作っていく思いがあるらしい。次回作にも期待をしたい。

Androidを支える技術II を読んだ

組み込みOSでありつつもセキュリティアップデートやユーザがインストールするアプリケーションに対応しそれらをマルチタスクで実行し、さらにユーザのリテラシーはサーバーやPCと比べても低いであると想像される。それが、Androidに求められた要件。これを、一般的なPCやサーバーと比較して貧弱な携帯電話上に構築する必要があった。

自分が初めてAndroidのアプリ作りを経験したのは、2009年か2010年のことだったと思う。初めて本を買ってきて4つのコンポーネントを学び、コードを意味もわからず写経していたとき、なぜ画面単位をActivityと言うもので管理するのだろうか?と考えていた。それ以前にGUIのプログラミンををやったことがあるわけでもなかったので、「きっと頭の良い人が考えれば理解ができる点なんだろうな」と思って後回しにしていた。それから10年近く経った今の今まで深くその理由を考えることなく進んできた。

本書はActivityが生成されて画面に出るまで、そしてどういった条件下でActivityが破棄されるのかのプロセスをLinuxのレイヤーから順番に追いかける構成になっている。アプリのインストールが行われたらPackageManagerServiceがpackage.xmlを更新してIntentを解決できるようにし、端末がブートしたときに関連するサービス群を起動し、Activityをスタックで管理する仕組みの中にユーザが起動したActivityを含めていく・・・という流れになると言える。

更に、再生成におけるデータの保持を実現するためのBundleの仕組みについての解説もある。どうやってActivityのもつデータを一時的に保持しておくのか、再生成のときだけ復元するのかの仕組みをコードとともに紹介している。実は自分はBandleは一度ファイルI/Oが発生するものだと思っていたけど、そう言う訳ではないらしい。こう言った誤った知識が正しく訂正されるきっかけとなるのも嬉しい。

Androidソースコードに注目をした本は他にもあったと思うが、やはりこの本が別なのはLinux的部分ではなくてAndroid的な部分に重点を置いていること、そして話題とする部分を絞って解説してくれることで、注目するべきソースコードが何のかがわかりやすくなる構成にあると思う。この本で言えばActivityの起動に関するプロセスについてのみの話題で1冊が構成されている。手広くなって読者目線で何が起こっているのかわからなくなってしまう、ということが無いのだ。

Androidのアプリを開発する中で、Zygoteというプロセスを見かけることはよくある。スタックトレースを辿っていくと最後の方に現れるやつだ。あれが一体何なのかは雰囲気的にランループを作るところなのかな?くらいの認識だったけれど、その実態はアプリケーションの起動を司るめちゃくちゃ重要なシステムサービスの1つだ。今まで単語が出てきても深くは考えずスルーしていた部分についてしっかりと理解をすることで、今後のバク調査やアプリ開発でも役立つかもしれない。

この本を読んで学んだことの1つに、1つのアプリケーションは1つのプロセスになるとは限らないと言えることだと思う。Androidアプリのパターンの1つにApplicationクラスの参照ツリーの中にデータを持たせて、アプリの中からSingletonのように利用するパターンが有るように思う。しかし、Applicationクラスはプロセスに対して一意ではあるが、外部から起動なActivityやServiceの提供をしている場合などにアプリのプロセスが2つ以上になることはあるようだ。そうなると、アプリの中でSingletonだと思っていたデータは実はプロセスの中でのSingletonであるという点が問題になってくるケースがあるかもしれない。良い具体例は思いつかないけれど。

誰もが使うパターンだからと言って、そのまま心を無にして利用することの危険さを思い出した。

今一度自分の知識をおさらいして、間違えた理解がないのか、間違った考え方をしていないのかを確認する意味でもこの本は非常に有用だ。そして、それ以上に普段は意識することのないアプリよりも下のレイヤーに対する詳細な解説があるおかげで、やってはいけないパターンに思いを馳せたり、パフォーマンスを高める工夫を発見したり、バグが発生したときの効率の良い調査を実現できるだろう。

少なくとも、Androidアプリ開発を現役でバリバリ行う人には必読書であると感じている。

DroidKaigi 2018 まとめ #droidkaigi

「ブログを書くまでが勉強会だ」って古い言葉もあったなとちょっと思い出しつつ。qiitaに書こうかとも思ったけどポエムっぽくなりそうだったのでこっちで。

自分が聞いたセッションのまとめと会場の雰囲気のまとめを作っていこうと思います。

セッションのまとめ

kotlinアンチパターン

Kotlinアンチパターン

感想

kotlinは便利で機能が多いので色々と使ってみたくなるが、あんまりやるとよろしくないよねって感じ。詳しい解説は資料を参照すればいいと思うので、ちょっと思い出したことを1つ。

late init を使うか lazy を使うか

プロパティをlateinit varにすることでインスタンス初期化時にプロパティを初期化できなくても、Nullableにすることなくプロパティをクラスの中で利用できて便利。例えばfindViewByIdなんかの結果得られたViewのインスタンスlate varにしておくのはよくあるパターンだと思う。

その一方でval by lazyも便利で、kotlinのDelegated property を利用することで実現している遅延評価の機能なんだけど変数をvalで宣言できるのが嬉しい。

どっちを使うのが正しいのか判断に困ることもときどきあって、資料中にもあるようにfindViewByIdby lazyクロージャの中で実行した結果、Viewの再利用に対応できない地雷も踏んだ。

lazy valをどこで使うかと言えば、例えばRealmRetrofitのような初期化にコストがかかるとされているオブジェクトの初期化だと思う。ただ、リソースの解放やFragmentの場合、再利用されることも考えなきゃいけない。

onStopRealm.closeしたあとでActivityFragmentが再利用されてclose済みのRealmにアクセスしてしまうなんてこともあった。このあたりを防ぐならスコープを広げるか狭めるかになるのでやっぱりby lazyを使うところじゃないかもしれない。

Applicationのライフサイクルで管理されるReamなどの場合は初期化のタイミングをアプリが起動したタイミングから適切にずらることは意味があると思う。

Androidの場合、UIのコンポーネント内でby lazyを使って初期化を行うパターンが使える部分は少ないかもしれない。

Inside Android Architecture Components

Inside Architecture Components // Speaker Deck

感想

昨年の Google I/O で公開されたけど、今のところ業務で利用をしたことは無かった Android Architecture Component の中身の解説。ありがたいありがたい。

Android のライフサイクルはかなり複雑だという感覚はみんな持っていると思うけど使いこなせないと「お前、そんなのもできないの?」みたいな空気感がある気がする(気のせい

Architecture Component の LifeCycle コンポーネントの導入で多少はマシになるかな?と思った。リソースの管理をActivityのライフサイクルに対応して行う必要がある場合、Activityから完全に分離したところで実現できるようになるためコードの複雑化を抑えられるだろう。

ちなみに、自分は以前はonCreateonStopを定義したinterfaceを作り、その実装にリソースの管理を行わせてActivityのプロパティにもたせ、Activityのライフサイクルコールバックで呼び出していたりした。

interface LifecycleResource {
  fun onCreate()
  fun onStop()
}

class NetworkResource: LifecycleResource {
  lateinit var restService: RestService

  fun onCreate() {
    restService = //初期化
  }

  fun onStop() {}
}

class Activity: Activity() {
  val resource = MutableList<LifecycleResource>()
  
  fun onCreate(saved: Bandle) {
    resources.add(NetworkResource())
    resources.forEach { it.onCreate() }
  }
  
  fun onStop() {
    resource.forEach { it.onStop() }
    resource.clear()
  }
}

こういうのを自前でやる必要がなくなったことに意味がある。

また、ViewModelというコンポーネントがどうなっているのかは調査の必要性を感じた。Activityと一対一になるコンポーネントだけどActivityのライフサイクルより長い期間生存ができるので、画面回転のタイミングでデータが失われないということ(だと思う

あとで出てくるけど、MVIパターンによるアプリの設計実装を行うときにも重要なコンポーネントとなっている。ただ、Activityのライフサイクルよりも長い時間生存している部分についてはまだ自分の理解が足りていななって思った。画面回転のときでもActivityonDestroyがコールバックされるけれど、それはActivityの「破棄」としては認識されないってことなんだろうな。ActivityThreadとかActivityManagerServiceと連携して難しいことをやってるのか?そのうち調べる。

Android における Model-View-Intent アーキテクチャ

Android における Model-View-Intent アーキテクチャ // Speaker Deck

感想

Androidでも、こんなReduxみたいなことができるようになってたのか〜(小並感

割と長い間、雰囲気でReactiveXというかRxJavaを使ってきたけどこういう風に使うのか〜ってちゃんと理解できたように思う。

  • データの流れが一方通行なこと
  • 副作用が入り込む場所が少ないこと

という点でこのやり方は良いなと思った。で、ちょっとコードを書いてみたけど気になった点もあった

  • ちょっとしたことをやるのでも用意するものが増えて大変
  • 画面遷移どうしようか
  • プロセス間通信とかBroadcastReceiverとかのことも考えると大変かもなぁ

みたいなところ。画面遷移に関してはセッション中にも「これ!っていうやり方がない」って話だった。同時にプロセス間通信を使ってActivityServiceで通信をするときや、BroadcastReceiverの通知を受け取る仕組みなんかをつくるときも、ActivityViewModelで完結しないかなって思う。

こればっかりはしょうが無いのか。

でもやっぱり、設計としてはシンプルだし考えることが少なくなって良いと思った。積極的に取り入れていきたい。

はじめてのUnitテスト

Unit Testing in a Nutshell - DroidKaigi 2018 // Speaker Deck DroidKaigi 2018で登壇してきたので振り返りとか補足とか - 怠惰を求めて勤勉に行き着く

感想?

id: fushiroyama さんのハンズオンを手伝ってきた。 droidkaigi 登壇者デビュー!めでたい!!(本当はちゃんと登壇したい

人前に出て話す機会が割りと久しぶりだったのでちょっと緊張をしたけど、まあまあうまくできたんじゃないかと思った。良かったことはいっぱいあるけど、やっぱりコミュニケーションが取れたことだと思う。

ハンズオン形式で、課題に行き詰まった人のお手伝いをする以外にも実際の業務の中での困りを色々と聞くことができた。なかなか表に出てこないリアルな現場の声だと思うし、実はみんなが求めている課題ってそこにあるんだろうなぁって感想。イシキタカイセッションや勉強会はいっぱいあるけど、自分の困りにぴったりマッチできるものってあんまりないからね。

と言いつつ、当日挙げられた質問をメモったりしてたわけでもないのであんまり覚えてない。ただ、いくつかはあって(懇親会のときに聞かれたものも被ってるかも

  1. Realm のテスト辛くないっすか?どうやってますか?

  2. Realm やめた。気合でモック作ってもいいけどそうするとRealmそのもののテストをやる感じになるから意味ない

  3. むっちゃマッチョなActivity、どうやってバラす?

  4. 絶対に忘れちゃいけないのは一発じゃバラせないってこと。 少しずつやっていく。ネットワークやDBにアクセスをしているようなUIに関係のない部分は最初の方にやる。 その次にまだActivityがマッチョなら、すこしずつViewのサブクラスにできないかを検討する。

そんなようなことを答えた。メモっときゃよかったなぁ。

Android Studio30分集中超絶技巧100選

Android Studio30分集中超絶技巧100選メモ DroidKaigi 2018 #DroidKaigi #DroidKaigi_room3

感想

便利

Elastic Team Building

Elastic Team Building // Speaker Deck

感想

成長していくチームの中でどうやってメンバーの増員やメンバー間のコミュニケーションを最適化していったのかの話。人が少ないときに人を取るため、国籍を問わず採用する戦略とか面接の時の基準の明確化で面接を効率化するとか確かにって感じ。

振り返ってみると自分のチームでも実践をしているところはあって、会議にアジェンダを作って共有をすることは必須だしダイレクトメッセージを利用しないとかは当たり前のことだと思っていたけど、ちょっと別のチームを眺めたりするとその限りでも無かったりするので世の中大変だなぁって思ってる。

自分たちのチームはここで言うところのサバイバルフェーズを脱したのかなって思ってはいる。学習フェーズをずっと続けている感じだろうな。コミュニケーションに関する障壁は自分たちのチームではかなり低い方で、会議でもアウトプットしやすい空気感があると思っている。あとは学習フェーズの突破方法か。

Elastic Leader Ship は読んでみようと思った。

エラスティックリーダーシップ ―自己組織化チームの育て方

エラスティックリーダーシップ ―自己組織化チームの育て方

アプリを成長させるためのログ取りとログ解析に必要なこと

アプリを成長させるためのログ取りとログ解析に必要なこと

感想

わりとなんとなくで取ってしまいがちなログをちゃんと目的とともに取るための方法の提案。何をどう増やせばビジネスが加速するのか、様々な指標が世の中にあるので、「何を計測」するためのログなのかをハッキリとさせることは重要。

バグ探しや動作の確認という意味でのログ取得ではない。

でっかい規模のチームでやっていて、実装する人とビジネスの人が完全に分かれている場合は分からないけど、自分たちのチームのように小さくてビジネスを考えることと実装を考えることがほぼイコールの場合、仮設を立てて計測して仮設と照らし合わせて・・・って流れになるのでログの取得一つに関してもしっかりとした設計が必要だと感じてる。

試料中には便利なロガーのラッパーを作った話もあるけれど、「何を回収したいのか」がビジネスによって違うことを考えると一概に一般化とかは難しいなってふと思った。

Android案件の見積もりに現れる要素、あるいは丁寧に埋設された地雷たち

Android案件見積りに現れる要素、あるいは丁寧に埋設された地雷たち

感想

わかる〜。

最近はスマホ案件の受託をあんまりやっていないのもあって、ご無沙汰なんだけど、やっぱり「iOSと同じようにして」って言うのはよく聞く。もう聞きたくない。

スプラッシュスクリーンなんかもActivityのライフサイクルを考えるとなかなか難しい。iOSと全く同じエクスペリエンスの提供は難しいってことをちゃんと分かってもらわないと辛いよなって。

人間の管理についても難しい。っていうか、こういうのって失敗しても成功してもよっぽどのことがないと後に残らないからあんまり共有されない気がするので有り難い。

エンジニアだって人間なんだから、新しい何かに挑戦をするようなワクワクする刺激がないとモチベーションが下がっちゃうのはしょうが無い。あと、コードレビューは本当に難しい。簡単に相手の人格を破壊できるしされる。自分の心がここまで弱いのかって驚く。

本当に些細な事だけど「レビューのときは必ず褒める」とかのルールを作っておかないと人間の心は簡単に死ぬ。

All you need is isolating the domain

Y.A.M の 雑記帳: Android アプリの開発でドメイン駆動設計に取り組む話

感想

Droid Kaigi に2年間連続物で登壇するの、すごいなぁって思いました。

難しいことで有名なエリック・エヴァンスのDDDをまとめて、現場で使った内容にして頂いた形式になっていて本当に有り難い。かなり実践的というか、実際に利用をする場合を想定したコードや事例の紹介がメインとなっていて、自分たちの現場でも使いたい気持ちが高まる。

値オブジェクトの活用や、ドメイン部分を別のモジュールにすることなどできることは多いように思った。そう言えば自分も以前ドメイン部分っぽい部分を別のモジュールにしていたことがあって、結局設計が別の部分がだめだったのでやめちゃったけれどそんなに悪くはなかったと思ってる。

次にアプリ作ることがあればもう一回実践してもいいかなー?

マルチログインの実装方法

DroidKaigi 2018にて「マルチログインの実装方法」という発表をしてきた - Just for Fun

感想

アカウントごとにデータを取得するRepositoryやメモリ、スレッドなどを管理する仕組みを分けようという提案。やり方の1つで面白いなと思った。ただし、これだと同時にログインをしている状態を作り出すことは難しいなって思った。

メモリの管理については、可変でグローバルな変数はまあ怖いから使いたくないけど、非同期のコールバックが別のアカウントの物が来てしまう現象なんかはあり得るなと思った。Daggerを利用すればスコープのアノテーションを活用していい感じにすることはできそう。

あとは、アカウントを管理するものにライフサイクルのような仕組みを導入してログアウトや非アクティブ化するときにコールバックメソッドの中で非同期処理のキャンセル処理やコールバックの無効化なんかをやるのかな?って考えている。

UIテストの実行時間を短縮させる方法

UIテストの実行時間を短縮させる方法

感想

実機やエミュレータで動作する上に実際のアプリを描画するUIテストを高速化させる話。

Espressoでsleep多用しないとかのコードレベルの話から実行時オプションの話まであって詳しい。本当によく調べられている。助かる、助かる・・・。

DroidKaigi の公式アプリについて言い訳をするなら、ViewMatcherが大変だったからってかんじになる。いい感じに目的のViewをみつけるための公文を探すのに疲れ果てて「あとはなんとかしてくれ〜」って気持ちでPR投げたら誰もなんとかしてくれなかった。

テスト関連の話はちょっとニッチ感が高い気がしていて、個人的にはテストに関して酸いも甘いもしっかりと知っておけば正しい状態で「テストをしない」ということも選択できていいかなって思っている。今後もテストに関しての情報収集をしっかりとやっていきたい。

全体の感想

今年はちゃんと公式アプリのコントリビューターになることができたし、登壇者の権利を手に入れたので少なくとも去年参加をしたときよりも何かが成長したように感じた。来年こそはセッション登壇を狙いたい。

セッション会場が去年よりも広くなったのは良かったし、車椅子のスペースがもしっかりとあって「やるじゃん、DroidKaigi!」って感じだった。

お昼ごはんの量もちょうどよかった。新宿にあるから足りなくてもすぐに近くのお店とかで補充ができるっていう安心感もあったかな。個人的なことだけど、朝ブラックサンダーを何個かかってから会場に行くようにしたのが良かった。脳のカロリー補充は大事やで。

空気感も穏やかなものだし、去年の課題だったと思う入場に関する部分もかなり改善されたようだった。やるじゃん、DroidKaigi。

その一方で会場が狭いかなーって気持ちもある。主に廊下とか。セッション間の混雑具合が本当にすごかった印象。もし可能なら廊下を一方通行にできたりすると改善されるのかな・・・?

今年のテーマは「ニッチ」が1つあったと思うけれど、それならもっとセッションの数を増やしてもっともっとニッチなやつがあっても良いな!って思った。

国内のカンファレンスではかなり規模があるものだと思うし、この規模をしっかりと運営して毎年改善しているスタッフは本当にすごい。それに、セッションの資料を作って登壇した方々もすごい。感謝の気持ちしかない。

1年後にまた会いたい。