Continuity is The Father of Success

Androidアプリとかゲームとか。毎日続けてるものについて。

Androidアプリ開発を始める 2020年春版 ライブラリ編

全体の概要からの続きみたいなもの。 前回がアプリ開発を始める時にざーっと考える(ベースであればとりあえず開発が開始できる)話だとすると、今回はアプリを作る時に何を組み合わせるかと言う話にしたいなと思っています。

blog.dr1009.com

去年のエントリがこちら。

blog.dr1009.com

雑感

「今から新規でアプリを作ります」という時に選択するライブラリについて、技術として2019年から2020年にかけて大きく変わったと言う印象はありません。 このため、ライブラリの選定が変わったもの以上にライブラリの枠組みが変わった点を2点あげ、ライブラリの読み方を書いておきたいと思います。

ktx

ktx は様々なライブラリのKotlin用拡張関数ライブラリです。 リリース時の資料なんかはこちら。

jakewharton.com

Android Jetpackでも提供されている core-ktx や、fragment-ktx などは知らない間に使っているかもしれません。

developer.android.com

一応コードを引用して紹介しておくと、ktxを使わないケースと使うケースが次のようになります。

// 使わない
sharedPreferences
            .edit()  // create an Editor
            .putBoolean("key", value)
            .apply()

// 使う
sharedPreferences.edit { putBoolean("key", value) }

// 使わない
sharedPreferences
            .edit()  // create an Editor
            .putBoolean("key", value)
            .commit()

// 使う
sharedPreferences.edit(commit = true) { putBoolean("key", value) }

この時、ktxで提供されているのは次のような拡張関数です。

inline fun SharedPreferences.edit(
         commit: Boolean = false,
         action: SharedPreferences.Editor.() -> Unit)

ktxは「Kotlinでライブラリを利用する時に、利用を簡単に/便利にする」ような位置付けであって、Kotlinのプロジェクトでも別に必須のものではありません。 2020年に大きく変わったのは、様々なライブラリがktxの提供を始めたことです。

例えば、Firebaseにktxが追加されるようになりました。 個人の経験になりますが、下記のktxは実際に利用して(それなりに)メリットを感じています。

また、3rd partyのライブラリでもktxが提供される流れもあります。 ライブラリの選定時にktxが提供されているかを調べてみるのも良さそうです。 (後述のCoilのように、そもそもKotlinで作りJavaでも利用できるようにしているライブラリもあるので、数年後にはktxは消えているかもしれませんが)

github.com

※注意、PermissionsDispatcherのktxは2020年4月5日時点でalphaリリースです

BOM

docs.gradle.org

Android向けのライブラリがBOM(bill of materials)に対応するようになりました。 利用シーンが多いのはFirebaseやOkHttpではないかと思います。

BOMGradle 5.0より導入された機能になります。 この機能を用いると、BOMで管理されるライブラリのバージョンをそれぞれ指定する必要がなくなります。

firebase.google.com

SpringのBOMなどは割と利用されている印象があるので、サーバーサイドの方からすると「ようやくか」というところかもしれません。

mvnrepository.com

FirebaseやOkHttpのような一つのライブラリの中でも複数のモジュールが存在するもの、またライブラリの中でも依存してる可能性が高いものについて。 BOMの活用によりより快適なビルド環境が構築されそうな予感がしています。

ライブラリ

Android Jetpack

developer.android.com

TargetSDKの関係でAndroid Jetpackの利用をする年となりました。

developer.android.com

旧来のsupport libは28の時期までしか更新されていないため、29以降が必須となる今年はAndroid Jetpackに移行する(AndroidXライブラリに移行する)必要があります。 とはいえ少し古い資料を参考にしつつAndroidXへのマイグレーション処理を行えば、旧来のバージョンからの変更を行うのは難しくありません。

大変なのは、AndroidXになり新たに提案されつつあるAPI更新に対応することです。 特に変更が大きいクラスはFragmentでしょう。

developer.android.com

Fragmentを利用してアプリを作成する場合には、1.0.0から最新までの変更点を全て確認することを強くお勧めします。

Network

square.github.io

square.github.io

github.com

github.com

REST APIであればOkHttp + Retrofit2 + Moshi-Codegen、GraphQLであればOkHttp + Apollo-Androidになるかと思います。 gRPCの場合は……リファレンスを参考に実装することになるかと。

OkHttpとRetrofit、MoshiがKotlinでリライトされました。 一時期移行に伴う不具合が散見されましたが、2020年4月5日時点では収束したと言えます。

