頭のメモリが少ない

プログラミングと日々の生活

【初心者歓迎】Kotlin開発Tech Talks 参加レポート

f:id:chick-developer:20170713124133p:plain

【初心者歓迎】Kotlin開発Tech Talks 参加レポート

概要

Google I/Oにて、ついにKotlinがAndroidの正式な開発言語として追加されました。今後より盛り上がっていくと考えられるAndroidでのKotlin利用に関する勉強会を開催します。

この勉強会で登壇するスピーカーが所属する5社は、Kotlinの導入度合いも会社の規模や成長フェーズもバラバラですが、いずれも「ヒトをつなぐ」自社サービスを展開しています。それぞれのサービスでのAndroid開発におけるKotlin利用について、その取り組みやノウハウなどを発表します。

【日時】7月12日(水)19:30~22:00
【場所】スタートアップベースキャンプ オープンスペース
【住所】東京都渋谷区円山町28-1 渋谷道玄坂スカイビル8F
【費用】¥1,000 ※飲み物と軽食をご用意します
【URL】https://connpass.com/event/59898/

■ほぼ毎週リリースアプリのKotlin書き換えの道のり

  • 株式会社葵 近藤 和宏

    Kotlin事情

  • アオイゼミ(iOS, Android, Web版)
  • 去年夏頃よりリニューアル計画
  • 10月には言語や内部仕様、リニューアル内容の決定
  • Kotlinの熱量と書きやすさ採用などを鑑み採用
  • 12月から開発開始
  • 12月末一部KotlinとJavaが併存している状態でリリース

  • ほぼ毎週リリース

  • 1月〜4月までの間、Kotlin Onlyで書き直しているブランチと試作用の2つのブランチを使って開発していた
  • 約20万行以上のコードを書き直し

メリット編

  • null safe
  • 公式サポート
  • 採用にインパクトある

色々あったよ編

  • Android 65K
  • Null safeなKotlinでもnullpointerexceptionは発生する “?"でマーク、コード側で防御、NPEが入り込まないように設計
  • 動画が再生されない事件 一部ユーザーが動画がされない報告が続出
    CDNがHTTP2.0に対応TLS1.1.以下を切り捨てていた
    急なトラブル対応にはまだJavaのコードを読む必要がある

■開発 4 年目のアプリへの Kotlin 導入

speakerdeck.com

ChatWork株式会社 宮下 竜大郎(チームリーダー)
国産のビジネスチャットサービス
最初はTitanium製
Android版(2014年12月からネイティブ)

なぜKotlinを導入するのか?

急ぐ必要はあるのか?
-> 急がなければならない理由はない
あくまでも公式サポート言語が増えただけ

  • それでもKotlinを導入する理由
    • エンジニア的な楽しさ
    • エンドユーザーのメリットにもなる言語機能

      • ボイラーテンプレートを少なく出来る
        • Lambda式
          java8のラムダ式よりもカンタンに書ける(スッキリ書ける)
    • 脳内の処理イメージをコードに反映しやすい
      スコープ関数
      拡張関数
      if, try, when

チームにどう効いていくるか

  • ユースケースを表したコードになりやすい
  • 無駄が無いのでコードレビューがやりやすい、確認と指摘の漏れが少なくなる

気をつけること

  • 7200メソッドほど増える
  • 65Kメソッド制限にぶつかる可能性が高まる
  • kaptが生成するコードに問題があることもある(たまにドハマリ注意)

どう導入を進めていくか

  • すべてのコードをKotlinにするのは先が遠い
  • 部分的にKotlin化していくのがよい、Chatworksでは下記の手順で行った
    • はじめに、シンプルなDialogFragmentをKotlin化して、誰でもJavaのコードがイメージできるように
    • 次に既存のコードを一つKotlin化
    • 新規機能コードをKotlinで追加

課題

  • 何をどこまでKotlinで書くかは定まっていない
  • 非同期処理でasync/awaitを使っていくとか
  • 全員が足並みを揃えられるかどうか
  • チームにとって、ひいてはユーザーの価値になるか
  • Kotlinのコードを負債にしてはいけない

■1年ぶりにやりなおすKotlinアプリ開発

speakerdeck.com

株式会社m-gram 北原 和也

エムグラム正確診断
もうすぐリリース予定
AndroidはKotlinで開発

思ったこと

Annotationプロセッサーを使用しているライブラリをするっと使えるようになっている
Kotlin記事も増えている(一年で倍以上増えている)

一年前のlintの結果がイマイチだった
クラスによってはwarning出たり出なかったりしてまだ使えない印象

Kotlinと一緒に使ったライブ

  • Retrofit2, OkHttp3, rxjava
  • Parceler
    -> Parcelableの実装がクソ楽になる
  • Dart
  • android-state
    -> icepickのkotlin版

■テストから始めるKotlin導入

speakerdeck.com

