21卒向けAndroid™研修
2021/04/20
0:00
Android は Google LLC の商標です。
講師紹介
• 名前:山田淳登(slack: atsuto.yamada)
• 仕事:TIPSTARの開発
• 15新卒
• 入社後ずっとAndroidメイン
0:01
この研修の目的と範囲
• 対象
• Android開発未経験のエンジニア
• 研修範囲
• 最新のAndroid開発の雰囲気をつかめるように
• 実際の業務に使うための入り口程度
• Kotlinの細かい文法は対象外
• 不明な場合は実習時間で質問ください
• 今では古い書き方も登場
• 基礎を学ぶために古い書き方も紹介する
• こんにちのAndroid開発は古い書き方を効率化しているものが多く、古い書き方
の理解が重要
0:02
研修内容
• Androidの基本構造
• UIレイアウト
• ActivityとView
• リスト
• (昼休憩 13時前を予定)
• 非同期処理
• 責務の分離
• 画面遷移
• ネットワーク通信
0:03
Androidの基本構造
Resources, AndroidManifest, build.gradle, Activity Lifecycle
0:03
Androidネイティブアプリとは
• AndroidのARTランタイム上で動くアプリ
• JVMではない
• Java7と一部のJava8互換
• Oracle Javaと動作が違うAPIがある
• 例えばSimpleDateFormatにおけるZ(タイムゾーン)の扱いが違う
• APIはJavaそのままなので訴訟が起きている
• 開発用の言語はKotlin/Javaが利用される
• Kotlinが主流
• Android APIはJavaで書かれている(闇)
• KotlinからJava呼び出しでトラブル起きがち
• classを吐き出せれば良いのでScala, Groovyでも開発できる
0:03
AndroidとKotlinの関係
• Java, Kotlinどちらもバイトコードを生成するための手段
• Kotlinでしか利用できない独自APIがある
• Android開発者がKotlinを選ぶ最大の理由
Java
Kotlin
バイトコード
.class
実行可能形式
.dex
0:04
プロジェクトのディレクトリ構成
• 初期状態では実際のディレクトリ構成で
表示してくれない
• Projectを選択すると実際のディレクト
リ構成になる
0:05
プロジェクトのディレクトリ構成
• app/src/main/res
• リソースを入れるところ
• 画像、図形、UIレイアウト、テキスト
• app/src/main/java
• Java/Kotlinのコードを入れるところ
• app/build.gradle
• 外部依存ライブラリなどが記されている
• app/src/main/AndroidManifest.xml
• アプリの構成情報が記されている
0:06
リソースディレクトリ
• app/src/main/res
• values/strings.xml
• アプリで利用する文字列を定義する
• Java/Kotlinコード上に文字列を直接書くことは無い
• 実行端末の言語環境によって表示文字列を変更するため
• この研修では理解のため例外的にコード上に表示文字列を書いてある
• values/colors.xml
• アプリで利用する色コード(#FFFFFF)を定義する
• values/themes.xml
• UIパーツの見た目の変更などを定義する
0:06
app/build.gradle
• ビルド時に必要な情報が定義されている
• アプリのバージョン
• compileSdkVersion
• どのAndroidバージョンのSDKでビルドするか
• 29 = Android 10
• minSdkVersion
• 動作可能最低Androidバージョン
• パッケージ名(applicationId)
• アプリを特定するためのユニークな文字列
• リリースしたら変更できない
• 外部依存ライブラリ
• dependencies
0:07
AndroidManifest
• 画面を定義(Activity)
• バックグラウンドサービスを定義
(Service)
• 近年バックグラウンドサービスに対する
AndroidOSからの規制が強く、使いづら
い
• 裏で何か動いているのは怪しい
• アプリ名, アプリアイコン
• 外部アプリから自アプリへの呼び出
し方法の定義
• intent-filter
0:08
Activity
• Androidでは画面1枚をActivityクラスのサブクラスで表現する
• 例ではAppCompatActivityのサブクラス
• 古いAndroidバージョンでも新しいActivityの機能を利用できるクラス
• 画面内で行う処理は、Activityのサブクラスを起点に処理が伸び
ていく
0:09
Activityのライフサイクル
• 画面の起動、バックグラウンド移動、フォ
アグラウンド移動、画面の終了などのアク
ションで呼ばれるメソッド群を「ライフサ
イクル」と呼ぶ
• それぞれのステート遷移時にメソッドが呼
ばれる
• 各ステートで適切な処理が必要
• コネクションの切断や、動画の停止など
• リソースを無駄にできないモバイルアプリ
ケーションに重要な要素
• ※画面回転などでも画面終了→起動が発生する
0:09
画面の起動
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestroy()
画面の表示
画面の終了
画面を隠す
Logcat
• ログを出力する仕組み
• android.util.Logを使うことで任意のタイミングでログ出力が可能
• ログレベルが6つある(以下はよく使う代表)
• DEBUG
• 開発用のログはこのログレベルで流すのが一般的
• Log.d(“TAG”, “Message”)を使うことで出力可
• ERROR
• クラッシュログが流れてくる
• Log.e(“TAG”, “Message”)を使うことで出力可
0:10
実習1-1
• 画面を見て一緒にやる実習
• サンプルコードをclone
• ディレクトリ表示を Project に変更する
• Hello, worldを表示させる
0:11
mkdir AndroidStudioProjects
cd AndroidStudioProjects
git clone git@github.com:mixigroup/2021AndroidTraining.git
実習1-2
• 画面を見て一緒にやる実習
• Log.dによるログをonCreate, onStart, onResume,
onPause, onStop, onDestroyの呼び出しタイミングで
Logcat出力する。
• ライフサイクルの呼び出しタイミングを知る。
• ※この実習の完了状態にする
• git checkout lession1-2end
• 以降、答えはすべてリポジトリ上にcommitしてある
0:20
UIレイアウト
View, ViewGroup
0:35
Viewとは
• ディスプレイ上に何か表示するためのコンポーネント
• android.view.Viewのサブクラス
• 代表的なView
• TextView
• 文字を表示
• ImageView
• 画像を表示
• Button
• ボタンを表示
• EditText
• 入力フォームを表示
• XMLを用いて表現できる(→次ページ)
• 実態はインスタンス
0:35
TextView
res/values/strings.xml UIで表示するテキストはこちらに定義
res/layout/activity_main.xml(レイアウトXML)
XML上でTextViewの属性(Attribute)を変更可能
0:36
TextViewに対する属性指定
android:layout 親要素に対する属性指
定
Viewを特定するID(自由な名前で設定可)
ImageView
0:37
res/layout/activity_main.xml(レイアウトXML)
ImageViewに対する属性指定
Viewのサイズ指定は実は親要素が管理している
この画像はApache License 2.0で配布されている制作物を含んでいます。
余談
• プリセットでアイコンが用意されており、好きなアイコンを追加で
きる
• resで右クリック→New→Vector Asset
0:38
EditText
res/layout/activity_main.xml(レイアウトXML)
0:39
サイズ単位 sp, dp
• アプリが動く端末のディスプレイは、高解像度・低解像度が混
在している
• pixel単位をそのまま使えない
• 低解像度ディスプレイでは大きく、高解像度ディスプレイでは小さく
なる
• サイズ指定では dp を使う
• 画面のピクセル密度に依存しないサイズ指定が可能
• テキストサイズでは sp を使う
• OSのフォントサイズ設定によって実サイズが異なる
• 標準サイズであれば 14sp→14dp
0:39
ViewGroupとは
• Viewを複数並べるためのコンポーネント
• android.view.ViewGroupのサブクラス
• ViewGroupもViewのサブクラス
• 代表的なViewGroup
• FrameLayout
• 複数のViewを重ねて表示
• LinearLayout
• 複数のViewを縦や横に並べて表示
• ConstraintLayout
• 複数のViewに制約(並べ方)を定義して表示
• RecyclerView
• 複数のViewを表示領域だけ縦や横に並べて表示
0:40
LinearLayout
• android:orientation
• vertical/horizontal
• 並べる方向を指定
• android:layout_gravity
• center/end/start等
• 配置のgravityを指定
• デフォルトはstart
0:41
この画像はApache License 2.0で配布されている制作物を含んでいます。
ConstraintLayout
• View同士の制約を設定して並べる
• app:layout_constraint…_to…
• 複雑な並べ方も可能
• 詳しくは実習で
0:42
この画像はApache License 2.0で配布されている制作物を含んでいます。
wrap_content, match_parent
• android:layout_width, android:layout_heightで指定
• wrap_content
• Viewのコンテンツに合わせてサイズを決定する
• match_parent
• Viewの親要素のサイズに合わせてサイズを決定する
• ConstraintLayoutにおける0dp
• constraint指定に従ってサイズ決定
0:43
実習2-1
• 画面を見て一緒にやる実習
• 実習前にブランチを変更する
• git stash
• git checkout lession2-1start
• LinearLayoutで ImageView, TextView, EditText, Button
を並べる
• ConstraintLayoutで ImageView, TextView, EditText,
Button を様々な並べかたを試す
• ドキュメント
0:45
実習2-2
• 自分で入力する実習
• 実習前にブランチを変更する
• git stash
• git checkout lession2-2start
• 左のように ImageView, TextView,
EditText, Button を
activity_main.xmlに並べてみる
• ※この実習の完了後の状態にする
• git checkout lession2-2end
1:00
この画像はApache License 2.0で配布されている制作物を含んでいます。
ActivityとView
ActivityからのViewの操作
1:15
ViewをActivityから操作する
• Activity#setContentView
• XMLをパースし、Activity内にViewを展開する
• XMLはViewの構成を記すだけの存在
• ViewインスタンスをActivity内で取得する
• Activity#findViewById
• XML内で設定したIDはR.id内にフィールドとして自動生成される
1:15
onCreate(画面起動時)にTextViewのテキストが変更される
Viewを任意のタイミングで操作する
• Viewがクリックされたタイミング
• View#setOnClickListenerを使う
• クリック時 setOnClickListener{ } ブロック内が実行される
1:16
Buttonクリック時にTextViewのテキストが変更される
Viewからデータを取り出す
• EditTextから入力テキストを取り出す
• EditText#getText※で取り出せる
• ただしEditableという型で返ってくるので純粋にStringがほしければtoString()
する必要がある
1:17
※Javaクラス上の getHoge, setHoge
はKotlin上では hoge に変換される
実習3-1
• 自分で入力する実習
• 実習前にブランチを変更する
• git stash
• git checkout lesson3-1start
• EditTextへの入力に連動して、入力したテキ
ストをTextViewに表示する
• ヒント
• ※この実習の完了後の状態にする
• git checkout lesson3-1end
1:18
この画像はApache License 2.0で配布されている制作物を含んでいます。
リスト
RecyclerViewの使い方
1:30
なぜRecyclerViewを使うのか
• 単純にLinearLayoutを使うと、描画する必要
のない領域までViewがレイアウトされる。
• リストの要素が100個あればTextViewが100個
必要→十数個しか使わないのに無駄
︙
画面領域が赤線部分だとすると
この領域外のTextViewが無駄
1:30
なぜRecyclerViewを使うのか
• RecyclerViewを使うと表示領域分だけ
Viewをレイアウトできる
• スクロールすると必要に応じてViewが新
規作成/再利用される
1:31
なぜRecyclerViewを使うのか
• スクロールすると、領域外に出た
Viewがリサイクルされ、次に見える
アイテムとしてレイアウトされる
1:32
なぜRecyclerViewを使うのか
• このような構造を持ったViewはAdapterViewのサブクラスとし
て存在している
• ListView
• GridView
• Spinner
• 今では、色々なカスタマイズが出来るRecycleViewがその役目
を果たしている
1:33
RecyclerViewを使う上で必要な実装
• RecyclerView.Adapter
• getItemCount
• データの総数を返す
• onCreateViewHolder
• Viewを新規作成し、ViewHolderを返す
• onBindViewHolder
• Viewにデータを設定する
• RecyclerView.ViewHolder
• 1行分のViewの入れ物
1:34
実習4-1
• 画面を見て一緒にやる実習
• 実習前にブランチを変更する
• git stash
• git checkout lesson4-1start
• 100個の要素を表示するリストを実装する
• ※この実習の完了後の状態にする
• git checkout lesson4-1end
1:35
実習4-2
• 自分で入力する実習
• 実習前にブランチを変更する
• git stash
• git checkout lesson4-2start
• Buttonを押すことでEditTextに入力された
テキストをリストに追加する実装を行う
• Adapter内のデータを変更した場合は
RecyclerView.Adapter#notifyDataSetChanged()を呼ぶ必要あ
り
• ※この実習の完了後の状態にする
• git checkout lesson4-2end
1:50
休憩時間(昼)
2:00
非同期処理
AsyncTask, Kotlin Coroutines
2:00
Androidにおける非同期処理の重要性
• Androidにおける処理スレッドの種類は大まかに2つに分けられ
る
• Mainスレッド(UIスレッドとも呼ぶ)
• IOスレッド(バックグラウンド スレッド)
• onCreateなどのライフサイクルメソッドはMainスレッドで呼
ばれる
• Mainスレッドで重い処理やネットワーク通信を行うと…
• 通信が終わるまでアプリ固まる
• ネットワーク通信するとクラッシュする(Androidの制約)
• Viewの操作はMainスレッドで行う必要がある
• IOスレッド→Mainスレッドへのコールバックが必要
2:01
AsyncTaskとは
• 古くからあるAndroidの非同期処理用のクラス
• IOスレッドで行うメソッド、Mainスレッドでコールバックするメソッ
ドが分かれている
• 非同期処理を理解するため今回使う
• 今ではKotlin Coroutinesに置き換わっている→後で説明あり
2:02
AsyncTaskとは
• AsyncTaskを実装
• doInBackground
• IOスレッドで実行される
• publishProgressで途中経過を設定
• 戻り値に処理結果を渡す
• onProgressUpdate
• Mainスレッドで実行される
• publishProgressの呼び出しタイミング
で実行される
• onPostExecute
• Mainスレッドで実行される
• doInBackgroundの結果が渡される
• onDestroyで停止させないと、画
面を閉じても実行停止しない
• メモリリークの原因になる
2:03
実習5-1
• 画面を見て一緒にやる実習
• 実習前にブランチを変更する
• git stash
• git checkout lession5-1start
• さきほどのAsyncTaskを利用したコードを実行する
2:04
Kotlin Coroutines
• 実習5-1の実装を、下のように書けると理想
• 実際動かすとアプリが固まる
• このようなスタイルで書くことを実現できるのがKotlin Coroutines
2:10
Kotlin Coroutines
• スコープ中で中断可能
• Mainスレッドがロックされない
• →アプリが固まらない
• 画面終了すると自動終了
• 開発者がライフサイクルを意識する必要がない
• メモリリークしにくい
• 実行スレッドの変更が容易
• 非同期処理とUI変更を同じスコープで行うことができる
2:11
Kotlin Coroutines
• CoroutineScope
• このブロック内でCoroutinesが有効になる
• Acvitityでは lifecycleScope.launch の呼び出しでブロックを作成でき
る
• delay
• 指定秒間スレッドを中断できる(停止ではない)
2:12
さきほどのAsyncTaskの実装をCoroutines
に置き換えるとここまで短くなる
Kotlin Coroutines
• Job
• launch の戻り値
• 実行中の処理を外から操作できる
• Job#isActive
• 実行中かどうか
• Job#cancel()
• 処理をキャンセル
• Job#join()
• CoroutineScopeでのみ呼び出し可能
• 処理が終わるまで、現在のスコープを中断
2:13
Kotlin Coroutines
• withContext
• スレッドの切替えを行う
• Dispachers.* から選ぶ
• suspend fun
• CoroutineScope内でのみ呼び出し可能な関数
• delay, withContext, Job#join はsuspend funで定義されている
2:14
Kotlin Coroutines
• Coroutinesの停止をハンドリングする
• suspend funの呼び出し箇所で CancellationException がthrowさ
れる
• catch/finallyすればハンドリングできる
• リソースの開放が必要な呼び出しで必須
2:15
実習5-2
• 自分で入力する実習
• 実習前にブランチを変更する
• git stash
• git checkout lesson5-2start
• 実習前に一度ビルドする
• Coroutines系ライブラリがIDEに認識さ
れてない可能性があるため
• 右のような動作をする実装
• ※この実習の完了後の状態にする
• git checkout lesson5-2end
2:16
責務の分離
LiveData, ViewModel
2:30
責務の分離
• 色々な機能を実装していくと発生する問題
• Activityが肥大化する
• 可読性の低下
• メンテナンスコストの増加
• バグの増加
• 書く人によって構成がバラバラ
• →責務の分離が必要
2:30
MVVMアーキテクチャ
• 近年のデファクトスタンダードな設計思想
• Model, View, ViewModelに分ける
(人によって理解が様々なので、分離ができていれば良い)
Activity
View
ViewModel Repository
Viewの変化・命令
Viewに設定する
データの通知
リクエスト
結果(レスポンス)
データをViewに
入れる事だけを
考えれば良い
Viewに出すデー
タの加工だけを
考えれば良い
データを取得する
ことだけを考えれ
ば良い
2:31
View ViewModel Model
ViewModel
• AndroidViewModel
• Activityに結びつくViewModelの親クラス
• 実装するにあたって、このクラスを実装する
• viewModelScope.launch
• ViewModel内でCoroutineScopeを生成する
• by viewModels<TYPE>()
• Activity内でViewModelを生成する
2:32
LiveData
• データの入れ物
• 更新を通知する機能が備わっている
• ViewModelからActivityへデータの更新を通知する役割
• LiveData#observe
• データの変化時に実行されるリスナー(Observer)を登録する
• ライフサイクルが onStart() から onStop() の間だけ実行される
• データが既に設定されている場合、登録時に1度だけObserverが呼ばれる
• LiveData#removeObserver
• Observerの登録を解除する
• ただし、Observer登録時にLiveData#observer(this) {} を利用した場合
はActivity#onDestroy()時に自動的に解除されるので必要ない
2:33
MutableLiveData
• 外から値を変更できるLiveData
• setValue(), getValue()
• LiveDataの値を更新/取得する
これまでの話を踏まえた、1秒ごとに更新するテキスト実装
2:34
MediatorLiveData
• 別のLiveDataのデータを元にデータ設定できるLiveData
• MediatorLiveData#addSource(){}
• ソース元を設定する
• 引数のLiveDataが変化したらブロックが呼ばれる
経過秒数というデータをテキストに変換
2:35
LiveData.map{}
• MediatorLiveDataを生成するショートカット
• 他のLiveDataの値を変換する
2:36
経過秒数というデータをテキストに変換(短縮版)
LiveData
A
LiveData
B
変換
LiveData.switchMap{}
• MediatorLiveDataを生成するショートカット
• 他のLiveDataの値を元にLiveDataを切り替える
2:37
LiveData
A
LiveData
B
LiveData
B
LiveData
C
Aを元に
スイッチ
LiveData.switchMap{}
LiveData
A
LiveData
B
LiveData
B
LiveData
C
Aを元に
スイッチ
chooseSecondが true の場合 “…秒”
falseの場合 “…分”
2:52
CoroutineLiveData
• ObserverがActiveな間、動作するCoroutineScopeを生成
• ObserverがonStartからonStopの間
• 画面がバックグラウンドにある時、停止してくれる(中断ではない)
• liveData<TYPE>{}
• emit()
• 値を設定する(≒setValue)
• emitSource()
• 値を設定するLiveDataを設定する
2:53
実習6-1
• 自分で入力する実習
• 実習前にブランチを変更し、ビルドする
• git stash
• git checkout lesson6-1start
• ボタンを押すと、指定地域の時刻表示に変わる実
装
• 日本: +9:00, ハワイ: -10:00, 北京: +8:00, インド:
+5:30
• ※この実習の完了後の状態にする
• git checkout lesson6-1end
2:53
画面遷移
Activityの画面遷移
3:15
Activityの新規作成
• AndroidStudioのウィザード上
から簡単に作成できる
• New→Activity→Empty Activity
• Activityサブクラス、
Layout.xml、AndroidManifest
定義が自動生成される
3:16
Activity遷移とパラメーターの受け渡し
• Intent
• Activity(, Service, Broadcast)の起動に必要なデータを設定するク
ラス
• Activity遷移の場合はコンストラクタ Intent(Context, Class<?>)
でインスタンス生成する
• Intent(this, SubActivity::class.java)
• Intent#putExtra(key, value)
• 遷移先に渡すパラメーターを指定
• Activity#startActivity()
• 画面遷移の開始
• 引数にIntentを渡す
※実際にはKeyは定数を指定した方が良い
3:17
※Context: Activityの親クラス
パラメーターの受け取り
• intent.getStringExtra(key)
• Stringパラメーターの受け取り
• intent.getIntExtra(key)
• Intパラメーターの受け取り
3:18
遷移先Activityでのパラメーター受け取り
これらの実装は次セクションの実習で使う
ネットワーク通信
ViewModel, LiveDataを用いたネットワーク通信とUI変更
3:19
ネットワーク通信
• ネットワーク通信を行うにはAndroidManifestの変更が必要
• <uses-permission android:name="android.permission.INTERNET" />
• <uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
• その他にもデバイス特殊機能へのアクセスには uses-permission の追加が必要
• カメラの使用 ※
• 位置情報 ※
• ストレージへのアクセス ※
• ※Android6.0未満ではuses-permissionの追加だけで済んだが、それ以降は更に
UI上での許可を求める必要がある(App Permissions)
• 必ずバックグラウンドスレッドで通信行う必要がある
3:19
HTTPアクセスとハンドリング
• HTTP/HTTPSクライアント
• HttpsURLConnection
• Android純正
• OkHttp
• Squre製
• Jsonパーサー
• JSONObject
• SDKに同梱
• Gson
• Google製
• Java時代のデファクトスタンダード
• Moshi
3:20
Coroutinesとネットワーク通信実装
• withContext(Dispatchers.IO) {} 内で通信を行う
• MutableLiveDataに結果を入れる
• withContext(Dispatchers.IO){}の中でsetValue()を行わないこと
• setValue()はMainスレッドで呼び出す必要がある
• (例)Githubの指定のレポジトリ情報を取得
3:21
実習7-1
• 自分で入力する実習
• 実習前にブランチを変更し、ビルドする
• git stash
• git checkout lesson7-1start
• ボタンを押すと、SubActivityへ遷移し
mixi-inc/AndroidTrainingの情報が
表示されるように実装する
• ※この実習の完了後の状態にする
• git checkout lesson7-1end
3:22
実習7-2(最終課題)
• 実習前にブランチを変更する
• git stash
• git checkout lesson7-2start
• EditTextにOwner名を入力し[決定]を押す
と、リポジトリ一覧を表示する
• リストアイテムを押すと、SubActivity
へ遷移しmixi-inc/AndroidTrainingの
情報が表示されるように実装する(7-1で
実装済み)
3:40

ミクシィ 21卒向け Android研修