昨年からの大きな変更としては、Gsonが下火になったことではないでしょうか。 下記の問題はGsonの利用を続けることを非常に難しくしました。

r8.googlesource.com

確認したところ開発が続いているようですが、KotlinとGsonの仕組みの相性がよくない(ハズ)のため、新規に開発を行うのであればMoshi-Codegenをお勧めします。

github.com

DI

Android Developerにページができました。

developer.android.com

Daggerの使い方も記載されるようになり、今後より書きやすくする対応を始めているところのようなので、Daggerを選んでおけば良いと思います。

developer.android.com

dagger.dev

medium.com

Image

developer.android.com

現時点では下記4つの選択肢があります。 このうち、Picassoは(開発が動いている様子はあるのですが)リリースが止まっているためお勧めできません。 Frescoは ImageView の代わりに com.facebook.drawee.view.SimpleDraweeView を利用する点などがあるため、ライブラリへの依存が(他のライブラリに比べると)強くなってしまう印象があります。このため、自分はあまり選択しようと言う気持ちになりません。

GlideとCoilを比べると、Glideの方が(歴史が長いため)APIの変更などの開発が少なく、使用例も豊富な印象です。 一方でCoilは0.9.5になりstableリリース間近であることや、Glideに比べてメソッド数が少ないことなどから、利用しても良いかなと感じるようになっています。

ゼロからKotlinでアプリを作るのであれば、Coilで開発を始めてみても良いのではないでしょうか。

Other

ViewBinding

ライブラリに含めるか迷ったのですが、一旦ここで。

developer.android.com

medium.com

リンクの内容そのままなのですが findViewById を使わなくてよくなるライブラリです。 DataBindingじゃないかと言う気もすると思うのですが、データのバインドをしないケースならViewBindingを利用できるようになりました。

これにより、ButterKnifeがDeprecatedになりました。 古いプロジェクトであれば、これの影響が一番大きいのではないかと思います。

github.com

PermissionsDispatcher

github.com

RxJavaではなくKotlin Coroutinesを使うのであればRxPermissionsを使う必要性も低いと思うので、Androidパーミッション周りで困ったら使いましょう。 先述の通りktxによる利用も、kaptを利用する場合もincremental buildへの対応もされているので、それなりのPCを利用していればビルドで気になることはないと思います。

ThreeTenABP

github.com

LocalDateOffsetDateTime などのJava8で提供されるTime & Date APIAndroidで利用するためのライブラリです。 新規アプリであればAndroid O以降を対象にして開発が開始されるであろうことや(しますよね?)、Android Gradle Pluging 4系で入るJava8 APIのR8によるDesugarにより、来年には不要になっていそうです。

jakewharton.com

一部のライブラリが依存していることもあるので、ゆっくりとライブラリ側の対応を待ちつつ(もしくはPRを作りつつ)、移行していく1年になりそうです。 本当にお世話になりました。

終わりに

ざっくり書くつもりが去年よりも長いエントリになってしまいました。 若干誰得な話になっている気がするのですが、本エントリが開発の助けになれば幸いです。

2つのシートを並べ替えて結合する処理

※昔書いた記事を某所から移行。


先日マクロで2つのファイルを結合して並べ替える処理を作ったので備忘録的に。 マクロって難しいですね。

Public Const MITSUMORI As String = "見積もり"
Public Const SHIRE As String = "仕入れ先"
Public Const KATABAN As String = "型番"
Public Const KINGAKU As String = "金額"

Sub Main()
    ' シートの用意
    Call ClearWorkSheets
    Call CreateWorkSheets
    Call CopyToWorkbook(SHIRE)
    Call CopyToWorkbook(MITSUMORI)
    
    ' ソート
    Call KatabanSort(SHIRE)
    Call KatabanSort(MITSUMORI)
    
    ' マージ処理
    Call KingakuTeknki
    
    MsgBox "完了!"
End Sub


' 前回読み込んだシートがあった場合に削除
Private Sub ClearWorkSheets()
    Application.DisplayAlerts = False
    With ThisWorkbook
        If SheetDetect(MITSUMORI) Then
            .Worksheets(MITSUMORI).Delete
        End If
        If SheetDetect(SHIRE) Then
            .Worksheets(SHIRE).Delete
        End If
    End With
    Application.DisplayAlerts = True
End Sub