株式会社エウレカ 二川 隆浩
futabooo

  • Pairsの歴史
    大昔のPairsAndroid
    Scala(90%)
  • 現在
    java 6割
    kotlin 2割

  • 最近リニューアルした、Global版
    Kotlin, ProtcolBuffer

十分に発達したバグはどれも、機能と見分けがつかない
-> 主要な機能でバグが発生

  • テストが書いてあれば防げた
    -> しかし、テストが書ける状態ではなかった

テストが書きやす構成にする
レイヤーをはっきり分ける
詳細は、Activityを改善した話のスライド見て!

■KotlinとJavaの相互運用

コネヒト株式会社 富田 健二
@tommykw

ママリのkotlin導入

  • javaだと冗長、複雑になるコードもすっきり可読性が上がる
  • Swift使いのiOSエンジニアにもなじみやすい文法
  • javaぬるぽを駆逐
  • デメリットが少なかった

導入方針

Kotlin導入結果

10月からスタートして54%導入完了
2割くらいコード行数が減った
簡潔に記述できるようになった

KotlinとJavaの相互運用

難しい点

  • PlatformType
    T!を表す
    IDEAでPlatformTypeだと警告を表示してくれる場合がある
    java側で@nullable, @nonnullを定義する

  • MappedType
    javaとkotlinの型テーブル

  • デフォルトfinal class
    kotlinのクラスはデフォルトfinal
    javaとは異なり、継承する場合にopenをつける

  • 検査例外について
    Kotlin側で@Throwを定義する
    IDEA側で警告を定義する

  • ドキュメント
    コメントの書式が統一出来る
    コメントがまとまっていてコードが読みやすい
    コメントを書くことによって読みやすいコードを意識できる

    • KDoc
      マークダウン記法
    • dokka(htmlとして吐き出してくれる)
      共存したプロジェクトにも対応

メリット

生産性の高さ
コレクションの操作が直感的で楽
学習コストもそれほど高くない
IDEAの補完が強力

デメリット

既存である場合は相互関係の辛さがある

ごちそうさまでした🍕

f:id:chick-developer:20170713123750j:plain

TextViewのAutolink問題

TextViewのAutolink問題

lolipop以上でTextViewでandroid:autoLink="web"と設定すると、余計な文字列までリンクになってしまう場合があります。 例えば、こんな文字列を入れた場合には"Link http://google.com but dont link abra.kadabra"、 http://google.comだけをリンクとしてほしいに、dkra.kadaraもリンクになってしまいます。

というわけで回避方法です。

gist.github.com

参考

https://stackoverflow.com/questions/30528825/android-autolink-is-too-aggressive

# libncurses.so.5: cannot open shared object file: No such file or directory

libncurses.so.5: cannot open shared object file: No such file or directory

Circle CIでAndroidのビルド環境を構築したときにビルドエラーが起きたのでその時のメモです。

> Building 93% > :android:compileDevelopmentDebugRenderscript:android:compileDevelopmentDebugRenderscript
/usr/local/android-sdk-linux/build-tools/23.0.3/llvm-rs-cc: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

:android:compileDevelopmentDebugRenderscript FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':android:compileDevelopmentDebugRenderscript'.
> com.android.ide.common.process.ProcessException: Error while executing '/usr/local/android-sdk-linux/build-tools/23.0.3/llvm-rs-cc' with arguments {-O 3 -I /usr/local/android-sdk-linux/build-tools/23.0.3/renderscript/include/ -I /usr/local/android-sdk-linux/build-tools/23.0.3/renderscript/clang-include/ -p /home/ubuntu/clear_android/android/build/generated/source/rs/development/debug -o /home/ubuntu/clear_android/android/build/generated/res/rs/development/debug/raw -target-api 14 /home/ubuntu/clear_android/android/src/main/rs/Sharpen.rs /home/ubuntu/clear_android/android/src/main/rs/TopHatTransform.rs}

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 41.333 secs

sshで覗いてみてもファイルは存在するんですが、どうやら32bitの方のパッケージをインストールせんと駄目みたいです。

対応策

dependencies:
  pre:
    - sudo apt-get install lib32ncurses5

参考

adb: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
http://stackoverflow.com/questions/17092093/adb-error-while-loading-shared-libraries-libncurses-so-5-cannot-open-shared-o

Cursor用Bindingライブラリ"Curson"

Cursor操作をチョットだけ楽にする

Androidを開発していたら必ずと言っていいほどCursorが出てきますね。 例えば、DBにqueryしてCursorのインスタンスを取得して、そのcursorをcursor.moveToFirstでポジションを移動して、getColumnIndexでindexを取得してからgetString, getInt等で値を取得して、最後にはちゃんとcursorをcloseしてなどなど…。 こんな感じでしょうか。

