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

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

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

Rust 入門してる #1

最近 Rust を学び始めました。 まだちまちま構文を追っているところですが、ドキュメントがしっかりしていて学びやすい印象です。 (手元では、現在の最新の安定板である Rust 1.7 を使っています。)

Web 上で実行できる環境も提供されています。

進捗 (4.1 節から 4.6 節まで)

とりあえず変数結合 (variable bindings) から初めて、関数やプリミティブ型、コメント、if やループといった制御構文あたりをざっと見てみました。

まだここら辺はそんなに特徴的な部分はないですね。 char 型の保持する値が Unicode スカラ値である、とか、例外により処理が戻らない関数 (diverging functions) のための戻り値の型は ! で宣言、といったあたりが個人的にはちょっと新鮮でした。

次の節は 「Ownership」 だったり、その次は 「References and Borrowing」 だったりするので、そのあたりから Rust っぽさが出てきそうです。

サンプルコード

各節の内容をコードに落とし込んだものです。

fn main() {
    hello_variable_bindings();
    hello_functions();
    hello_primitive_types();
    hello_comments();
    hello_if();
    hello_loops();
}

// 4.1 Variable bindings
// See : https://doc.rust-lang.org/book/variable-bindings.html
fn hello_variable_bindings() {
    println!("=== 4.1 Variable bindings ===");

    // `let` で変数結合 (variable binding) の割り当てを行う。
    let a = 100;
    println!("`a` is {}", a);

    // `let` 式の左辺にはパターンも使える。
    let (b, c) = (1, 2);
    println!("`b` is {}, c is {}", b, c);

    // コロンの後に型を書いて型注釈 (type annotation) も可能。
    let d: i32 = 5;
    println!("`d` is {}", d);

    // 通常は結合は不変 (immutable) だが、`mut` で可変 (mutable) にできる。
    let mut e = 100;
    println!("`e` is {}", e);
    e = 200;
    println!("`e` is {}", e);

    // 結合の初期化は使用までに行われればよい。 (使用までに行われなければならない。)
    let f;
    f = 10;
    println!("`f` is {}", f);

    // スコープとシャドーイング。
    { // スコープはブロック内。
        let g = 20;
        println!("`g` is {}", g);
        // ブロックの外の結合を使用できる。
        println!("outer `f` is {}", f);
        // 同じ名前の結合で前の結合を覆い隠せる (shadow)。 (外のブロックの変数結合を隠す例。)
        let f = 30;
        println!("inner `f` is {}", f);
    }
    // ここからはもう上のブロック内の `z` は見えない。
    // 同じスコープの結合も隠せる (shadow)。
    let f = "Another type"; // Mutable な変数結合に割り当てし直す場合と異なり、別の型の値を割り当てられる。
    println!("`f` is {}", f);
}

// 4.2 Functions
// See : https://doc.rust-lang.org/book/functions.html
fn hello_functions() {
    println!("=== 4.2 Functions ===");

    // `fn` の後に関数名、括弧で囲まれた引数、ブレースを続けて関数定義。
    // 引数には型の宣言が必要。
    fn print_sum(x: i32, y: i32) {
        println!("sum is {}", x + y);
    }
    print_sum(1, 2);

    // 引数の戻り値の型は `->` に続けて宣言する。
    // 最後の式が戻り値になる。
    // (返り値の型が `unit` 型 (`()`) の場合は省略できるっぽい?)
    fn sum(x: i32, y: i32) -> i32 {
        x + y
    }
    print_sum(sum(1, 2), 3);

    // 最後にセミコロンがあると戻り値は `unit` 型になる。
    fn sum2(x: i32, y: i32) -> () {
        x + y;
    }
    sum2(1, 2);

    // Rust は主には式 (expression) ベースで、2 種類の文 (statement) を除いて他は全て式。
    // 1 つめの文、‘declaration statements’ について。
    // 例えば `let` による変数結合は文。 よって `let x = (let y = 100)` みたいな記述はできない。
    // 結合済みの変数への割り当ては式だが、その値は空のタプルになる。
    let mut v1 = 100;
    println!("v1 is {}", v1);
    let v2 = v1 = 200; // `v2` は `200` ではなく `()`。
    println!("v1 is {}, v2 is {:?}", v1, v2);

    // 2 つめの文、‘expression statements’ は、任意の式を文にするもの。
    // Rust では式と式をセミコロンで分割し、文の連なりとして記述していく。
    // (なので、上で見たように関数の最後にセミコロンがあるかどうかで返り値が変化する。)

    // `return` キーワード。
    fn foo(x: i32) -> i32 {
        if x < 0 { return x }
        x + 1
    }
    println!("`foo(-5)` is {}", foo(-5));
    println!("`foo(5)` is {}", foo(5));
    
    // 処理が戻らない diverging functions のための戻り値は `!` で宣言。
    fn do_panic() -> ! {
        // `panic!` マクロは現在のスレッドの実行をクラッシュさせる。
        panic!("Always panic");
    }
    if false { // 実際にはクラッシュしないように。
        // Diverging function の返り値は任意の型として扱える。
        let panic_return: i32 = do_panic();
    }
    
    // 関数を指す変数結合も生成できる。
    let sum_func: fn (i32, i32) -> i32 = sum;
    println!("`sum_func(5, 6)` is {}", sum_func(5, 6));
}

