Kotlin Android Extensionsを使ってみよーよ

2017年12月15日
区分
Android
報告者:
honda

この記事はactindi Advent Calendar 2017の15日目の記事です。

Androidエンジニアのhondaです。去年のアドベントカレンダーではKotlinのことを書きましたが今年もKotlinについて書きます。よろしくお願いします。

現在、Android版いこーよではKotlinを使っています。100%Kotlinです。 弊社でKotlinアプリを作ってみたい方はこちらとかこちらなどで応募お願いします。お願いします!

前置きはこのくらいにして、Android版いこーよではKotlin Android Extensionsを使っています。 なかなか、プロダクトでのKotlin Android Extensionsの採用を聞かないので、今回はKotlin Android Extensionsの素晴らしさをお伝えしたいと思います。

Kotlin Android Extensionsとはなにか?

Kotlin Android ExtensionsはKotlinの開発元であるjetbrains社が開発したAndroid開発を支援するためのプラグインです。 このプラグインを使うことによって、Androidアプリエンジニアの方ならおなじみの「findViewById」を使わずにコードからUIの要素を参照することが出来るようになります。

Kotlin Android Extensionsを導入してみよう!

Android Studio 3.0以上で新規プロジェクトを作成するとデフォルトで Kotlin Android Extensions使えるようになっています。素晴らしいですね。 既存プロジェクトの場合はbuild.gradle (Module: app)に以下を追加するだけです。

apply plugin: 'kotlin-android-extensions'

簡単ですね。

Kotlin Android Extensionsでコードを書いてみよう!

・Activity

簡単なアプリを作ってみましょう。 TextViewとButtonが配置された画面でTextViewには”Hello World!”と表示されています。 ButtonをタップするとTextViewの”Hello World!”の表示が”ハローワールド!”に切り替わります。 コードは以下の通りです。

Activityのレイアウトファイル
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="sample.samplekotlinandroidextensions.MainActivity">

    <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
              android:text="Hello World!"
              app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent"
              app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
    <Button android:id="@+id/button" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_marginTop="8dp"
            android:text="ハローワールド!" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" />

</android.support.constraint.ConstraintLayout>
Activityのソース
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        this.button.setOnClickListener {
            this.textView.text = "ハローワールド!"
        }
    }
}

これでfindViewByIdを使わずにtextViewに対して文言を設定することが出来ます。 レイアウトファイルでIDに設定した名前でコード上から参照出来ます。

・Fragment

次にFragmentで試してみましょう。 先程のActivityのSample同様にButtonをタップするとTextViewの内容が変わるものを作成します。

Fragmentのレイアウトファイル
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                             xmlns:app="http://schemas.android.com/apk/res-auto"
                                             xmlns:tools="http://schemas.android.com/tools"
                                             android:id="@+id/frameLayout"
                                             android:layout_width="match_parent" android:layout_height="match_parent"
                                             tools:context="sample.samplekotlinandroidextensions.SampleFragment">

    <TextView android:id="@+id/helloFragmentTextView" android:layout_width="wrap_content"
              android:layout_height="wrap_content" android:text="@string/hello_fragment"
              app:layout_constraintBottom_toBottomOf="parent"
              app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toTopOf="parent" />
    <Button android:id="@+id/helloFragmentButton" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp" android:layout_marginTop="8dp"
            android:text="@string/hello_fragment" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/helloFragmentTextView" />

</android.support.constraint.ConstraintLayout>
Fragmentのソース
class SampleFragment : Fragment() {

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_sample, container, false)
        view.helloFragmentButton.setOnClickListener {
            view.helloFragmentTextView.text = "ハロー フラグメント"
        }
        return view
    }
}

ここで注意しなければならないのがonCreateViewでのUIの要素の参照方法です。 上記のサンプルの用にonCreateViewではinflateしたviewに対してUIの参照を行わないといけません。

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?): View? {
    val view = inflater.inflate(R.layout.fragment_sample, container, false)
    this.helloFragmentButton.setOnClickListener {
        this.helloFragmentTextView.text = "ハロー フラグメント"
    }
    return view
}

上記の様にUIに参照しようとするとNullPointerExceptionが発生します。 onCreateView以外では下記の様にUIに参照出来ます。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    this.helloFragmentButton.setOnClickListener {
        this.helloFragmentTextView.text = "ハロー フラグメント"
    }
}

・RecyclerView

次にRecyclerViewで試してみましょう