' 作業用のシートを作成
Private Sub CreateWorkSheets()
    ' アクティブなシートを記憶
    Dim OldSheet As Worksheet
    Set OldSheet = ActiveSheet
    
    ' 見積もり用のシートを作成
    Dim NewMitsumoriSheet As Worksheet
    Set NewMitsumoriSheet = Worksheets.Add(After:=Worksheets(Worksheets.count))
    NewMitsumoriSheet.Name = MITSUMORI
    
    ' 仕入れ先用のシートを作成
    Dim NewShireSheet As Worksheet
    Set NewShireSheet = Worksheets.Add(After:=Worksheets(Worksheets.count))
    NewShireSheet.Name = SHIRE
    
    OldSheet.Activate
End Sub

' ファイルを開いてシートをコピー
Private Sub CopyToWorkbook(Target As String)
    Dim OldWorksheet As Workbook
    Dim TargetSheet As Worksheet
    Dim OpenFileName As String
    
    ' コピー先のワークシートを保持
    Set OldWorksheet = ThisWorkbook
    
    ' ファイルを開く
    MsgBox (Target & "用のファイルを選択してください")
    OpenFileName = Application.GetOpenFilename("Microsoft Excelブック,*.xlsx?")
    Workbooks.Open OpenFileName, ReadOnly:=True
    
    ' シートにコピー
    Set TargetSheet = ActiveWorkbook.Worksheets(1)
    TargetSheet.Cells.Copy OldWorksheet.Worksheets(Target).Range("A1")
    
    ActiveWorkbook.Close
End Sub


' シートを型番でソート
Private Sub KatabanSort(SheetName As String)
    Dim ActivateSheet As Worksheet
    Dim MaxRow As Long
    Dim MaxCol As Long
    Dim KatabanCol As Long
    
    ' 対象のシートの縦横サイズを獲得
    MaxRow = RowEnd(SheetName)
    MaxCol = RowEnd(SheetName)
    ' 型番の行を取得
    KatabanCol = getKataban(SheetName)
    
    If SheetName = SHIRE Then
        Dim KingakuCol As Long
        KingakuCol = getKingaku(SheetName)
        Call DeleteDuplicate(KatabanCol, KingakuCol, MaxRow)
    End If
    
    Set ActivateSheet = ActiveWorkbook.Worksheets(SheetName)
    ActivateSheet.Activate
    'ソートの実行
    Range("A1", Cells(MaxRow, MaxCol)).Sort (Cells(1, KatabanCol)), Header:=xlYes
End Sub

' 型番を比較して金額を転記
Private Sub KingakuTeknki()
    ' 型番の行を取得
    KatabanCol = getKataban(MITSUMORI)
    
    ' 転記先(見積もりシート)をアクティブにする
    Dim ActivateSheet As Worksheet
    Set ActivateSheet = ActiveWorkbook.Worksheets(MITSUMORI)
    ActivateSheet.Activate

    ' 型番の右隣の列に金額欄を追加
    Columns(KatabanCol + 1).Insert
    Cells(1, (KatabanCol + 1)) = KINGAKU
    
    Dim i As Long
    Dim j As Long
    Dim MitsumoriCount As Long
    Dim ShireCount As Long
    
    MitsumoriCount = RowEnd(MITSUMORI)
    ShireCount = RowEnd(SHIRE)
    
    Dim MitsumoriKataban As Long
    Dim ShireKataban As Long
    Dim MitsumoriKingaku As Long
    Dim ShireKingaku As Long
    
    MistumoriKataban = getKataban(MITSUMORI)
    ShireKataban = getKataban(SHIRE)
    MitsumoriKingaku = getKingaku(MITSUMORI)
    ShireKingaku = getKingaku(SHIRE)
    
    ' 見積もりシートと仕入れシートを比較し、一致するモノがあれば転記
    ' 高速化のためにソート済みであることを利用する
    For i = 2 To MitsumoriCount
        For j = 2 To ShireCount
            If Worksheets(MITSUMORI).Cells(i, MistumoriKataban) = _
                Worksheets(SHIRE).Cells(j, ShireKataban) Then
                ' 転記
                Worksheets(MITSUMORI).Cells(i, MitsumoriKingaku) = _
                    Worksheets(SHIRE).Cells(j, ShireKingaku)
                Exit For
            ElseIf Worksheets(MITSUMORI).Cells(i, MistumoriKataban) < _
                    Worksheets(SHIRE).Cells(j, ShireKataban) Then
                ' 次の行の確認へ
                j = j - 1
                Exit For
            End If
        Next j
    Next i