// 4.3 Primitive Types
// See : https://doc.rust-lang.org/book/primitive-types.html
fn hello_primitive_types() {
    println!("=== 4.3 Primitive Types ===");

    // `bool` 型
    let b1 = true;
    let b2: bool = false;
    println!("`b1` is {}, `b2` is {}", b1, b2);

    // `char` 型
    // 単一のユニコードスカラ値 (コードポイントから上位と下位のサロゲートコードポイントを除いたもの) を表す。 4 バイトらしい。
    let c1 = 'a';
    let c2: char = '💛';
    println!("`c1` is {}, `c2` is {}", c1, c2);
    
    // 各種数値型
    // 符号の有無 (signed/unsigned)、サイズ (fixed/variable)、
    // 整数か浮動小数点数 (integer/floating-point) でカテゴリ分けされる。
    // 整数は符号付きと符号無しがある。 
    // 符号付整数の型には `i` が、符号無整数には `u` が使用される。
    // Fixed size types は型にそれぞれのビット数 (8、16、32、64) が使用される。
    let num1: i32 = -100; // 32 ビット符号付整数
    let num2: u8 = 3; // 8 ビット符号無整数
    println!("`num1` is {}, `num2` is {}", num1, num2);
    // Variable sized types は、マシンのポインタサイズに依存するサイズ。
    // 符号付は `isize`、符号無は `usize`。
    let num3: usize = 8;
    println!("`num3` is {}", num3);
    // 浮動小数点数は `f32` と `f64`。
    // IEEE-754 single and double precision numbers に相当。
    let num4: f32 = 10.5;
    println!("`num4` is {}", num4);

    // 配列
    // 同一型の要素からなりサイズ固定。 `[T; N]` で型が表現される。
    // `T` は要素の型。 `N` はサイズ。
    let array1: [i32; 7] = [0, 1, 2, 3, 4, 5, 6];
    println!("`array1` is {:?}", array1);
    // `[elem; size]` という省略表記で、全ての要素を `elem` で初期化したサイズ `size` の配列を生成。
    let array2: [i32; 5] = [10; 5];
    println!("`array2` is {:?}", array2);
    // サイズは `len()` で取得可能、各要素は `[n]` で取得可能 (インデックスは 0 から開始)。
    println!("Size of `array1` is {}, `array1[0]` is {}", array1.len(), array1[0]);

    // Slice
    // メモリ上で隣接する指定の型の値の並びに対するビュー。
    // 以下は配列に対する例だが、配列以外の様々なものに対して `&` と `[]` の組み合わせで slice を生成できる。
    let complete_slice = &array1[..]; // `array1` のすべての要素を含む slice 
    let middle_slice = &array1[1..4]; // `array1` のインデックス 1 から 3 までの要素を含む slice
    println!("`complete_slice` is {:?}, `middle_slice` is {:?}", complete_slice, middle_slice);

    // `str`
    // これ自身は便利ではないが、`&str` という感じでリファレンスで使用すると便利である。
    let str1: &'static str = "Hello, world!";
    println!("`str1` is {}", str1);

    // タプル
    // 固定サイズの順序付きリスト。
    let tuple1: (i32, &str) = (1, "hello");
    println!("`tuple1` is {:?}", tuple1);
    // destructuring let によりタプル内のフィールドにアクセスできる。
    let (tuple1_elem1, tuple1_elem2) = tuple1;
    println!("`tuple1_elem1` is {}, `tuple1_elem2` is {}", tuple1_elem1, tuple1_elem2);
    // Indexing syntax により、各要素にアクセスすることもできる。
    println!("`tuple1.0` is {}, `tuple1.1` is {}", tuple1.0, tuple1.1);
    // 1 要素のタプルを括弧で囲まれた式と区別するために、カンマを使う。
    let tuple2 = (2,);
    let not_tuple = (2);
    println!("`tuple2` is {:?}, `not_tuple` is {}", tuple2, not_tuple);

    // 上で見たように関数も型を持つ。
    fn function1(x: i32) -> i32 { x }
    let function2: fn(i32) -> i32 = function1;
    println!("`function2` is {:?}", function2);
}