RecyclerViewに表示するItemのレイアウトファイル
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView android:id="@+id/sampleRecyclerItemTextView" android:layout_width="wrap_content"
              android:layout_height="wrap_content" android:layout_marginBottom="8dp"
              android:layout_marginEnd="8dp" android:layout_marginStart="8dp"
              android:layout_marginTop="8dp"
              tools:text="TextView"
              app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
RecycleViewのAdapterコード
class SampleRecyclerViewAdapter(private val itemDataList: List<String>) :
        RecyclerView.Adapter<SampleRecyclerViewAdapter.ViewHolder>() {
    override fun getItemCount(): Int = itemDataList.count()

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder? {
        parent ?: return null
        return ViewHolder(LayoutInflater.from(parent.context).inflate(
                R.layout.view_sample_recycler_item, parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
        holder?.itemView?.sampleRecyclerItemTextView?.text = itemDataList[position]
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        init {
            itemView.setOnClickListener {
                Toast.makeText(
                        itemView.context, 
                        itemView.sampleRecyclerItemTextView.text, 
                        Toast.LENGTH_SHORT).show()
            }
        }
    }
}

RecycleViewのAdapterがListに入った文言をItemのレイアウトファイルを使ってリスト表示しています。 レイアウトのsampleRecyclerItemTextViewへはViewHolder内でitemView経由で参照します。

Kotlin Android Extensionsの挙動を理解しよう!

さて、とても便利なKotlin Android Extensionsですが、内部でどんなことをしているのでしょうか? Activityのサンプルコードを逆コンパイルしてJavaコードに変換して、どんなことをしているのか理解していきましょう。

public final class MainActivity extends AppCompatActivity
{

    public MainActivity()
    {
    }

    public void _$_clearFindViewByIdCache()
    {
        if(_$_findViewCache != null)
            _$_findViewCache.clear();
    }

    public View _$_findCachedViewById(int i)
    {
        if(_$_findViewCache == null)
            _$_findViewCache = new HashMap();
        View view1 = (View)_$_findViewCache.get(Integer.valueOf(i));
        View view = view1;
        if(view1 == null)
        {
            view = findViewById(i);
            _$_findViewCache.put(Integer.valueOf(i), view);
        }
        return view;
    }

    protected void onCreate(Bundle bundle)
    {
        super.onCreate(bundle);
        setContentView(0x7f09001b);
        ((Button)_$_findCachedViewById(R.id.button)).setOnClickListener((android.view.View.OnClickListener)new android.view.View.OnClickListener(this) {

            public final void onClick(View view)
            {
                view = (TextView)_$_findCachedViewById(R.id.textView);
                Intrinsics.checkExpressionValueIsNotNull(view, "this.textView");
                view.setText((CharSequence)"\u30CF\u30ED\u30FC\u30EF\u30FC\u30EB\u30C9\uFF01");
            }

            final MainActivity this$0;

            
            {
                this$0 = mainactivity;
                super();
            }
        }
);
    }

    private HashMap _$_findViewCache;
}

_$_findViewCache というハッシュマップのプロパティを用意して、Viewを管理しています。 参照時に_$_findViewCacheにViewインスタンスが無ければ、findeViewByIdをしてViewインスタンスを格納して参照し、すでに_$_findViewCacheにViewインスタンスがあれば、それを参照するという処理だということがわかりました。

注意点

Kotlin Android Extensions、とても便利ですが気をつけなければならない部分があります。

・誤って他の画面のUIへの参照するコードが書けてしまう。ビルドも通る。

Kotlin Android ExtensionsはData Bindingのようにコード生成しているのではなく、UIの要素をハッシュマップで管理しています。 誤って他の画面の要素を参照するコードを書いてしまっても、ビルドは正常に通ってしまうので気づけません。 実行時に_$_findCachedViewByIdでNullが返却されるため、NullPointerExceptionでクラッシュして、気づくことになります。 レイアウトファイルの要素のIDは他の画面の要素のIDが被ったり、どこの画面の要素なのかわからなくなることを防ぐために命名規則を設けておくと良いかもしれません。 例えば、「sampleActivityTextView」というような感じです。

・設計と相談

すでにデータバインディングを使っているような既存のプロジェクトではKotlin Android Extensionsを使っていくとなると設計を変えざるを得ないと思います。 使用する場合は設計と相談の上、ご使用ください。

まとめ

  • Kotlin Android Extensionsはjetbrains社製のAndroid開発支援プラグイン。
  • Kotlin Android Extensionsは導入が簡単!
  • Kotlin Android Extensionsを使うとfindViewByIdを使わずに簡潔にUIを参照するコードが書けるようになる!
  • FragmentやRecyclerViewのAdapterなどでも使える!けど、Activityでのやり方とは違うので注意。
  • Kotlin Android ExtensionsはUIの要素をViewインスタンスとしてハッシュマップで管理している。
  • 間違って他の画面のUIを参照するとクラッシュするので命名規則などで回避。
  • 使う時は設計と相談。

DroidKaigi2017に行ってきました。(2日目)

2017年03月17日
区分
android
報告者:
honda

こんにちは、hondaです。

DroidKaigi2017に行ってきた(1日目)

こちらではDroidKaigi2日目で聴講したセッションを簡単にまとめたいと思います。

2日目に見てきたセッション

ウェルカムトーク

DroidKaigi参加者には事前にアンケートを取っていてその結果がスライドで紹介されていました。 気になった項目を抜粋してみました。

・参加者の年齢

年齢 割合
20代  46%
30代  45%
40代  6%

私は30代の部類です。今後40代も増えてくるのでしょうか。

・事業分類

事業分類 割合
自社サービス  68.2%
受託開発  23.5%

受託開発ではなかな参加出来ないのでしょうか。弊社は自社サービスですが、参加させてもらえた事に感謝ですね。

・開発規模

開発規模 割合
3〜5名  36%
2名  25.8%
1名  24.7%
6〜9名  7.9%

1名〜2名での開発規模が5割行ってますね。職場でアプリエンジニアが少ないとこういったカンファレンスが本当にありがたいです。アプリ開発で6〜9名って逆に開発を回すのが大変そうなイメージなのですがどうやって回していっているのでしょう。

minSdkVersion

minSdkVersion 割合
4.1〜4.3  36%
4.0.x  33.7%
4.4.x  15.2%
5.0.x  12.4%

6,7はさすがにないですねー

DroidKaigi公式アプリ

  • 296PRs
  • 126issues
  • 68contributors

@konifarさん、ありがとうございました!アプリ助かりました。

Android ORMの選び方

Ormaが個人的に気になっていたので聴講しました。 なぜデータをローカルに保存するのか?どうやって保存するのか?そして、複数のORM(ActivitAndroid,greenDAO,Requery,SQLBrite & SQLDelight,Realm,Orma)を比較して、良し悪しを浮き彫りにするような内容でした。 プロダクトでRealmを使っている分、若干色眼鏡気味ではありますがはやり今のところはRealmなのかなと。 ただ、マイグレーションの煩雑さやequalToでの型安全の指摘は納得できるものでした。 Ormaは型安全に関しては解決されていて、Realmに比べて実装の軽さを感じました。 サービスや作るアプリの性質次第では採用してみたいですね。

未熟なチーム開発

弊社でもアプリチームは1年ちょっとでチームらしくなってきました。 さらに成熟させるヒントなどありそうだと思ったので聴講しました。 秩序づくりのところは大いに活かせそうでした。秩序とそのドキュメント化は面倒ですが大事ですね。 あとはよりテストブルな実装は大事。 実業務を進めながら新人教育をするのはなかなか難しい。。。

Kotlin + RxJava + Dagger2 + Orma + Retrofit で作るAndroidアプリ

より実践的な内容でいこーよアプリに活かせそうだったので聴講しました。 早口ではありましたが、DroidKaigiのアプリのコントリビュータをgithubから取得して表示するサンプルアプリを使っての説明でした。 とりあえず、RxJavaあたりから導入してみて、楽していきたい気持ちになりました。 Dagger2はテストとかも絡みそうなのでもうちょっと後から入れていこうかと。

4年続くアプリにおけるチーム開発

未熟なチーム開発と同じ理由で聴講しました。 UIファースト(UI駆動?)な開発スタイルでのバージョンごとでのアーキテクチャーの移り変わりと規約やドキュメントの整備やユーザインタビューでのアプリの質を高める話でした。 いこーよでもアーキテクチャーの見直しや規約、ドキュメントの整備は今まさに行っていることでとても参考になりました。 あと、目的別での部署を超えたチーム編成はとても興味深いものでした。

コマンドなしでぼくはAndroid開発できない話

コマンドで楽したい!という動機で聴講しました。

特に気に入ったコマンドを紹介します。

端末のワイヤレス接続

adb shell ip addr show wlan0 | grep 'inet ' | cut -d' ' -f6|cut -d/ -f1  ←端末のローカルIPを取り出す
adb tcpip 5555
adb connect "取り出したローカルIP":5555

条件としては端末とPCは同じWifi環境下にいる必要があります。 端末をUSBケーブルでPCとつなげて、上記コマンドを実行するとUSBケーブルが外れた状態でもコマンドを送ることが出来ます。

キーイベント送信

adb shell input keyevent KEYCODE_POWER ←電源キーを押す
adb shell input keyevent KEYCODE_SLEEP ←スクリーンをOFF

端末再起動

adb shell reboot 

Activity情報取得

adb shell dumpsys activity top

今表示しているActivitiyのクラスがわかる 今表示しているActivityのレイアウト構造がわかる!このアプリどういうレイアウトしてるんだろう?って思った時はレイアウト構造を丸裸に出来ます。

/data/data/packageName配管のファイルを見たい

adb shell run-as packageName

アプリの設定画面が見たい

shell am start -a android.settings.APPLICATION_DETAILS_SETTINGS -d package:"アプリのパッケージ名"

UIをカスタムしてあって、設定アプリが探しにくい端末だと使えます。

上記以外の使えるコマンドがここにまとめれてるそうです。

サンプルアプリをサクッと試したい

dryrun adbのコマンドではないですが、Githubに公開しているアプリをサクッと試すときにすごく重宝するツールです。

dryrun "GitHubリポジトリURL"

エンジニアが武器にするMaterial Design

まだMaterialDesignをものに出来ていないところがあるのでものにしたいために聴講しました。 ナノハさんではスピードも機能の1つと捉えてアニメーションもユーザに対するコンテンツの提供スピードを損ねないように実践されていて、とても興味深かったです。 その中でもアニメーションの改善で離脱率が半分に削減した話は特に興味深く登壇後やアフターパーティーでお話しを聞いてslackで弊社内のアプリチームに展開したところとても反響が大きかったです。 いこーよでも上手にMaterialDesignを活用して、ユーザに心地よくアプリを使っていただけるように改善していきたい。しよう。

テスト0から目指すクラッシュフリー率99%

テスト以外でも品質担保や不具合改善などの知見が得られそうだと思い聴講しました。 すごく当たり前のことですが、テストうんぬんの前のテスタブルにコードを書くというのが一番大切なんだなーと改めて感じました。 Activityからモデルを分離して、Interfaceを定義し、コンストラクタで依存するインスタンスを渡すようにするだけどすごくテスタブルでコードの見通しがよくなります。 あと、Fragment実装で一度は目にしたことがあるIllegalStateExceptionで悩んでる方はvultureを使うと幸せになれるようです。 最後にこの言葉は忘れないようにしよう。

まとめ

2日間ずっとお話しを聞き続けるっていうのは結構疲れました。 が、知見だらけの2日間はすごくエクサイティングで楽しかったです。 ずっと言い続けてますが、参加させてもらえたことに本当に感謝です。 この思いと知見をチームメンバーに伝えつつ、より良いプロダクトを作りたいですねー。 来年もぜひともDroidKaigiに参加したい! できれば、チームメンバー全員で参加して、お互い見れなかったセッションを現地で共有しあいたい!

以上、DroidKaigi2017に参加した感想でした。

DroidKaigi2017に行ってきました。(1日目)

2017年03月15日
区分
android
報告者:
honda

こんにちは、hondaです。

2017/03/09〜2017/03/10に開催されたDroidKaigi2017に参加してきました。 まずはチケット代8000円を負担してくれた会社と通常業務がある中、参加することにLGTMを出してくれた会社のメンバー、そして、帰宅が遅くなっても文句も言わず子供の面倒を見てくれた妻に感謝です。本当にありがとうございました! あとDroidKaigiを運営している有志の方々にも感謝しかありません。

今回ぼっち参加でしたが、登壇者の方に質問してもちゃんと話くれましたし、DroidKaigiの協賛企業のブースは面白かったし、おやつは充実してたし、ごはんは美味しかったし、とても楽しめました。

今回は見てきたSessionなどの感想をざっくり書いていきたいと思います。

アプリについて

DroidKaigi2017アプリ DroidKaigiアプリリポジトリ 毎朝、リポジトリをチェックしてチャンスがあればPRを出してみようかと思ってたのですが、コントリビュータの方々のPRのスピードに圧倒されるばかり参加できなかったのが少し残念でした。 内部実装を眺めては「ここを参考にしたらいこーよアプリの改善で使えるかも」と妄想していました。 DroidKaigi当日はこのアプリのおかげで見たかったセッションはすべて見ることが出来ました。 アプリ開発に当たられた@konifarさん、そしてコントリビュータの方々には感謝しかありません。

1日目に見てきたセッション

ウェルカムトーク

今回は800人近くのエンジニアの方が来場したそうです。 すごーい!

How to apply DDD to Android Application Development

特定のアーキテクチャーではなく、アーキテクチャーを採用するまでの考え方を知りたくて聴講しました。 わかる! ドメイン駆動設計~もちこちゃんの大冒険~は読んだことあるのですが、とても分かりやすい説明でした。 ドメインとモデルを明確に定義し、それを意識した考え方とそれにあった設計を実装していくことが大事!

リリース自動化と効率のよいリリースフローを求めて

弊社アプリチームはまだ人数も少なく、少しでも効率よく開発を進めることが重要なので、そのヒントを掴みたいので聴講しました。 現状、CIを入れて内部の動作確認などで自動化は進めていて、製品版リリースでは自動化はまだなのでその辺のTIPSが聞けたり、裏付けが出来てとても良かったです。 ただ、サービスの規模などの状況によっては自動化の「やりすぎ」も起こり得るのでそのへんは注意が必要だなと感じました。

Androidリアルタイム通信アプリ作成Tips

弊社いこーよアプリでもリアルタイムの技術はのちのち必要な場面出てきそうだなと感じているので聴講しました。 リアルタイム通信を実現するライブラリの紹介でした。 200枚のスライドだっため、説明がとても早口で聞いてるが少し大変でした。 結果としてはrealm mobile platformはすごいなの一言でした。 弊社アプリでもRealmを使っているので状況によってはrealm mobile platformをうまく使ってサービスを大きくしていきたいですね。

Data Bindingで開発を気持ちよくしよう

弊社アプリでは100%Kotlinで実装されているのですが、KotlinとData Bindingは相性が悪いという情報を聞いていたので採用を控えていました。 本セッションでその辺が触れられなかったので残念でしたが、たしかにActivityの見通しはよくなったのでタイミングを見て採用したいですね。

実践アニメーション

現状のいこーよアプリでは積極的にアニメーションを採用していません。 今後はユーザの使い心地のためにMaterialDesignの積極的に採用すると思い聴講しました。 実践的な説明もあり、聴講してよかったです。

オフラインファーストなアプリケーション開発

いこーよアプリではAndroid、iOS両方でRealmを採用しています。同じ時間帯で他のセッションも気になったのですが開発メンバーの強い要望もあり、こちらを聴講しました。 Androidリアルタイム通信アプリ作成Tipsでも出ていましたが、realm mobile platformはやはり素晴らしいテクノロジーですね。 他の聴講者も興味が強かったようで、聞いてきたセッションの中で一番質問数が多かった気がします。

How to remodel current testing environment

テストコードはアプリの動作保証をする上で重要ですが、アプリやサービスの規模、リソースの関係でなかなかガッツリと取り組めていないのが現状です。 なのでその辺をうまくバランスとりながら、保証を担保していく方法がないかと思い聴講しました。 テストをやる理由からBDDやTDDでのテスト方針的なことがとてもロジカルに語られていました。 当たり前なことではあると思いますが、全くテストがない状態から部分的にテストコードを拡充させていく際もテスト方針や設計は明確にして進めるべきだなと感じました。

Android Bikeを作ろう

個人的に今年はIOTやってみたいなーと思い聴講しました。 実際の実装した話を元にしたスライドだったので具体的な話が聞けて良かったです。 まずはうちにあるものでIOTを実感してみようと思わせてくれるセッションでした。

以上、DroidKaigi1日目の感想でした。

Android版いこーよがリリースされました

2017年02月16日
区分
Service
報告者:
honda

こんにちは、hondaです。

2017/02/13にAndroid版いこーよがリリースされました!

プレス記事
アプリ紹介ページ
Google Play

あと、現在口コミプレゼントキャンペーンを実施しています。
是非、iOS、Androidアプリから口コミしてみてください!

このアプリでお父さんお母さんと子どもたちが楽しくお出かけできれば嬉しいです。

今後もiOS版含めて、より良いアプリを目指しますのでよろしくお願いします!

新規のAndroidプロジェクトでKotlinを採用してみた結果

2016年12月22日
区分
advent-calender
報告者:
honda

この記事はアクトインディ Advent Calendar 2016 22日目の記事になります。

現在、アクトインディではAndroid開発にKotlinを採用しています。
今回はKotlinを採用した経緯と採用後の感想を簡単にまとめたいと思います。

なぜ、Kotlinを採用したか?

単純にKotlinで書きたかった(興味、期待があった)から

正直これが一番大きかった気がします。
チームポリシーとして明言しているわけではないのですが、今のチームメンバーは「作っている人が如何にモチベーションを保って開発出来るか?」を大切にしている感があります。
あと、新規アプリだったこともあり、Kotlinを導入する障壁はありませんでした。
Javaよりも気持ちよくコードが書けそうという期待もありました。

Javaと併用可能

万が一、「Kotlinはちょっと駄目かも・・・」となってもJavaにシフト出来るので気持ち的に安心感がありました。

Swiftと似ている

Androidアプリ開発が始まる前、iOSアプリ開発をしていました。
iOSアプリ開発も新規アプリ開発だったので、100%Swiftで開発を行っていました。
開発メンバーもSwift開発者がAndroid開発も兼任していたので、JavaよりもKotlinのほうが言語習得のコストは少なくなりそうだなと考えました。
あとはNull安全が大きな採用要素でした。

実際のところどうだったか?

学習コストは低かった(感覚値)

他の言語と比較してどうだった?という風には言えないのですが、感覚値では学習で手こずった感はあまりありませんでした。
SwiftからKotlinと言語が変わっての障壁もすくなかったように思います。

導入が楽

Android StudioのKotlin用のプラグインが入っているのでJavaで書かれたプロジェクトをKotlinのプロジェクトに簡単に変換出来ます。また、アクティビティクラスなど追加する場合でも最初からKotlinコードで生成することが出来ます。

書いてて楽しい

個人的に「書いてて楽しい」の助けになっている要素の一部分をすごく簡単ですがまとめました。

・データクラス

data class SampleDataClass(val sampleVal1: Int, val sampleVal2: String)

一行で書けます。

・拡張関数

fun String.sampleExtensionFun(): String {
    // 処理
}

既存のクラスに対して関数を生やすことが出来ます。

・型推論

val sampleVar = 0

いちいち、型定義しなくても変数定義時に値を設定することで型が決定します。
が、最近Kotlin採用しているプロダクトでビルド時間が長くなってる感があるので、型推論と型を明示的に宣言している場合にどれくらいビルド時間が違うのか検証してみたいですね。

・if式

class SampleClass {
    fun sampleFun(score: Int): String {
        return if (score >= 30) "合格" else "赤点"
    }
}

ifは式です。なので上記の様にif分岐の結果をreturnとして返すことが出来ます。

・when式

class SampleClass {
    fun sampleFun(score: Int): String {
        return when(score) {
            1 -> "one"
            2 -> "two"
            3 -> "three"
            else -> "other"
        }
    }
}

when(Javaでいうところのswitch)は式です。なので上記の様にwhereの結果をreturnとして返すことが出来ます。

・名前付き引数とデフォルト引数

fun sampleFun(sampleArg: Int = 0) {
    // 処理
}
sampleFun(sampleArg = 1)

関数/メソッド呼び出し時に引数を指定出来ることでコードが明確になります。
デフォルト値を設定することで引数を省略することも出来ます。

・ラムダ式

fun main(args: Array<String>) {    
    val sampleClass = SampleClass()
    sampleClass.sampleFun {
        print("callback")
    }
}

class SampleClass {
    fun sampleFun(callback: (() -> Unit)) {
        callback.invoke()
    }
}

ラムダを使って、簡単にコールバックなどを定義することが出来ます。

・Null安全

fun main(args: Array<String>) {    
    val sampleClass = SampleClass()
    sampleClass.sampleVar = "0"
    print(sampleClass.sampleVar1.toInt())  // エラー
    print(sampleClass.sampleVar1?.toInt()) // 安全呼び出し(?)をつけることで正常に動作します
}

class SampleClass {
    var sampleVar1: String? = null // Nullableな定義
    var sampleVar2: Int = 0 // NonNullな定義
}

Kotlinは言語的にNullPointerExceptionが発生しそうなところ変数参照などのコードで静的に警告が出て、Compileも出来ない様になっています。 これだけでだいぶ安心してコードを書くことが出来ます。 また、データ表現としてNonNullで定義するか?Nullableで定義するか?を意識出来る様になってきます。 メンバー間で議論することもあり、有意義なコードを書ける様になっていきます。

・エルビス演算子(?:)

class SampleClass {
    fun sampleFun() {
        val sampleVal = huge() ?: return
        // 処理
    }
}

エルビス演算子を使って、hoge()の戻り値がnullだった場合は上記の処理でreturnすることが出来ます。

・などなど・・・

Kotlinにはコードを早く楽しく書くための機能が言語的に備わっていたり、シンタックスが簡潔です。
なのでプロダクトに必要な本質的な実装に集中することが出来ます。

まとめ

足早なKotlinの紹介でしたが、1からAndroidアプリを作るときに開発言語でKotlinを使ってみることをおすすめします。
Kotlinはいいぞ。

最後に

KotlinでAndroidアプリ作りたい!アクトインディにジョインしていこーよを開発をしたい!方はこちらまで!

KotlinコードをJavaコードに変換してみた

2016年12月15日
区分
advent-calender
報告者:
honda

この記事はアクトインディ Advent Calendar 2016 15日目になります。
どうぞよろしくお願いします。

アクトインディではAndroidアプリの開発言語にKotlinを採用しています。

Kotlinではstaticというキーワードはありません。
staticなメソッドを実装するためにはcompanion objectを使って実装します。
また、companion objectはクラス内にシングルトンオブジェクトを生成する場合にも使用されます。

今回はKotlinで実装されたcompanion objectがJavaではどうような定義になるのか?どのように動作しているのかを理解するためにKotlinコードからJavaコードに変換してみたいと思います。

検証環境

Kotlin Compiler version 1.0.5-2
java version “1.8.0_92”
Jad v1.5.8g.

KotlinコードからJavaコードに変換するには?

①Kotlinコード(sample.kt)をCompileします。

$ kotlinc sample.kt -include-runtime -d sample.jar

②Compileして出来たsample.jarからclassファイルを取り出します。

$ jar -xvf sample.jar 

③classファイルからJavaコードに逆コンパイルします。
これでKotlinコードからJavaコードに変換できます。

$ jad -s java -d src -r sample.class

Kotlinサンプルコード

以下のKotlinサンプルコードをJavaコードに変換します。

class Sample {
    companion object {
        var sampleProperty: String = ""
        fun sampleMethod1() {
            this.sampleProperty = "sample method1"
        }
        fun sampleMethod2() {
            this.sampleProperty = "sample method2"
        }
    }
}

変換後のJavaコード

import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;

public final class Sample
{
    public static final class Companion
    {
        public final String getSampleProperty()
        {
            return Sample.sampleProperty;
        }

        public final void setSampleProperty(String <set-?>)
        {
            Intrinsics.checkParameterIsNotNull(<set-?>, "<set-?>");
            Sample.sampleProperty = <set-?>;
        }

        public final void sampleMethod1()
        {
            setSampleProperty("sample method1");
        }

        public final void sampleMethod2()
        {
            setSampleProperty("sample method2");
        }

        private Companion()
        {
        }

        public Companion(DefaultConstructorMarker $constructor_marker)
        {
            this();
        }
    }

    public Sample()
    {
    }

    private static String sampleProperty = "";
    public static final Companion Companion = new Companion(null);
}

Sampleクラス内にはstaticなインナークラスであるCompanionが定義されています。
また、Sampleクラスのprivateなクラス変数としてsamplePropertyとpublicなクラス変数としてCompanionが定義されています。
これで外部からメソッドへのアクセスはSample.Companion. sampleMethod1()という表現になり、samplePropertyへのアクセスはCompanion経由で行い、制御していることがわかります。

まとめ

Kotlin→Javaへの変換はそんなに難しいことではありません。
companion objectはJavaではstaticを駆使して実現されていました。
同じことを処理をするKotlinコードとJavaコードを見比べるとやはりJavaの方がコード量が多くなりがちでKotlinが如何に簡潔に書けるか実感することが出来ました。

最後に

KotlinでAndroidアプリ作りたい!アクトインディにジョインしていこーよを開発をしたい!方はこちらまで!

bitriseで起こった問題と解決方法

2016年12月01日
区分
advent-calender
報告者:
honda

この記事はアクトインディ Advent Calendar 2016 1日目になります。 どうぞよろしくお願いします。

アクトインディのAndroid開発ではCIにbitriseを使用しています。 今回はbitriseで起こった問題と解決方法をご紹介します。

発生した問題

2016/11/07頃から突然bitirseでビルドエラーが発生しました。 最初は実装ミスか?と思っていたのですが、ログ調査すると以下のようなログが出力されていました。

File /root/.android/repositories.cfg could not be loaded.
Preparing "Install Solver for ConstraintLayout 1.0.0-alpha8".
"Install Solver for ConstraintLayout 1.0.0-alpha8" ready.
Finishing "Install Solver for ConstraintLayout 1.0.0-alpha8"
Installing Solver for ConstraintLayout 1.0.0-alpha8 in /opt/android-sdk-linux/extras/m2repository/com/android/support/constraint/constraint-layout-solver/1.0.0-alpha8
"Install Solver for ConstraintLayout 1.0.0-alpha8" failed.
Preparing "Install ConstraintLayout for Android 1.0.0-alpha8".
"Install ConstraintLayout for Android 1.0.0-alpha8" ready.
Finishing "Install ConstraintLayout for Android 1.0.0-alpha8"
Installing ConstraintLayout for Android 1.0.0-alpha8 in /opt/android-sdk-linux/extras/m2repository/com/android/support/constraint/constraint-layout/1.0.0-alpha8
"Install ConstraintLayout for Android 1.0.0-alpha8" failed.

ビルドしていたプロジェクトではConstraintLayoutを使用していて、 そのConstraintLayoutのインストールに失敗しているようです。

ConstraintLayoutとは

Android開発で使用する新しく登場したUIレイアウトです。 プロジェクトが依存するライブラリとしてgradleで定義しないといけないものです。 詳しくはこちらを御覧ください。

解決方法

今回の問題を解決した方法が以下になります。

./gradlew dependencies || true ←※このコマンドを追加
./gradlew "--build-file" "build.gradle" "assembleDebug" "--stacktrace"

ビルドコマンドの前に上記の※のコマンドを追加するだけです。

解決方法の解説

ビルドエラーの原因はConstraintLayoutのインストールが失敗したことです。 が、実はConstraintLayoutのインストールは完了していました。 この現象はAndroid StudioからSDK Managerを立ち上げてインストールした場合でも発生します。 以下の画像が発生した時のメッセージです。

91f1b5ac095035d49ff8d19cd2ccc355.png (308.5 kB)

「Finish」を押して再度SDK Managerを立ち上げてインストール状況を確認するとインストールしたことになっています。

cba047f278c1949bd38a989088b4f8a2.png (27.6 kB)

なので

./gradlew dependencies

このコマンドで依存関係にあるライブラリを先にインストールされます。 インストールは完了していますが結果として異常終了となるので

./gradlew dependencies || true

とすることで正常終了となり、CIが止まることなくビルドすることが出来ます。

以上です。

アクトインディ Advent Calendar 2016

2016年10月28日
区分
advent-calender
報告者:
honda

こんにちは honda です。
今年もアドベントカレンダーの季節が近づいてきました。

今年もアクトインディではアドベントカレンダーやります。
アクトインディ Advent Calendar 2016

参加したい方がいらっしゃいましたら今すぐ入社
お待ちしています!

去年のアクトインディ Advent Calendar

iOS版いこーよがリリースされました

2016年07月28日
区分
Service
報告者:
honda

はじめまして、hondaです。

本日、iOS版いこーよ1.0.0がリリースされました!

プレス記事
アプリ紹介ページ
app store

入社して半年以上過ぎましたがずっとコレを作ってました。
このアプリでお父さんお母さんと子どもたちが楽しくお出かけできれば嬉しいです。

現在、Android版いこーよを鋭意作成中です。
ご興味のある方はこちらまで!

Android版いこーよでの開発言語ではKotlinを採用予定です。
一緒にKotlinやらないか?

技師部隊からの
お知らせ

【求人】エンジニア募集しています。

本頁の来客数
八十七万千百七十六名以上(計測停止中)

メンバー一覧

アクトインディ技師部隊員名簿

アクトインディ技師部元隊員

アクトインディへ

カテゴリー

アクトインディ

aaaa