ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Android アプリ開発における SQLite のロックとマルチスレッドの話

Android アプリ開発で SQLite を使っていると、しばしば次のような例外が投げられることがあります。*1

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

軽く調べてみたところ

このエラーをぐぐってみると、「複数スレッドから SQLite を使う場合に、それぞれのスレッドで異なる SQLiteDatabase オブジェクトを使っているとこのエラーが出ることがある」 とか書かれていて、回避策として 「複数スレッドで SQLite を使う場合は 1 つの SQLiteDatabase オブジェクトを複数スレッドで使いまわすこと」 というような情報が得られました。 あとは当たり前ですが 「トランザクションがちゃんと閉じられているか確認すること」 とか。

いくつかの質問やブログ記事を見てみましたが、いずこも上のようなことが書かれていて、具体的にこの例外が発生する条件などがよくわかりませんでした。

あるスレッドでトランザクションを張っているときに別のスレッドで挿入などを行おうとすると 『SQLiteDatabaseLockedException が投げられます』、ということが書かれていたブログ記事も見つけたのですが、手元で確認したところ、トランザクションを張ってる時に別スレッドで insertOrThrow メソッドを呼び出しても必ずしもエラーになるわけじゃなくて、やっぱりよくわかんないなぁという感じでした。

そんなわけなのでちょっと詳細を調べてみることにしました。

調査

下記のようなテストコードを書いて動作を調べました。

次のような環境で実行しました。

API level によって待てる時間が違ってたりするっぽい (ちゃんと調べてない) ので、環境によっては一部のテストに失敗するかもしれません。

結果として

結論としては、次のような知見が得られました。

ベストプラクティス?

ベストプラクティス的なものはまだはっきりとは見えてないのですが、ぐぐって出てきた情報や今回調べたことなどを元に今のところわかっていることを書いておくと次のようになります。

詳細

*1:この例は API level 18 のもの。 API level 7 だと “android.database.sqlite.SQLiteException: error code 5: database is locked” というような例外

*2:API level 11 より前だと SQLiteException

*3:永遠に待ち続けるのかどうかは調べていない。

*4:API level 11 より前だと SQLiteException

*5:API level 11 より前だと SQLiteException

プログラムに対する適切なコメント付けについて考えてる

上記記事を読んだ。 「コメントを入れるか入れないか」 について言及されていて、ちょうど最近、人から 「nobuoka はコメントを書きすぎだと思う」 と指摘されたところだったので、『Code Complete』 に興味がわいた。 『Code Complete』 はそのうち読むとして、そういう本を読んでみる前に自分のコメントについて振り返ってみようと思う。

コメントを入れるか入れないか
  • これまでのコメント論をきちんと表しており、この部分は本当におもしろかった
  • 結論としては
    • ややこしいコードの意味をコメントで説明するくらいならコードを綺麗にすべきだが、コードよりも抽象的レベルで説明するコメントは絶対に必要
    • 特に複数人なら。一人でも未来の自分に向けて必要。
    • しかし、コードを復唱するコメントは最悪である
  • コメントの最適な数としては大体10ステートメントに1個くらいで、プログラムが最も明瞭になるという研究がある
職業プログラマーなら必ず読むべき「Code Complete」 - $shibayu36->blog;

なぜコメントを書くのか

私がコメントを書くのは、基本的には将来の自分や他人がそのコードを読むときに読みやすくなるように、という目的である。 これはおそらく大多数のプログラマがそうなのではないかと思う。

その目的部分ではみんな合意できると思うのだけれど、問題は 「どういうコメントがあると読みやすいと感じるのか」 あるいは 「読みにくいと感じるのか」 という部分はプログラマごとによって違うということだと思う。

なので、読み手として 「自分自身はどういう場合にコメントがあると嬉しいのか」 みたいなのを実例付きで書いてみる。

どういうコメントがあると嬉しいか

自分自身、どういうコメントがあると嬉しいと思っていて、実際にどういうコメントを書いているのか。

コードだけからは読み取れない 「意図」 を説明するコメント

例 1 のように、「なぜそういうことをするのか」 を説明するコメント。

なぜそうするのか、という意図はコードからは読み取れないわけなので、意図を説明するコメントに対して否定的になる人はほとんど居ないと思う。

クラスやメソッド、フィールドなどの説明をするコメント

例 2 のようにクラスの説明をしてたり、例 3 のようにメソッドとその引数などの説明をしてたり、例 4 のように変数の説明をしたりするコメント。 私はコードを読んで理解するのに結構時間がかかるので、こういうコメントがあると嬉しい。

例 2 や例 4 に関しては 「そもそも名づけが糞である」 と思うし、例 2 だと本来は OAuth 1.0 の RFC の URL でも張っておくべきところだとも思うが、まあそれは今回は良いとしよう。