// 4.4 Comments
// See : https://doc.rust-lang.org/book/comments.html
fn hello_comments() {
    println!("=== 4.4 Comments ===");

    // 行コメントと doc コメントがある。
    // `//` で始まるコメントは行コメント。
    
    // doc コメントは `///` で始める。

    /// doc コメントのサンプル用の関数。
    ///
    /// # 例
    /// 以下のように使用する。
    /// ```
    /// do_test(); // 呼び出し
    /// ```
    fn do_test() {
        println!("hello");
    }
    do_test();

    // `///` による doc コメントは、そのコメントの後ろにあるものに対するコメント。
    // 別の形式の doc コメントとして、`//!` というものがある。
    // これは、そのコメントを含んでいるものに対するコメント。
    // モジュールや crate (crate とは?) の内部で使用されるらしい。

    // doc コメントについて、さらに詳しいことは次のページに書かれている。
    // https://doc.rust-lang.org/book/documentation.html
}

// 4.5 if
// See : https://doc.rust-lang.org/book/if.html
fn hello_if() {
    println!("=== 4.5 if ===");

    // 条件が括弧で囲まれないのと本体がブロック必須なこと以外はわりと C に近いっぽい。
    let x = 5;
    if x == 5 {
        println!("`x` is five!");
    } else if x == 6 {
        println!("`x` is six!");
    } else {
        println!("`x` is not five or six :(");
    }

    // 式なので値を返す。
    // (`else` がない場合は常に `()` になるらしい。)
    let y = if true { 10 } else { 20 };
    println!("`y` is {}", y);
}

// 4.6 Loops
// See : https://doc.rust-lang.org/book/loops.html
fn hello_loops() {
    println!("=== 4.6 Loops ===");

    // 現在のところ `loop`、`while`、`for` の 3 種類。
 
    // 明示的に抜けない限り無限にループする。
    loop {
        println!("In `loop`");
        break;
    }

    // 条件付き。
    let mut count = 0;
    while count < 4 {
        println!("In `while, with `count` {}", count);
        count += 1;
    }

    // `for`
    for x in 0..4 {
        println!("In `for`, with `x` {}", x);
    }

    // `.enumerate()` で index を取得できる。
    for (index, value) in (5..8).enumerate() {
        println!("`index` is {}, `value` is {}", index, value);
    }

    // ループの制御には `break` と `continue` が使用できる。
    let mut y = 0;
    loop {
        y += 1;
        if y % 2 == 0 { continue }
        if y > 4 { break }
        println!("`y` is {}", y);
    }

    // ループにラベルを付けて、`continue` や `break` で指定できる。
    'outer: for x in 0..5 {
        'inner: for y in 0..5 {
            if x % 2 == 0 { continue 'outer }
            if y % 2 == 0 { continue 'inner }
            println!("x: {}, y: {}", x, y);
        }
    }
}

