AnkoでLayout XMLを殺した
そういえば先月頭にRettyさんでAnkoについてLTしたのでスライド置いておきますね。
speakerdeck.com
あ、ちなみに本稿は特にAnkoの解説は行ってないポエムです。
Ankoとは
Kotlin製DSL。Viewを書くのに利用します。
XMLでView書いた場合の以下のような問題を感じてJetBrainsのエンジニアが開発したのがAnko。
- 型安全じゃないし
- null安全じゃないし
- 似たようなことを毎回かかなきゃいけないし
- XMLをパースする分パフォーマンス悪いし
- コードの再利用できないし
こんな感じでViewを書けます
verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } }
Javaで書いた場合との差が冒頭に貼ったスライドに書いてあるので、興味ある人は見てください。
なにこれ魔法!?🎩
Kotlinの拡張関数とプロパティを利用してます。 拡張関数を引数にとる関数がどうたらこうたら。これも冒頭のスライドに書いてあるので興味のある人は見てください。
XML全部殺してフルAnkoにした
mints-Android-LayeredArchitecture 暇な時に開発してるアプリのViewをすべてAnkoで書いてみました。 (まだ改善できそうなところは多い)
(追記) HEADではもう、LayoutXMLとDatabindingを利用する形に書き換えてしまった。
ActivityやFragmentとは別のファイルにViewをまとめる
AnkoでViewを書いてる部分
class MainUi : AnkoComponent<MainActivity> { private lateinit var viewPager: ViewPager override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) { verticalLayout { val tabLayout = tabLayout { tabMode = TabLayout.MODE_SCROLLABLE tabGravity = TabLayout.GRAVITY_FILL setSelectedTabIndicatorColor(getColor(ctx, R.color.colorPrimary)) }.lparams(width = matchParent, height = wrapContent) viewPager = viewPager { id = 1 offscreenPageLimit = 2 }.lparams(width = matchParent, height = matchParent) tabLayout.setupWithViewPager(viewPager) } } fun showPages(fm: FragmentManager, pages: List<PageAdapter.Page>) { viewPager.adapter = PageAdapter(fm, pages) } }
利用側
class MainActivity: AppCompatActivity() { private val ui = MainUi() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ui.setContentView(this) } }
XMLを書かなくていいのは結構いいですね。今回は一人での実装だったのでただの予想ですが、コードレビューでXMLを読むのが辛いのですが、Ankoなら多少軽減されるかも。 もっと細かい単位でAnkoComponentを実装していけばコードのreuseができそう。
MacBook Pro 13-inch with TouchBar
仕事用のMacBookPro 13inch TouchBar付きが届いたのでその感想。
tl;dr
- 薄い軽い、持ち運ぶ気になった
- ストロークの浅いキーボードなかなか打ちやすいぞ
- TouchIDが最高なことはみんなしってるだろ?
- TouchBarに最適化されたソフトウェア結構いいかも
- TouchBarカスタマイズして便利なショートカットつくれるぞ!
esc
は常に左上にあるから困らねーぞ!- functionキーは
fn
押せばでてくるぞ! - キーボードだとかモニタだとかじゃらじゃらつけないんでUSB Type-Cだけなのはこまらんぞ!
ボディ
前モデルに比べて軽く、薄くなった。
多少のさだが、結構体感できるほどに違う。これは正義。
今後は通勤時にも持ち運びしてもいいかなと思う。
キーボード
MacBookとおなじようなキーボード。
慣れてみるとこれがなかなかいい。
扱い始めて数十分後にはもとのキーボードのストロークの深さが気に食わなくなった。
TouchID
TouchIDが最高なことなんてiPhone/iPadつかってる人にはわかりきったことだよね。
TouchBar
よい。
BetterTouchToolsで独自のボタンをつくれるので、AndroidStudioでよく使うコマンドを割り当てたりした。
ソフトウェアがTouchBarに対応している場合もなかなかおもしろい。
たとえばSafariであれば、favorites画面でbookmark barに設置しているものがTouchBarに出ていたり
メディアの再生を(safariがフォアグラウンドになくても)TouchBarで操作できたり
これ地味に便利ね。safariでなにかメディアを再生してるときに話しかけられた場合、safariにきりかえて...タブひらいて...ってやらなきゃいけなかったのが面倒だったので(面倒だったのでそもそも止めないことも多かった)
Escapeない
気にならない。
物理ボタンではなくなったが左上には常に表示されている。
Functionキーない
以前よりワンストローク増えてしまうが、fn
を押せばTouchBarにFunctionキーが表示されるので困らない。
コマンド類は前述したようにTouchBarのカスタマイズで最適化できる。
TrackPad
カーソルスピード高く設定して使ってるので広くなっても特に恩恵はない。
誤タッチを気にしてるひともいるみたいだけど、ちゃんとパームリジェクション効くから落ち着けって。
とはいうものの、右下クリックをSecondaryClickに割り当ててると困る。
右下に手のひらが置かれてる状態でクリックするとSecondaryClickとして検出される。
諦めて2FingerClickでSecondaryClickにした。は〜。
USB Type-Cしかない
困らない。
外部キーボード??外部モニタ??キーボードもモニタもMBPにすでについてますよ!!
検証端末の接続もこまらない。Nexus5XはもともとUSB Type-Cです。
iPhoneはケーブルかわないとですね。は〜だるい。
電源
MagSafeはその名の通り安全で最高だった。
ただUSB Type-Cにもいいところはある。
- 右からでも左からでも給電できる
- モバイルバッテリーから給電できる
モバイルバッテリーから給電できるの最高でしょう?
総評
満足
Kotlin拡張関数は怖くない、その実態を紐解く。
Kotlin未経験Javaエンジニアに拡張関数を説明すると「怖い」と言われることがあります。 おそらく、クラスを継承する事なく拡張できることが黒魔術的に見えるがゆえの感想なのではないでしょうか。 本稿では拡張関数の実態を知ることで、拡張関数をもっと身近に感じてもらいたいと思います。
拡張関数とはなにか
拡張関数はクラスを継承せずに機能を追加するための機能です。
下記のようにfun Type.functionName(...)
とすることでそのType
に新たに関数を追加する事ができます。
fun String.println() { println(this) }
拡張関数は通常の関数と同じように呼び出すことが可能です。
"Hello World".println() // out: Hello World
拡張関数はどのように実現されているのか
StringExtension.kt
ファイルに実装した下記コードをコンパイルすると、StringExtensionKt.class
が生成されます。
fun String.println() { println(this) }
ではStringExtensionKt.class
をJavaへデコンパイルしてみましょう。
public final class StringExtensionKt { public static final void println(@NotNull String $receiver) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); System.out.println($receiver); } }
ファイル名Kt
クラスに拡張関数と同名のstaticメソッドが生成されていることから、拡張関数がただのstaticメソッドであることがわかると思います。
ここまで分かってしまえば何も怖いものはありませんね。
private拡張関数
以下のようなFooクラスがあるとします。
class Foo {
}
これに対してFooExtensionファイルにprivate拡張関数を実装しましょう。
private fun Foo.doSomething() { } class Foo { init { doSomething() } }
さて、ここで一つ疑問が出てきました。先程の例と同様であれば以下のようにFooExtensionKtクラスにprivate staticメソッドが生成されるはずです。 となるとFooExtensionのprivate staticメソッドをFooから呼び出せる事はおかしいですね。
public final class FooExtensionKt { private static final void doSomething(@NotNull Foo $receiver) { } }
実際にコンパイルしてみると以下のようになりました。
public final class FooExtensionKt { private static final void doSomething(@NotNull Foo $receiver) { } public static final void access$doSomething(@NotNull Foo $receiver) { doSomething($receiver); } }
doSomethingへアクセスするためのブリッジメソッドが生成されています。 ここでFoo.classもデコンパイルしてみましょう。
public final class Foo { public Foo() { FooExtensionKt.access$doSomething(this); } }
なんと、private拡張関数を呼び出していた部分はブリッジメソッドの呼び出しに変わっています。 どうやらコンパイル時にブリッジメソッドが生成され、Kotlinからのprivate拡張関数呼び出しはこのブリッジメソッド呼び出しに置き換えられるようです。
では、もしFooがJavaクラスだった場合はどうでしょうか。 この場合そのprivateメソッドはまったくの役立たずになります。Javaからはただのstaticメソッドとしか見えない上にprivateであればアクセスする方法が無いからです。 コンパイル時に生成されるブリッジメソッドへのアクセスもコンパイル前では不可能です。
おわりに
どうでもいいですが、今回はバイトコードもそこそこ読みました。
普段全く読むことがないのでバイトコードを読む力はあまりないですが、これを機にちょくちょく見ていこうかなと思います。
Android StudioであればTools -> Kotlin -> Show Kotlin Bytecode
で即座に読むことが出来ますので、是非活用してみてください。
eurekaに入社して半年たった
eurekaに入ってから半年が経過。 pairsのAndroidアプリを開発してる。 どういう会社だとか書くのは面倒なのでやったことだけ軽くリストアップ。
やったこと
- Orma導入
- Kotlin導入
- Dagger導入
- Architecture改善
Orma導入
SQLiteいじるのにActiveAndroidを使っていた。ActiveAndroidは遅いし開発とまってるし、使い続けてもメリットは無いなということでOrmaへの移行を行った。
Ormaは速い。
クエリビルダはカラム単位でメソッドを自動生成してくれる。文字列を使ってカラム名を指定とかする必要がない。Typoによるバグは出ないし、テーブル定義でカラム名が変更されたらクエリビルダにも変更が加わるのでコンパイルが通らなくなる。
マイグレーションはリネーム以外は自動。
素晴らしいですね。
pairsはテーブル数多いしカラムも多かったのでそれなりに大変だった。
まったく根拠のない推測だけど、いまどきのネイティブアプリはDBそんなに複雑なものは無いと思うので大体の場合は大変ではないともう。
Kotlin導入
個人で一番書いている言語。仕事でも書きたいなと思ってた。
個人的にKotlinで気に入っている機能はnull安全と関数リテラル。
NPEが起こりうるコード書けない仕様になってる。
関数を変数に代入したり関数に渡したりできる。
チームに「Kotlinどう?」って聞くと、拒否反応は出なかった。
Kotlin勉強会を開いたり社内LT大会でKotlinを布教した。
フルKotlinにする予定は今のところない。ViewのみJavaで書いて、PresenterやModelはKotlinで書いてる。
追記: 結局Kotlin最高だなとなってそういった制限は撤廃された。
Dagger導入
後述するArchitectureの改善するにあたってDaggerがないと厳しい感じだったので導入。
Architecture改善
API周り・DB周りのインターフェースがstaticメソッドで、テストが書きにくかった。
class FooDao { public static List<Foo> findAll() { return App.getOrma().selectFromFoo().toList(); } public static void save(Foo foo) { App.getOrma.insertIntoFoo(foo); } }
class FooClient { public static Observable<Foo> fetch() { return Client.getRetrofit() .create(FooService.class) .fetch() .doOnNext(new Action1() { public void call(Foo foo) { FooDao.save(foo); } }); } }
リクエストの度にRetrofit#createをコールするので処理に無駄が多かった。
DB周りはOrma移行のときに改善するチャンスがあったが、移行と改善を並行させたら絶対コケると思ったのでインターフェースは全く変えてなかった。
依存する物(Service/Orma)はすべてコンストラクタで受け取るように変更。
ClientはRetrofitが生成したServiceを叩くだけのクラスに。
DaoはOrmaを叩くだけのクラスに。
RepositoryはClientで通信してDaoでINSERTする、Daoを叩いてSELECTする。
UseCaseは必要なRepositoryを使ってビジネスロジックをもつ。
PresenterはUseCaseとViewの架け橋的存在。
といった感じに。
他
最近は施策もやった。
あとはちょこちょこリファクタリングしたり、勉強会ひらいたり、AssociateEngineerやインターン生にレクチャーしたり。
エンジニア向けニュースアプリをつくってます。
mintsについて
エンジニア向けのニュースアプリつくってます。下記サービスのクライアントです(menthas作者様に感謝🙏🏻)。
テクノロジーに関するニュースを扱っているサービスはたくさんありますが、よりエンジニア向けに特化された記事というものはないですよね。大体ガジェットやテック企業のニュースになってしまいます。 その点、menthasはライブラリやフレームワークの話などを集めてきてくれるのでとても重宝しています。
開発
開発は2016年3月からやっていました。 ある程度完成したところで興味が失せてしまって放置していましたが、8月に公開しました。 そこからまた興味が失せてしまっていましたが、1ヶ月程前に設計を変えて実装しなおすことに決め、アップデートを行いました。
この先考えてること
- JobSchedulerをつかってみたいので、バックグラウンドで記事を先読んでオフラインでも記事を読めるようにしようかな
- MVVMもやりたいから差し替えようかな
あまり文章を書くのは得意ではないので、使用した言語とアーキテクチャとライブラリをざらっとかいておきます。
言語
Kotlin
アーキテクチャ
CreanArchitecture + MVP
ライブラリ
Rx
RxJava
RxAndroid
RxBinding
ORM
Orma
Network
OkHttp3
Retrofit2
DI
Dagger2
Image
Debug
Timber
Stetho
View
AdvancedRecyclerView
ParallaxViewPager
Resoure
Koresource
Json
Gson
Parcelable
PaperParcel
Test
Robolectric
Knit
MockWebServer
KMockito
未経験者がエンジニアとして働く選択肢
未経験だけどエンジニアになりたい/なったという声をいくつか聞いたので自分の例を書いてみる。
自己紹介
- 2014年3月 某大学経済学部卒業
- 2014年9月 受託開発会社に入社
- 2016年6月 eurekaに入社
適当に大学生活を送っていたので、なんの志もなかった。やれることと言えば営業くらいだろうと思って就職活動をするも、なんの魅力も見いだせなかったので就職先を得ないまま卒業。
その後急にエンジニアになることを決めて受託開発会社に入社。
PSVitaゲーム開発、Webアプリ、Androidアプリ開発を経て受託開発から抜けだしてサービス開発をしたいと思うようになりeurekaに転職。pairsのAndroidアプリのデータベースとかアプリの設計を改善してる。
エンジニアになろうと決めた
プログラミングには元から興味があった。
某MMO RPGのサーバーをローカルでエミュレーションしたり、Minecraftのサーバーを建てて友人同士でワイワイやってたのが理由。
エンジニアはブラックとは聞いていたが、興味のない事を一生だらだらやるより良いだろうと思った。ブラックでも技術さえ獲得すればより良い環境に行くこともできるだろうしとも思った。
企業選び
有名なプロダクトつくってるような所は未経験の募集なんてやってないので諦めましょう。
SIer(業務システムの開発を請け負う企業)とか受託でゲームとかアプリ作ってる所は未経験でも結構募集してる。
私の場合、業務システムの開発はつまらないなと思ったのでSIerは除外した。 ゲーム・スマートフォンアプリ・業務ソリューションと幅広く受託してる会社を選びました(1社しか受けてないですが)。
ブラック企業ホワイト企業の区別の付け方はわかりません。請負で開発している以上、残業の量はプロジェクトによるし。
SIerの友人は200~300時間働いたりしてたけど、プロジェクト変わったらやたら暇になったと言っていた。
私も受託開発会社での最後のプロジェクトは、立ち上げ時260時間働いたりしたけどそれ以降はほとんど定時に帰っていた。
ブラックかホワイトかってのも個人の価値観によるしね。260時間でも楽しかったので全く苦じゃなかった。
この時は「未経験者を募集している会社でスキルを身につけて、好きなゲームを作っている所に転職しよう」とか考えてた。
実際はゲームよりもアプリを作ることにハマってWeb系の会社に転職しましたが。
まぁそんな心持ちでいいんじゃないかな。やりたいことがあってそのための1Stepと捉えての就職なんてのもありなんじゃないでしょうか。
勉強
未経験者歓迎と書いてあっても全く勉強しないで入るのは辛いだけなので勉強しましょう。
私はスッキリわかるJava入門を購入しました。
これでJavaという言語の文法とオブジェクト指向(なんかプログラミングする上での概念とでも思ってください)を学んでいった。
文法を覚えるだけでは英語を喋れないのと同様、プログラムミングも文法を覚えただけでは全く書けないので書く事が重要。
上記の本は練習問題が随所にあったので良かった。
Javaを選んだのはよく聞く名前だったからっていうのと、てきとーにぐぐったらJavaにしとけって書いてあったから。私も未経験者におすすめを聞かれたらJavaと答える。理由は間口が広いから。
数学が云々とかよく書かれてますが、やることによる。
数学が出来ないと全く何も出来ない層もあるだろうし、まったく数学を使わない層もある。
数学が分かるほうが選択肢が広がるのはたしか。
入社・研修
未経験者も受け入れておいて研修無い企業はやばいので弾こう。
私の場合は2ヶ月の研修期間があったが、タイミングが悪かったのかちゃんとした内容の研修は受けれず危機感を覚えた。
翌年の4月に入ってきた新入社員(半数が未経験)は良い研修を受けていたようです。
- AizuOnlineJudgeでJavaの基礎学習
- JavaでWebアプリの開発
- ↑のAndroid版開発
業務
- いまオレはこの会社で最底辺に属し、最も使えない人間である
- なんでもかんでも聞いたら「こいつこんなこともわからないのか…」とお荷物扱いされる
というネガティブな思考の上で仕事をしていた。心がもたないのでこういう考えは捨てたほうが良い。
未経験を募集している時点で企業がその人を教育するのは当たり前のことだし、わからないことを教えてくれない後輩/部下ってのも困る。
- 何から何まで分からないなら、その事をしっかり伝えておくこと
- 10分くらい自分で考える
- 10分たっても分からなかった分かりそうな人に聞いてみる
- 人の書いたコードを真似て覚える
- 日々情報を集める
その後こんな感じで仕事してたらいつの間にかAndroidアプリを一人で作らされて、アプリ開発にのめり込みだして転職して今に至るって感じです。
26歳で未経験で入社してきた人だっているし、営業からエンジニアにジョブチェンジした人だっているし、ディレクターからジョブチェンジした人だっています。
大卒じゃないと無理なんてこともなくていろんな学歴の人がいます。
挑戦する気さえあればやってやれないことはないので興味があれば挑戦してみては?