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

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

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

UWP アプリ 「みお×ぽん」 のバージョン 1.1.0 をリリースしました

2016 年 10 月 1 日、IIJmio の 「IIJmio 高速モバイル / D サービス」 が au の回線にも対応して 「IIJmioバイルサービス」 に改称されました。

それに合わせて、「IIJmioバイルサービス」 のクーポン切り替えを行うことができる UWP アプリ 「みお×ぽん」 をバージョンアップしました。

www.microsoft.com

文言変更や一部デザイン変更、バグ修正のみであんまり大きな変更はしていないのですが、ご利用くださっている方はバージョンアップしてくださいませ。

変更内容

  • IIJmio 高速モバイル / D サービス」 という文言を 「IIJmioバイルサービス」 に変更。
  • 画面幅が小さい場合には、閉じた状態のナビゲーションウィンドウを完全に非表示にするように変更。
  • UWP アプリのライフサイクルを適切に扱えていなかった問題を修正。 *1

ちなみに公式アプリもあるよ

「みお×ぽん」 の最初のリリース時には IIJ による公式の UWP アプリはなかったのですが、2016 年 6 月ごろに公式アプリがリリースされました。

やったぜ!

*1:昔の MS のサンプルプロジェクト通りの実装だとだめっぽい。 これに関してはまた書こうと思う。

レスポンシブデザインのために resize イベントを使うのはやめて matchMedia メソッドを使おう

レスポンシブデザインのために CSS メディアクエリを使うことが多いと思います。 CSS 側だけで完結したらいいのですが、JavaScript 側でも画面サイズの変更を検知したかったり、画面サイズ以外のメディアクエリ相当のことをしたくなったりすることはありますよね。

画面サイズの変更自体は window に発生する resize イベント (window.onresize イベントハンドラ) で検知できますが、CSS メディアクエリとこれを組み合わせてレスポンシブ対応しようとすると以下の問題がでてきます。

  • ウィンドウサイズ変更時に resize イベントが高頻度で発生するので、resize イベントのリスナでコストのかかる処理を行うのはよくないとされる。 (Throttling することが推奨される。)
  • CSS メディアクエリと完全に対応するものではないので、CSS 側との対応を取りづらい。

上の方はまあ throttling すればいいのですが、下の問題はどうしようもないですね。

window.matchMedia メソッドと MediaQueryList

そこで別の方法としておすすめしたいのが window.matchMedia メソッドとその返り値の MediaQueryList オブジェクトです。 標準化についてはまだ完了しておらず、CSSOM View Module で作業されているようです。 とはいえ最近のブラウザだとどのブラウザでも使えるみたいなので、実用的に使っていける状況になっているかと思います。 (IE 9 とか少し古めの Android のブラウザとかでは使えないので、そこら辺のサポートが必要ならまだ使えませんが><)

window.matchMedia メソッドの引数としてメディアクエリのリスト (media query list: メディアクエリをカンマ区切りで繋げたもの) を渡すと、それらのメディアクエリのリストを表す MediaQueryList オブジェクトが返されます。 MediaQueryList#matches プロパティを使うことで、リスト中のメディアクエリのうち少なくとも 1 つ以上が真になっているか、もしくはすべてが偽であるかを判別できます。

また、MediaQueryList#addListenr メソッドでイベントリスナを設定することで、matches の値の変化を検知することができます。

// TypeScript です。

// 縦 600px 以下、または横 600px 以下の場合に matches state が真になるメディアクエリリスト。
var mql = window.matchMedia("(max-width: 600px), (max-height: 600px)");
// メディアクエリリストの matches state に応じた処理を行う関数。
function handleMediaQueryListMatchesState(matches: boolean) {
    if (matches) {
        // メディアクエリリストの matches state が真の場合の処理。
    } else {
        // 偽の場合の処理。
    }
}
// イベントリスナを設定して matches state の変化を検知。
mql.addListener((evt) => handleMediaQueryListMatchesState(evt.matches));
// 初期化。
handleMediaQueryListMatchesState(mql.matches);

便利ですね。 非対応ブラウザを切っていいようでしたらどんどん使っていきましょう。

歴史的経緯?

ところで MediaQueryListaddEventListener メソッドを持っているはずなのに、それとは別に addListener メソッドも持っていてどうなってるんだろう、と思いますよね。 私も思いました。 どうやら昔は MediaQueryList は独自のコールバックの仕組みを使っていて、addEventListener を持っていなかったようです。 また、コールバックメソッドに引数として渡される値も MediaQueryListEvent ではなく、MediaQueryList オブジェクトがそのまま渡されていたようです。

Note: This specification initially had a custom callback mechanism with addListener() and removeListener(), and the callback was invoked with the associated media query list as argument. Now the normal event mechanism is used instead. For backwards compatibility, the addListener() and removeListener() methods are basically aliases for addEventListener() and removeEventListener(), respectively, and the change event masquerades as a MediaQueryList.