End Sub


' シートがあるかどうかを確認
Public Function SheetDetect(SName As String) As Boolean
    Dim sheet As Worksheet
    For Each sheet In ThisWorkbook.Worksheets
        If sheet.Name = SName Then
            SheetDetect = True
            Exit Function
        End If
    Next
End Function

' 1つの型番に複数の金額があったときに、高い方を残す
Public Sub DeleteDuplicate(KatabanCol As Long, KingakuCol As Long, CountEnd As Long)
    Dim i As Long
    Dim j As Long
    Dim count As Long
    
    With ActiveWorkbook.Worksheets(SHIRE)
        count = CountEnd
        
        For i = 2 To count
            For j = i + 1 To count
                If .Cells(i, KatabanCol) = .Cells(j, KatabanCol) Then
                    If .Cells(i, KingakuCol) > .Cells(j, KingakuCol) Then
                        .Rows(j).Delete
                    Else
                        .Rows(i).Delete
                    End If
                    j = j - 1
                    count = count - 1
                End If
            Next j
        Next i
    End With
    
End Sub

' シートの最終行を取得
Public Function RowEnd(SheetName As String) As Long
    RowEnd = ActiveWorkbook.Worksheets(SheetName).Range("A1").SpecialCells(xlLastCell).Row
End Function

' シートの最終列を取得
Public Function ColEnd(SheetName As String) As Long
    ColEnd = ActiveWorkbook.Worksheets(SheetName).Range("A1").SpecialCells(xlLastCell).Column
End Function

Public Function getKataban(SheetName) As Long
    getKataban = ActiveWorkbook.Worksheets(SheetName).Cells.Find(KATABAN).Column
End Function

Public Function getKingaku(SheetName) As Long
    getKingaku = ActiveWorkbook.Worksheets(SheetName).Cells.Find(KINGAKU).Column
End Function

Androidアプリ開発を始める 2020年春版

2019年のはこちら。 丸一年経ったので今年の知識の棚卸しも兼ねて。

blog.dr1009.com

はじめに

昨年版と同様に、本項の目的は下記2つです。

  • サーバーサイド開発等の経験があるエンジニアが、Android開発を始める際に参考となる資料をまとめる
  • 2020年で当たり前とされる技術を整理する

Androidアプリの開発環境を整える

AndroidStudioの導入

developer.android.com

必ず公式ドキュメントを参考にしてインストールしましょう。 最近では公式ドキュメントの大半を日本語で読むことができるようになっています。

開発を始めてみようと思ったら、ドキュメント通りに手を動かしてみることを強くお勧めします。

Flutter

2019年からの大きな違いの一つに、Flutterの存在感が増したことがあります。

flutter.dev

雑に書いてしまえば、Webアプリをベースとしたサービスのアプリであれば、FlutterによりAndroidアプリを開発してしまった方が早いかもしれません。 このため、開発を行う対象によってはKotlinによるAndroidアプリ開発を経ず、FlutterによるAndroidアプリ開発を始めてしまってよいと思われます。

flutter.dev

公式ドキュメントは英語になりますが、非常に懇切丁寧な説明がされています。 Flutterで開発されたアプリの一覧などもあるので、自分の作ってみたいアプリが明確な場合には、見比べて検討してみるとよいと思われます。

flutter.dev

開発する環境

Figmaの登場により、以前よりMacを選ぶ必要性が減ったように感じます。 ChromeBookでAndroidStudioを動かすことができるようになったこともあり、より自分な好きな環境で開発が行えるようになってきたと感じます。

developer.android.com

Androidの基礎知識

developer.android.com

このうち、一番最初に書いてある「Androidアプリのセキュリティの話」と「アプリのコンポーネント」は目を通しておくことをお勧めします。 アプリのセキュリティについて知ることで、「アプリのデータをどのように保存するべきか」を考えることができるようになります。アプリでデータを保持する際に、ベースとなる知見です。 また「アプリのコンポーネント」の中では、「アクティビティ」を特に読み込んでください。そのほかはすぐさま使うことは少ないかもしれませんが、機能開発の中で詰まった時に「あの話に関係があるのかな?」と気づくきっかけにすることができるため、目を通しておくことをお勧めします。

アクティビティのライフサイクル

Understand the Activity Lifecycle  |  Android Developers