最近 「こういう説明は実装を読めばわかるから書かないでほしい。 実装を読むときの邪魔になる」 って言われて、そういう人もいるのかー、と思ったりした。 上の例は実装が簡単なのでまあコメントがなくても読むのにかかる時間はそんなに変わらないだろうけど、もうちょっと長かったり難しい処理をしているメソッドなどを読むことを考えると、私の場合はコメントがある方がより速くコードを読める *1 と思う。 少なくとも (読む必要がないコメントでも) 邪魔とは感じないかなー。 まあそこら辺は人それぞれなのかもしれない。

今回挙げた例だと仕様的な部分は全部クラス内に閉じているので、「encode メソッドによるパーセントエンコードの対象になるのは 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', '~' を除く全ての文字である」 ということはクラス内を全部調べればわかることではあるけど、encode メソッドを使いたいときにいちいちクラス内の実装を全部読みたいとは思わない。 それに動的型付けの言語だと、(コメントに書かれていないと) そのメソッドに渡されてくる引数の型さえも呼び出し側を見ないと全くわからない場合もあるわけで、「あるメソッドを使いたいと思った時に、引数に渡すべき型を知るためにそのメソッドを呼び出している別の箇所を調べる」 などをいうことをする必要がある。 個人的にはやりたくないなー。

まあ思想が統一されていないのは、コメントを付けるとか付けないというどちらの方針よりも良くないと思うので、複数人が関わるプロジェクトの場合はそのプロジェクトの指針に従うべきだと思ってる。

処理の塊を説明するコメント

例 5 の removeBackgroundImage メソッドの中のコメントに書いているように、「設定値を変更して保存する」 「見た目を更新する」 「ファイルを削除する」 という具合に、理想的にはそれぞれメソッドに切り出して抽象化すべきような内容を同じメソッドに書く場合などに、処理の塊が明示されるようにコメントを書くことがある。

「理想的にはメソッドに切り出して抽象化すべき」 と上に書いたが、実際のところ 2 行の処理をプライベートメソッドに切り出す必要性はないと思うので、こういうコメントを書いておく形が個人的には良いと思っている。 まあ今回の例だと 2 行の処理に対して 1 行のコメントを付けてるので、「こんなコメント書くな糞が」 と言われても仕方ないかなーという気はするし、個人的なプロジェクトの場合しかこういうことはしてないつもりではある。

TODO など

例 6 のように TODO などが書かれたコメント。

TODO とか FIXME とか XXX とか、そもそもそういうものを書かないで済むようにするのが理想ではあるけど、実際にはそういうのは残ってしまうので、コメントに明記しておくのが良い。 これについても 「意図」 のコメントと同じで否定的に思う人はほとんど居ないと思う。

ドキュメントなどへの誘導

例 7 や 8 のように、ドキュメントの URL などが書かれたコメント。

自分が探すのに苦労したドキュメントなど、将来の自分がそのコードを読んでドキュメントを見たいと思った時に探すのに苦労すると考えられる場合は URL などを書いておく。 個人プロジェクトの場合はわりと気軽に使うけど、複数人が触るコードの場合は気軽には使わないようにしている。

利用者に対する注意など

例 9 のように 「このメソッドを使うときはこういうことに注意すること」 みたいなコメント。

パブリックなメソッドの場合はこういうコメントを書くべきではなくて、実装側で安全にしておくべき。 一方で、ある範囲内でしか使用されないプライベートなメソッドの場合は、その範囲内の実装者が気を付けられると考えられる場合はコメントに注意点を書いておいて、実装時に注意する、などすればよいと思う。 例えばクラス内にプライベートなメソッドとか。 例 9 の場合はクラス外に公開されているメソッドだけど、個人プロジェクトだし 「プロジェクト内にプライベートである」 と考えられるので、コメントを付けて注意喚起する程度で十分だと思って、そのようにした。

メソッドの実装者が知っておくべき仕様

例 10 の JSON における数値の表記形式の仕様が書かれたコメントのように、メソッドの仕様の説明ではなく、メソッド実装者が知っておくべき仕様を書いたもの。

RFC などの URL を張っておけばいいという話かもしれないけど、実装する立場の人間としてはそのメソッドに関する部分だけコメントで書かれていた方が嬉しい。 しかしその場合でも RFC などの URL は張っておくべき (少なくとも参照した仕様がなんであるかが明確にわかるようにすべき) だと思うので、上のコメントはその点では糞だと思う。

その他

大体そんな感じかなー。 あとは著作権表記をコメントに書いたり、Javadoc とかだとコードサンプルを書いたりすることもあるけど、それはコードとしてのコメントというよりはドキュメントとしてのコメントなのでその他扱いでいいような気がする。 他には、設計のことを書いたりすることはあるかなぁ、という程度か。