Gradle のマルチプロジェクト構成におけるプロジェクトの評価順 (設定フェーズにおけるプロジェクト間の依存関係)

Gradle の公式のドキュメントに書いてあるけどどこに書かれてるのか探すのにいつも手間取るのでメモっておく。

前提知識

  • Gradle のビルドには、「初期化」 (Initialization) と 「設定」 (Configuration) と 「実行」 (Execution) の 3 つのフェーズがある。 (Chapter 20. The Build Lifecycle)
  • マルチプロジェクトの場合、各プロジェクトが評価されていって 「設定」 フェーズが終わり、そのあと 「実行」 フェーズに移る。
  • 「実行」 フェーズではタスクの実行が行われるので、タスク間の依存関係により実行順が決まる。

プロジェクトの評価順

  • Chapter 24. Multi-project Builds に書かれてる。
  • 基本としては、各プロジェクトが名前のアルファベット順に評価される。
  • 入れ子になったプロジェクトの場合、上位のプロジェクトから下位のプロジェクトの順でプロジェクトが評価される。
  • あるプロジェクト (A とする) の評価時に、別のプロジェクト (B とする) で定義される何かを参照したり変更を加えたりする場合、A より先に B が評価されている必要がある。
  • プロジェクトの評価順序に依存関係を持たせるには、Project#evaluationDependsOn(String) メソッドが使用できる。
  • あるプロジェクトの評価後に処理を実行したい場合は、Project#afterEvaluate(Cloure) メソッドなどを使用すると良い。

gif-writer (GifWriter.js) version 0.9.3 をリリースしました

2013 年にリリースした GIF Encoder のライブラリ “GifWriter.js” を npm モジュールにして “gif-writer” としてリリースしました。 バージョンは 0.9.3 です。 *1

変更内容としては以下のとおりです。

  • 各ファイルをモジュールに変更。
  • パッケージに TypeScript の型定義ファイル (.d.ts ファイル) を含めるように。

npm モジュールに型定義を含める話

TypeScript 1.6 からモジュール名解決を npm モジュールから行う機能が入りました。

今回のリリースでは、npm モジュールに TypeScript の型定義ファイルを含めています。 よって、TypeScript で書いているプロジェクトで “gif-writer” モジュールを使う場合、単に以下のように import するだけで型定義が有効になります。

import * as gw from "gif-writer";

これは便利!!

*1:バージョン 0.9.0 と 0.9.1 はパッケージングに失敗したもので、0.9.2 と 0.9.3 の差分は README のみです。

「DroidKaigi 2016」 と 「関モバ #11」 で発表しました

「DroidKaigi 2016」 にて 『5 年続く 「はてなブックマーク」 アプリを継続開発する技術』 というタイトルで、また 「関モバ #11」 で 『Components­Recycler­Adapter — RecyclerView で複数の view type や複雑なデータ構造を扱う』 というタイトルで、それぞれ発表しました。

DroidKaigi 2016

5 年前に最初のリリースが行われたアプリを継続的に開発しており、そのための取り組みを共有しました。

要点をまとめると以下のようになります。

  • テストは Testing Support Library に頼っている。
    • 最近は Android の標準のテストフレームワークが充実してきている。
    • まだほとんどテストを書いていないというプロジェクトでは、まずは標準のテストフレームワークを使い始めると良いと思う。
    • 今回の DroidKaigi でも、テストに関する多数のセッションがあり、参考になる。
  • CI については、社内の Jenkins サーバー上でテストの実行やリリースパッケージ作成の自動化を行っている。
    • 各種タスクを Gradle タスクとして定義し、タスクの実行をシェルスクリプトで記述。
    • 最近は Jenkins Workflow plugin への移行を進めている。 (Groovy での DSL が書きやすいとか、リリースパッケージのアップロード処理の前に人の確認を挟みやすいなどの利点のため。)
  • Preview 版 (production flavor の preview) を定義して、開発中の機能を preview 版で有効に。
    • 長い期間の開発でも他ブランチとのコンフリクトをしづらく & チーム内配布で確認しやすく。
  • Annotations Support Library を活用。
  • 必要に応じてライブラリ的なものを作る。
    • 例えば 「関モバ #11」 で発表した ComponentsRecyclerAdapter など。