Cursor cursor = null;
try {
    cursor = getCursor(...);
    if (cursor != null && cursor.moveToFirst()) {
        int columnIndex = cursor.getColumnIndex(MediaStore.Images.Media._ID);
        Long id = cursor.getLong(fieldIndex);
        //以下、略...
    }
} finally {
    if (cursor != null) {
        cursor.close();
    }
}

毎度毎度同じコード書くのは辛いので、このようなボイラーテンプレートなコードは自動生成するに限ります。 という訳で、Cursorを操作をチョットだけ楽にするために、Cursor<->Entity変換を行うライブラリCursonを作りました。

How to install ?

build.gradleに下記を記述してください

buildscript {
  repositories {
    mavenCentral()
   }
  dependencies {
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'com.github.dkajiwara:curson:1.0.2'
    apt 'com.github.dkajiwara:curson-compiler:1.0.2'
}

How to use?

使い方は簡単です、Cursorに対応するEntityオブジェクトを作成してください。 次に、作成したEntityオブジェクトとCursorのインスタンスCursor.fromCursor()を引数に渡してください。 具体的なコードを書いたほうが分かりやすいと思いますので、下記に端末内の画像取得する場合の例を載せています。

public class Image {
    @CursorRow(ImageColumns.BUCKET_DISPLAY_NAME/*カラム名*/)
    String bucketDisplayName;

    @CursorRow(ImageColumns.BUCKET_ID)
    String bucketId;
}

public class ImageManager {
    public List<Image> getImages() {
        Cursor cursor = query...//image取得する
        return Curson.fromCursor(cursor, Image.class);
    }

    public Image getImage() {
        Cursor cursor = query...//image取得する
        return Curson.fromCursor(
                cursor,
                Image.class /*作成したEntityクラスを指定する*/,
                0 /*cursorのpositionを指定する*/);
    }
}

上記の例では、Cursorのcloseを呼んでいません。これはCursor.fromCursor()ではデフォルトでcursor.close()を呼んでいます。なので、変換時にCursorのcloseをしたくない場合は、メソッドCurson#fromCursor(Cursor, Class, int, boolean)を使い、引数(boolean)にfalseを指定してください。 また、CursorRowアノテーションは下記の型をサポートしています。

  • byte[]
  • int / Integer
  • double / Double
  • float / Float
  • short / Short
  • String

Other

Cursorオブジェクトへの変換としてCursor.toCursor()メソッドをサポートしています。EntityオブジェクトからCursorオブジェクトへの変換なのであまり使う機会は少ないかと思いますが、UnitTest時のContentProviderのモックで使用するとチョットだけ楽になります。

public class ExContentProvider extends ContentProvider {
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        List<Sample> list = new ArrayList<>();
        list.add(new Sample(1, 100000000L, "description1", "title1", 1));
        list.add(new Sample(2, 100000001L, "description2", "title2", 1));
        list.add(new Sample(3, 100000002L, "description3", "title3", 0));
        list.add(new Sample(4, 100000003L, "description4", "title4", 1));
        return Curson.toCursor(list, Sample.class);
    }
}

End

今回、ライブラリの使い方のみで中身の実装に関しては触れていませんが、雑に説明するとコンパイル時にアノテーションプロセッシングを使用してEntityクラスから下記のようなクラスを生成しています。実行時には内部でこのクラスを呼び出すことでCursor<->Entityオブジェクトの変換を行う仕組みとなっています。

public class Sample$$CursonEntityBinder implements CursorBinder<Sample> {
  @Override
  public Sample bind(Cursor cursor) {
    final int idIndex = cursor.getColumnIndex("_id");

    Sample sample = new Sample();
    sample.id = cursor.getInt(idIndex);

    return sample;
  }
  //いろいろ省略...
}

Reference

github.com

※本記事は以前qiitaに投稿したものと同様の内容です

qiita.com

ContentProviderのauthorityの取得方法

ContentProviderのauthorityの取得方法

ContentProviderを使用する場合に、authorityはmanifestに書いてあるauthorityをコピペで

public static final String AUTHORITY = "com.hogehoge.jp";

定数として定義するのが一般的なやり方だと思います。

AndroidManifest.xmlのauthorityはContextとContentProviderの実装クラスさえ分かれば、コードからでも取ること可能です。

    /**
     * Get ContentProvider authority from AndroidManifest.xml.
     * 
     * @param context context
     * @param providerClass providerClass
     * @return authority
     */
    public static String getAuthority(Context context, Class< ? extends ContentProvider> providerClass){
        PackageManager manager = context.getApplicationContext().getPackageManager();
        try {
            ProviderInfo providerInfo = manager.getProviderInfo(
                    new ComponentName(context, providerClass), PackageManager.GET_META_DATA);
            return providerInfo.authority;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }

他にもAndroidManifest.xmlの内容はPackageManagerから色々と取得出来ます。

■参考

http://developer.android.com/reference/android/content/pm/PackageManager.html

※本記事は以前qiitaに投稿したものと同様の内容です

qiita.com