AndroidアプリをKotlinで作る場合でも、Flutterで作る場合でも「アクティビティのライフサイクルのコンセプト」まで熟読することをお勧めします。 アプリの起動状態が異なるケースで発生するバグのトラッキング、ユーザーが別のアプリを立ち上げる時に気をつけることなどはアクティビティのライフサイクルをベースに理解することができます。

昨年同様、アプリケーションのライフサイクルやフラグメントのライフサイクルは開発が進んできたら理解していけばよいと思います。 もしもそれらを理解したいなと思ったら、Android Architecture Componentsの導入とGoogleがお勧めするアーキテクチャの導入を検討してみてください。

developer.android.com

Android Developerの抑えどころ

極めて個人的な意見となりますが、下記のページは適宜読み込むとよいと思います。 Flutterでアプリを開発する場合でも、1〜3は参考になるのではないでしょうか?

  1. Understand Tasks and Back Stack  |  Android Developers
  2. Intents and Intent Filters  |  Android Developers
  3. Input events overview  |  Android Developers
  4. Layouts  |  Android Developers

Material Design

Material ThemeのバージョンアップがGoogle I/O 2019で発表され、Dark Themeなどが正式に導入されました。

material.io

個人的に2019年に比べてAndroidアプリ開発者が覚えるべきことが大幅に増えたなと感じているのですが、その際たるものがMaterial Designに関してです。

material.io

Androidアプリ開発の中で、これまではAndroidアプリの実現したい機能に対応するUIパーツを探して当てはめるようなことがよく行われていました。 しかしAndroid向けのMaterial Designライブラリが十分な完成度となった現在、エンジニアにはMaterial Designの「理由」を理解しながらパーツを選定する技能が求められつつあります。

1例としてアプリのナビゲーションの選択を挙げてみます。 パッとアプリのナビゲーションを行うUIパーツの名前は思い浮かぶでしょうか? どのUIパーツを選択しますか? なぜそのパーツを選択するのでしょうか?

material.io

Material Designを確認すると、上記のように「UIパーツ」と「利用シーン」が明確に定義されています。

このためMaterial Designをデザイナーや企画者との共通言語として利用できるよう、理解を深める必要性が高まっています。 かつ、選択したGUIアーキテクチャがUIパーツの状態を適切にハンドリングできるよう、各UIパーツのAPIにも知見を深める必要があると言えます。

JavaとKotlin

Kotlinを利用しましょう。 デメリットとしてはJavaのみでアプリを作るより1~1.5MBほどアプリサイズが大きくなることがあげられますが、それを補って余りあるほどの開発のしやすさがあります。

Kotlin and Android  |  Android Developers

少し横道に話はそれますが、Androidの開発技術の認定なども始まっています。 (たしか)英語でのコースとなるのですが、興味と時間があればコースの受講と認定証の取得を目指してみてもよいのではないでしょうか。

Google Developers Certification

サンプルリポジトリ

Androidアプリの設計などの学習をしたいケースもあると思います。 その際には、下記のリポジトリを確認することをオススメします。

DroidKaigi 2020については、DroidKaigi App Fireside Chatがあるので試聴してみるのもよいと思います。


DroidKaigi App Fireside Chat

個人的にはAndroid Architecture ComponentsのMVVMが好きなので、2020年の構成は理解しやすかったです。

開発端末

AndroidStudioのエミュレーターが高速なため、メモリとCPUに余裕のあるマシンであれば実機がなくてもよいのではないかなと思っています。 エミュレーターの起動速度やアプリの動作速度、位置情報の変更など開発で必要な機能は十分です。

ただカメラを使ったアプリを作る場合には、実機を用意することを考えてみてください。 逆に言えば、それ以外の機能を主とするアプリであればエミュレーターによる開発で大半を賄うことができます。

なお、個人的に開発端末としてお勧めなのは Pixel 3a です。 今年の6月ごろにPixel 4aが出るんじゃないかなと思っているので、出たらPixel 4aにするのがよいのではないかと思っています。

国内版SIMフリー Google Pixel 3a 64GB Just Black

国内版SIMフリー Google Pixel 3a 64GB Just Black

  • メディア: エレクトロニクス

国内版SIMフリー Google Pixel 3a 64GB Clearly White

国内版SIMフリー Google Pixel 3a 64GB Clearly White

  • メディア: エレクトロニクス

終わりに

去年よりシンプルに書こうかと思っていたのですが、なんだかんだ同じぐらいになっちゃいました。 ライブラリ編はまた別にまとめようと思います。

ライブラリ編

blog.dr1009.com