適切なコメントについて考えてる

今までコメントがなくて困ったことはあってもコメントがあって困ったことはなかったので、こないだ 「コメントがあると邪魔」 って言われて結構驚きがあった。 なので最近は適切なコメント量、あるいはコメントの種類について考えたりしてる。

shiba_yu36 さんの記事を読んだところ、『Code Complete』 には以下のように書かれてるらしいけど、詳細を知りたい感じだ。

  • コードよりも抽象的レベルで説明するコメントは絶対に必要
  • コードを復唱するコメントは最悪

自分のコメントはコードを復唱するもののつもりではないけど、実際には復唱するものに分類されるのかなー。 あるいは抽象化のレベルが適切でないとか。 うーん。

info.vividcode.util.json.JsonParser クラス は 2 年前に書いたコードで今見るとひどい感じの実装ではあるけど、コメント付けに関しては今の自分が見ても過不足ないと思う (が、逆にいえば実装をもうちょっとまともにすればコメントは減ると思う) ので、自分自身が自分自身のために書くコメントについてはちゃんとできてるかなーというのが今のところの実感としてある。 問題は複数人で触るコードだよなぁ。 他人のコードの読み書きについての経験は浅いので、そこら辺は経験を積むと同時に本を読んで勉強したいところ。

まあ何はともあれ 『Code Complete』 を読んで、それからまたこの記事に書いたことを振り返ってみよう。

Code Complete第2版〈上〉―完全なプログラミングを目指して

Code Complete第2版〈上〉―完全なプログラミングを目指して

Code Complete第2版〈下〉―完全なプログラミングを目指して

Code Complete第2版〈下〉―完全なプログラミングを目指して

*1:ここで言う 「コードを読む」 というのは、当該メソッドの実装を読む場合と当該メソッドを使用しているコードを読む場合の両方を含んでいる。 また、当該メソッドを初めて読む場合も、既にそのメソッドを知っており記憶がおぼろげながらに存在する場合も両方含んでいる。

Windows ストアアプリ開発において UI 部品をページコントロールで実装する

Windows ストアアプリを JavaScript で開発する話です。 アプリを複数のページで構成する場合、基本的にはベースを単一の HTML ページにして、その上に次々とページコントロールを読み込むことでページ遷移を実現します。

ここで使用する ページコントロール (WinJS.UI.Pages.PageControl オブジェクト) ですが、名前からしてページ全体を表すためのもののように思えます。 しかし、実際にはページ全体に限らずページの一部を構成する UI 部品 (UI コンポーネント) をページコントロールで実装することもできます

UI 部品をページコントロールで実装する

1 つのページ内に複数の複雑な処理を行う UI 部品を配置する場合 (そして、それらの UI 部品における処理同士はそんなに密接に絡み合っていない場合)、保守することを考えるとそれらの UI 部品はそれぞれ別々のファイルにして実装したいところであります。 また、複数のページに表示したい UI 部品がある場合も、その UI 部品はページ全体のファイルとは別にして管理したいところです。

UI 部品を別ファイルで管理する方法としては色々な方法が考えられますが、最も簡単な方法はページコントロールとして作成することだと思います。 ページコントロールの中にページコントロールをレンダリングする場合にどんなコードを書けばいいのかを書いておきます

基本的なこと : ページコントロールの作り方とページ中へのレンダリングの仕方

一応基本を。 Visual Studio 2012 を使用して Windows ストアアプリを開発している場合、ページコントロールを作るには "ソリューションエクスプローラー" の中のページコントロールを作成したい場所のディレクトリを右クリックして、"追加" → "新しい項目" から "ページコントロール" を選択することでページコントロールのためのファイル (ページコントロールを構成する HTML と CSS, JavaScript の 3 つのファイル) が生成されます。

そして、そのページコントロールをページ中に表示するには以下のような方法があります。 (他にも方法はあります。) ただし、render メソッドやコンストラクタ呼び出しの直後からページの内容が使えるようになるわけではないことに注意が必要です。

// WinJS.UI.Pages.render メソッドを使う方法
var elem = document.getElementById("page-control-host");
WinJS.UI.Pages.render("/pages/pageControl.html", elem);

// PageControl オブジェクトコンストラクタを使う方法
var MyPageControl = WinJS.UI.Pages.get("/pages/pageControl.html");
var elem = document.getElementById("page-control-host");
new MyPageControl(elem);

ページコントロール中にページコントロールを埋め込む

さて、ページコントロールの初期化処理の途中で別のページコントロールを読み込む場合、そのページコントロールを埋め込むタイミングを考える必要があります。