CSSOM View Module, 4.2. The MediaQueryList Interface

実際に試したところ、Firefox 49.0.1 や Edge 38.14393.0.0 では古い挙動になっていました。 Chrome 53.0.2785.116m は最新の CSSOM View Module にあった実装になっていました。 Firefox や Edge の実装はまだ最新の CSSOM View Module にあった実装になっていないので、しばらくは addListener メソッドを使っていくようにするのが良さそうです。

他の方法

CSS 側でメディアクエリを使って特定要素のプロパティを変更するなどして、JS 側からはそのプロパティを見ることでどのメディアクエリが有効になってるのか検査するのが今のところは安定、という話も。 IE 9 や古めの Android 端末をサポートするならそういう方法が良さそうです。 (ということですよね? 他の理由があるなら教えてください!)

レスポンシブデザインのために resize イベントを使うのはやめて matchMedia メソッドを使おう - ひだまりソケットは壊れない

今はまだ画面サイズ検知用の要素作って、font-familyとかをメディアクエリで変更するのが一番楽かな。font-family: "sp";とか。resizeイベント側はfont名を見るだけ。ただイベントで変化を検知できるのはいいなぁ

2016/10/02 14:57


参考

PowerShell (Windows) で Docker コンテナにホストディレクトリをデータボリュームとしてマウントする際に pwd 相当のことをしたい

試した環境

  • Windows 10 Home (Anniversary Update; 64-bit 版)
  • Docker Toolbox 1.12.0

前提知識

絶対パスで記述するためにどうするかが問題

Linux では以下のように pwd コマンドを使うのが一般的かと思います。

docker run -v $(pwd)/path/to/target:/container/path ...

じゃあ Windows (PowerShell) でどうするのか、というのが問題です。 PowerShell にも pwd コマンド相当の Get-Location コマンドレット があるのですが、これをそのまま使うとパスの形式が通常の Windows のパス表記の形式になるので、docker コマンドの -v オプションに渡せません。

Write-Output "$(Get-Location)/path/to/target"
# => C:\Users\userName\Documents\project/path/to/target
# 本当は /c/Users/userName/Cocuments/project/path/to/target という形式で欲しい

# 実際にやってみると以下のようなエラーが出る。
docker run -v "$(Get-Location)/path/to/target:/container/path" ...
C:\Program Files\Docker Toolbox\docker.exe: Error response from daemon: Invalid bind mount spec "C:\\Users\\userName\\Documents\\project/path/to/target:/container/path": invalid mode: /container/path.
See 'C:\Program Files\Docker Toolbox\docker.exe run --help'.

雑に対応する

そんなわけで、以下のように Windows の通常のパスの表記を Linux ぽい感じに変換する関数を定義して使っています。

function pwd_as_linux {
  "/$((pwd).Drive.Name.ToLowerInvariant())/$((pwd).Path.Replace('\', '/').Substring(3))"
}

# 実際使う場合は以下のような感じ。
docker run -v "$(pwd_as_linux)/path/to/target:/container/path" ...

関数を定義するまでもなく変数に格納して使いたいって感じなら以下のようにもできます。

# Linux 風のパス形式。
$pwd_as_linux = "/$((pwd).Drive.Name.ToLowerInvariant())/$((pwd).Path.Replace('\', '/').Substring(3))"

# 使う。
docker run -v ${pwd_as_linux}/path/to/target:/container/path ...

Docker Engine 側で通常の Windows のパス形式を受け付けてくれたらいいのですが、コロンが区切りに使われてるから難しいんだろうなぁと思ったり。

参考ページ

株式会社はてなを退職しました

私事で恐縮ですが、2016 年 8 月 31 日付で株式会社はてなを退職しました。 はてなユーザーの皆様や一緒に仕事をした皆様、また、仕事以外でも勉強会などで一緒になった皆様、大変お世話になりました。 ありがとうございました!

f:id:nobuoka:20160930125506j:plain
いろいろと送別の品を頂きました! ありがとうございます!!

入社したのは 2012 年 4 月で、当時は本格的な web サービス開発について全然知らないという状態でした。 そこから約 4 年間、Perl での web アプリケーションの開発から JavaScript や TypeScript でのフロントエンド開発、Windows ストアアプリや Android アプリといったクライアントアプリ、開発環境周りなどを経験し、社内の人にも社外の人にもいろいろと教えてもらいながら web サービスのエンジニアとして成長することができました。 本当にありがとうございました。 まだまだはてなでやってみたいことはありましたし、貢献できる部分もあるだろうとは思ったのですが、私的な事情により今回退職することを決めました。