Preview 版について補足

開発中の機能を Preview 版 (product flavor の preview) でのみ有効にする、という話がスライド中に出てきます。 これについて、具体的な方法がわからないということで質問いただきました。 (src/preview/java 下に preview 版専用のソースコードを書くとすると面倒くさすぎるのでは? という。)

具体的には、ソースコードは全て src/main/java 下に置いて、

  • build.gradle ファイル内で buildConfigField を使って preview 版専用の機能を有効にするかどうかの値を持ったフィールドを BuildConfig に生成し、
  • Java のコード中で BuildConfig のフィールドを見て if で条件分岐して機能を有効にしたり無効にしたりする

という感じです。

関西モバイルアプリ研究会 #11


ComponentsRecyclerAdapter を用いて複数の view type や複雑なデータ構造を扱う

複数の item view や複数のデータセットによる複雑なデータ構造を扱いやすくする RecyclerView.Adapter のサブクラス ComponentsRecyclerAdapter の紹介です。

Gradle plugin を Plugin Portal で公開する (2016 年 2 月版)

背景 : Gradle プラグインの開発について

独自の Gradle プラグインの書き方は次のページに書かれています。

ここには公開の方法も書かれていて、次のように述べられています。

If you are publishing your plugin internally for use within your organization, you can publish it like any other code artifact. See the ivy and maven chapters on publishing artifacts.

If you are interested in publishing your plugin to be used by the wider Gradle community, you can publish it to the Gradle plugin portal. This site provides the ability to search for and gather information about plugins contributed by the Gradle community. See the instructions here on how to make your plugin available on this site.

39.5.2. Publishing your plugin (Chapter 39. Writing Custom Plugins)

広く使われうるプラグインを公開する場合には Gradle plugin portal が使えるようです。

Gradle Plugin Portal とは

Introducing The New Gradle Plugin Portal l Gradle」 によると、Plugin Portal は 2014 年 6 月ごろに公開されたようですね。 最初は Gradle plugin の検索のインターフェイスを提供するものだったようで、Maven リポジトリとしては Bintray が使われてたようです。

その当時の、プラグインを Plugin Portal で公開する方法は下記ページに書かれています。

現在では Gradle Plugin Portal 自身が Maven リポジトリも持つようになっていて、Gradle Plugin を Gradle Plugin Portal にアップロードすることで公開することができます。

Gradle Plugin Portal で公開されると何が嬉しいか?

まずひとつは、Gradle Plugin Portal で検索できること。

あとは、build.gradle でプラグインを適用する際に以下のように書けること。 (Gradle 2.1 以降。)

plugins {
    id 'com.gradle.plugin-publish' version '0.9.3'
}

Gradle Plugin Portal で公開されてないと以下のように書く必要があります。

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "com.gradle.publish:plugin-publish-plugin:0.9.3"
  }
}

他にもあるかもしれませんがとりあえずはそれぐらいでしょうか。

Gradle Plugin Portal への公開 (2016 年 2 月版)

公開方法は次のドキュメントに書かれています。

書かれているそのままですが、流れを紹介すると、

  1. Plugin Portal のアカウントを作成して、
  2. API key を生成して、
  3. API key を $HOME/.gradle/gradle.properties に書いて、
  4. Plugin Publishing Plugin を適用して build.gradle を書いて、
  5. ./gradlew publishPlugins でアップロード。

という感じですね。

普通にやれば普通にできるのですが、いくつかややこしいことがあるので書いておきます。

  • Gradle plugin ID は名前空間分けしたものにする必要がある。
  • 特に何も指定しなければ Artifact の group id の前には gradle.plugin. が追加される。
    • 上書きすることもできて、過去に Bintray にデプロイされたプラグインがあって、それの group id と artifact id を保ちたい場合は上書きする必要がある。
    • group id を上書きした場合は、セキュリティ上の理由で Gradle チームが手動で受け付けることになるので、group id を上書きしないことが推奨されている。
    • Gradle - How do I use the Plugin Publishing Plugin?」 の 「Full Example」 のコメント参照。

以上です!