ページコントロールのコンストラクタを呼び出して新たなインスタンスを生成する際、ページコントロールのインスタンスに定義されているいくつかのメソッドが順に呼ばれます。

  1. init : ページコントロールの内容がセットされる前に呼び出される
  2. load : もともとの HTML ファイルから作られた DOM 木のコピーを行う (普通はオーバーロードせずにデフォルトのまま使えばよい)
  3. processed : ページコントロールの内容がセットされ、WinJS.UI.processAll() の呼び出しも行われた後に呼び出される
  4. ready : ページコントロールのレンダリングの一連の流れの最後に呼び出される

これらのメソッドについては WinJS.UI.Pages.IPageControlMembers インターフェイスのドキュメント をご覧ください *1

これらの処理の中でページコントロール自身に別のページコントロールを読み込ませるために最適なタイミングは processed メソッドの中です *2。 また、読み込んだページコントロールの内容が揃う (読み込んだページコントロールの processed メソッドの処理が完了する) までは、ready メソッドが呼び出されないようにする必要もあります。 これは、processed メソッドの返り値として、子も含めてすべてのページコントロールの処理が完了した時に fulfilled される Promise オブジェクトを返すことで実現できます *3

これらのことを踏まえたうえで、別のページコントロール (下の例では ChildUI) を読み込むページコントロール (下の例では ParentUI) を定義する最低限の JavaScript の記述は以下のようになります。

(function () {
    "use strict";

    // このページコントロールの中に読み込む別のページコントロール (子のページコントロール)
    var ChildUI = WinJS.UI.Pages.get("/pages/ChildUI.html");

    WinJS.UI.Pages.define("/pages/ParentUI.html", {
        /// <field type="HTMLElement" domElement="true" hidden="true">このページコントロールの最上位要素</field>
        element: null,
        /// <field type="Array">child ui のリスト</field>
        _childUIs: null,
            // ここにプロパティを書いておくと IntelliSense の恩恵を受けられる (値は null でいい)

        init: function (element, options) {
            // 通常はコンストラクタで行うような処理をここに書けばよい
            this._childUIs = [];
        },
        processed: function (element, options) {
            // ページコントロール ChildUI をレンダリングする先の HTML 要素を取得する
            // (この例では複数個読み込む)
            var childUIContainers = element.getElementsByClassName("child-ui-container");
            // 読み込んだページコントロールの初期化処理が processed まで完了したら fulfilled される
            // Promise オブジェクトを入れておくための配列
            var promisesToBeWaited = [];
            for (var i = 0, len = childUIContainers.length; i < len; ++i) {
                var childUI = new ChildUI(childUIContainers.item(i));
                this._childUIs.push(childUI);
                promisesToBeWaited.push(childUI.renderComplete);
            }
            // 全ての ChildUI の processed が終わるのを待つ
            return WinJS.Promise.join(promisesToBeWaited).then(function () {
                // ChildUI の ready よりも自身の ready を後に実行する様に, イベントループのキューに入っている別の処理を先に実行させる
                return WinJS.Promise.timeout(0);
            });
        },
        ready: function (element, options) {
            /// <summary>processed の処理が全て終わった後にページコントロールの仕組みによって自動的に呼び出される処理</summary>
            // イベントリスナの設定とかはここでやればいい
        },
        unload: function () {
            /// <summary>この UI の終了処理を行う</summary>
            // 子のページコントロールの unload メソッドを呼び出す
            this._childUIs.forEach(function (childUI) {
                if (childUI.unload) childUI.unload();
            });
        }
    });
})();

しかしよく考えてみたら

読み込むページコントロールを静的に決定できる場合は、以下のように HTML 側で data-win-control 属性に入れておけばいいですね (下の例は読み込むページコントロールを ChildUI でグローバルに参照できる場合)。 上のような面倒なことしなくていいですし。 unload の呼び出しを忘れないようにしないといけないので、その点だけ注意が必要です。

<div class="child-ui" data-win-control="ChildUI"></div>

読み込むページコントロールを静的には決定できない場合などは JavaScript で読み込む必要があるので、そういう場合は上で述べた方法を使うのがいいのではないでしょうか。

*1:ちなみに、これらのメソッドはデフォルトで定義されていますが、WinJS.UI.Pages.define メソッドを使って新たなページコントロールのコンストラクタを定義する際に、第 2 引数として渡すオブジェクトにこれらを定義しておくことでオーバーライドできます。

*2:ready メソッドは、それが呼び出された時点ですでにページコントロールの中身は使用可能な状態になっている必要があるので、ready メソッドの中では遅すぎます。 また、init メソッドの中ではページコントロールのコンテンツが何もない状態ですので、子のページコントロールを読み込む先の HTML 要素もまだ存在しません。 よって、init メソッドの中では早すぎます。

*3:ページコントロールの初期化処理の中で、processed メソッドが Promise オブジェクトを返した場合、それが fulfilled されるまで ready メソッドは呼び出されないため。