退職することになりはしましたが、私自身ははてなで働けて良かったと思っておりますし、今年上場するなど勢いもありますので、はてなのサービス開発に興味がある方やインターネットで人の生活をよりよくしていきたいという方はぜひぜひはてなの採用に応募してみてはいかがでしょうか! (辞めた人の目線で話を聞いてみたいという方がいらっしゃいましたら *1 お声がけくださいー。)

さて、はてなについてはおいておいて私自身のことを言いますと、今後も web サービス開発に携わっていくつもりですし、勉強会などにも参加すると思いますので、今後ともどうぞよろしくお願いいたします!

ところで

退職する少し前からインターネット上での活動を控えていたせいか、何名かの方から 「生きてますか?」 というようなメッセージを頂きました。 心配してくださってありがとうございます。 活動を控えている理由は私的な事情で、本当に単に控えているだけですのでご安心ください。 *2

(ここに書かれていた内容については、当事者との相談をもって削除しました。)

おわりに

皆様が平穏無事な日々を過ごせますよう祈っております。

*1:いない気はするけど

*2:ソフトウェア開発関係や仕事関係についてはインターネット上での活動を控えてると厳しいのでそろそろ活動していきます。

Android の Instrumented Test で指定のサイズのテストだけ実行する (@SmallTest とか @LargeTest とか)

Android Testing Support Library (ATSL) の話。 バージョン 0.5 時点での情報です。

ライブラリの準備方法などはドキュメントを読んでください。

テストサイズを表すアノテーション

android.support.test.filters というパッケージがあって、この中にはテストをフィルタするのに使用できるアノテーションが入っています。 その中に、テストのサイズを表すためのアノテーションが 3 つ入っています。

これらはテストクラス自体に付けることもできますし、メソッド単体に付けることもできます。 実装を見たところ、メソッドとクラスの両方にアノテーションが付けられている場合はメソッドアノテーションが優先されるようです。

ちなみに、Android Testing Support Library じゃなくて android.test.suitebuilder.annotation パッケージの方にも同名のアノテーションがありますが、そっちは deprecated ぽいし AndroidJUnitRunner と組み合わせて使うことはできないぽいので注意しましょう。

AndroidJUnitRunner とテストサイズによるフィルタリング

AndroidJUnitRunner でテストを実行する場合、テストサイズによるフィルタリングが可能です。 下記 Javadoc にいろいろ書かれています。

adb でテスト実行する場合

上のドキュメントでは、adb コマンドでテストを実行する際にどういうオプションを渡せばいいかが主に書かれています。 例えば、small サイズのテストのみを実行する場合は、以下のようになります。

adb shell am instrument -w -e size small your.test.target/android.support.test.runner.AndroidJUnitRunner

Gradle の connectedAndroidTest タスクで実行する場合

AndroidManifest.xml で指定する (ただし現在はバグで動かない)

上のドキュメントには All arguments can also be specified in the in the AndroidManifest via a meta-data tag ということが書かれています。 すなわち、テスト用のアプリパッケージの AndroidManifest.xml ファイル (app/src/androidTest/AndroidManifest.xml ファイル) に以下のような記述をすると、small サイズのテストのみが実行されるはず、ということです。

    <instrumentation
        android:name="android.support.test.runner.AndroidJUnitRunner"
        android:targetPackage="...">
        <meta-data android:name="size"
                   android:value="small"/>
    </instrumentation>

ただし現在はバグで動きません。 (指定が無視されます。) バグ報告を上げたので、対応状況は下記ページをご覧ください。

バグがなければ、上のような記述をしておけば ./gradlew :app:connectedAndroidTest という感じで Gradle タスクでテストを実行したときにテストサイズのフィルタリングが効きます。

Gradle のビルドスクリプトで指定する

AndroidManifest.xml に書かずに Gradle のビルドスクリプトにオプションを指定することができます。 上の方法の代わりにこの方法を使うことで、上のバグを回避できます。 sumio さんに教えて頂きました。


android {
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        testInstrumentationRunnerArguments size: 'small'
    }
}

上のような感じで書きます。

Android Studio 上でテストを実行する場合

Android Studio 上でテストを実行する場合は、build.gradle での記述が有効にならないので、configurations を弄る必要があります。 下図のような感じで、Extra options に 「-e size small」 と入力すれば良いです。

f:id:nobuoka:20160527031054p:plain

おわり

@SmallTest アノテーションとか @LargeTest アノテーションとかがあって、テスト実行時にそれらのフィルタリングができるという情報を過去に見たのですが、いざやってみるとあまり情報がなくてうまくいかなかったのでまとめてみました。

コミットごとのテストは @SmallTest だけにして、ある程度開発が終わってレビューに出す前の段階になったら @MediumTest@LargeTest のテストも行うようにする、とかにしたらテストにかかる時間が短くなっていいかもしれませんね。

関連ページ