この講座で身につく主なスキル
「設計(アーキテクチャ)」の基礎知識(MVVM / MVP)
「
Android Architecture Components
(AAC
)」の使い方(Androidアプリ開発の新スタンダードとなる設計ライブラリ [ViewModel / LiveData / DataBinding])
サービス(
Service
)を使い方(バックグラウンド処理)「Koin」を使ったDependency Injection(DI:依存性の注入)
ランチャースクリーン(スプラッシュスクリーン)の作り方 BottonNavigationViewの使い方
「ExoPlayer」を使ったBGM再生(MediaPlayerより高機能な音声・動画再生ライブラリ)
SeekBarを動かして音量調整する方法(端末のボリュームコントロールとリンクさせる方法)
⇥⇤⌅⇧⌃⌥ ⌦↵
⇥⇤⌅⇧⌃⌥ ⌦↵
ホップ
ステップ
ジャンプ
どんなアプリを作りたいかを 紙に書く
完成図(レイアウト)の作成と 小道具(画像・音など)の準備 つくり方説明書
(ソースコード)の作成
resフォルダ
javaフォルダ
紙に書いたことを 1つずつ 実装するだけ
⌅✏⇣⌘✓◆ ⌫⇠⇡
⌅✏⇣⌘✓◆ ⌫⇠⇡
⇢⇣ ✓⌧⌅ ✓⌧ ✓!
⇢⇣ ✓⌧⌅ ✓⌧ ✓!
(注)Google I/O => Google Innovation in the Open
現在のGoogleのほとんどのサービスで適用されている
⇒ スマホだけでなくあらゆるデバイス(web含む)で適用 現在のGoogleのほとんどのサービスで適用されている
⇒ スマホだけでなくあらゆるデバイス(web含む)で適用
Google I/O 2014で発表された
新しいデザインガイドライン(デザイン言語)
✏ ◆⌘⌃"#$ %&'
✏ ◆⌘⌃"#$ %&'
断片・かけら・小部分
タブレット(大画面UI)の出現がFragmentを誕生させた タブレット(大画面UI)の出現がFragmentを誕生させた
表示だけでなく動作も持てる画面(Activity)のパーツ(部品)
()*+ ,
()*+ , -./0123456 -./0123456 %& %&
表示だけでなく動作も持てる画面(Activity)のパーツ(部品)
動作が持てる(独自のライフサイクルがある) ビュー(View)との違い
アクティビティ(Activity)との違い
Fragment単独では使えない(必ずActivity に属する)
1つの画面に複数のFragmentを配置できる
(Activityは画面に1つだけ)
()*+ ,
()*+ , -./0123456 -./0123456 %& %&
onAttach onCreate onCreateView onActivityCreated
onResume onPause
onDestroyView onDestroy
onDetach onCreate
onResume onPause
onDestroy
Activity Fragment
開始開始
完了完了
開始開始 完了完了
画面が開いている
FragmentがActivityにくっつく
FragmentがActivityから離れる Fragmentの生成
Fragment内のViewの生成 Activity#onCreateの完了
⇒ Viewの操作はここでやるのが安全 (特にViewに何かを表示するとき)
Fragment内のViewの破棄 Fragmentの破棄
./012345
./012345 ⇡)$(7$8⌃ ⇡)$(7$8⌃
対応したい言語コードを設定したvaluesフォルダを用意し 言語ごとに文字列リソース(strings.xml)を作成
res/values
<string name="sort">Sort</string>
<string name="delete">Delete</string>
<string name="edit">Edit</string>
<string name="register">Register</string>
strings.xml
<string name="sort">ソート</string>
<string name="delete">削除</string>
<string name="edit">編集</string>
<string name="register">登録</string>
strings.xml
res/values-ja
■ 日本語 : ja ■ 英語 : en ■ 中国語 : zh
■ スペイン語 : es ■ フランス語 : fr ■ 韓国語 : ko
ファイル名 は同じ
主な言語コード
⌘✓◆⇡9:;
⌘✓◆⇡9:; - - <=>)$? <=>)$? 6 6
アイテム(View)を1つだけ表示させるためのコンテナ(フレーム)
ビューを重ねて表示させたい場合や、Fragmentを動的に設定する場合 に使える(ルートレイアウトの子ビューとして設定)
Activity
FrameLayoutFragmentA
FragmentB
Fragmentを 動的に入れ替え
./023@0ABC5
./023@0ABC5 %&' %&'
マテリアルデザイン標準テンプレートであるBasicActivityの上に スライド形式のナビゲーションバーが乗っかった3階建てレイアウト
Class MainActivity extends AppCompatActivity { fun onCreate() {
SetContentView
(R.layout.activity_main) }
}
1つのソースファイル
3つのレイアウトファイル(3階建て構造)
app_bar_main.xml content_main.xml
1階[下]CoodinatorLayout ConstraintLayout
include
2階[中]
2階以下は BasicActivityと同じ
include
activity_main.xml
DrawerLayout3階[上]
NavigationView
headerLayout
(nav_header_main.xml)
menu
(menu/activity_main _drawer.xml))
HeaderLayout とmenuから成る
F0GH105HB4EM/0N3/
F0GH105HB4EM/0N3/ %& %&
状態リスト(StateListDrawable)でタッチフィードバックを定義する res/drawableフォルダに状態リストのxmlファイルを作る
効果を出したいImageButtonの「srcCompat」に、作成した状 態リストを設定する(Propertiesからでも可能)
ImageButtonの背景画像の設定先を「srcCompat」から
「background」に移動させる
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/colorPrimaryBackground" android:state_pressed="true"/>
<item android:drawable="@android:color/transparent" android:state_pressed="false"/>
</selector>
ボタンが押されたとき
ボタンが押されていないとき
O P(Q=RS 8⇡TUK
O P(Q=RS 8⇡TUK
■Buttonの下辺から[from]
■ImageViewの上辺へ[to]
■34dpの制約[how]
■ImageViewの右辺から
[from]
■親(parent)の右端へ[to]
■56dpの制約[how]
制約の3要素
画面上のパーツの位置関係を制約(Constraints)で設定
全部の制約を自動的に設定 してくれる「Infer Constraints」
制約を全て解除する
「Clear All Constraints」
個別の制約を解除したい 場合はここで消す
基本は好きなように並べて最後に「Infer Constraints」を押せばいい 基本は好きなように並べて最後に「Infer Constraints」を押せばいい
VB4W5/0H 45@0ABC5 VB4W5/0H 45@0ABC5 & &
%XYZ[ \]^_`ab c
%XYZ[ \]^_`ab c
RecyclerView + CardViewを使う RecyclerView
CardView
それぞれ
レイアウトファイルを 作って
ソースコードで
結合させる
ListView をさらに進化させて柔軟にしたもの
丸みのある角と影を持つ FrameLayout(カード)
1枚のカード内に
複数の種類のView(クラス)を 組み込むことが出来る ListViewに出来ない芸当
✏ ◆⌘⌃"#$ ⇡⌅O$⌃
✏ ◆⌘⌃"#$ ⇡⌅O$⌃
I`⌫de⇤◆⌅,⇡ XK
I`⌫de⇤◆⌅,⇡ XK
⌘✓◆⇡⇢fg
⌘✓◆⇡⇢fg - - ⌘=⇧ 8P ⌘=⇧ 8P 6 6 !%& !%&
h⇤i⇢ h⇤i⇢ j=⌅k=R lmfgn j=⌅k=R lmfgn !⌥op⇤⇡qr !⌥op⇤⇡qr
アーキテクチャ:アプリを構成する部品の役割分担のさせ方のパターン
Androidアプリはややこしい!
エントリー 複数の ポイント
コンポーネント 他の との連携
他のアプリ
(暗黙的インテント) との連携
複雑な ライフ サイクル
メモリがなくなったら システムに勝手に殺される(T_T)
やみくもにソースコード書いてたらグチャグチャになってまうで~(T_T)
ソースコードにも「設計」が要るな~
⇒ 複雑なアプリの堅牢性と品質を高める ソースコードにも「設計」が要るな~
⇒ 複雑なアプリの堅牢性と品質を高める
完成図 つくり方説明書
レイアウト[xml] ソースコード[kotlin]
✓<*)s *&t< 8 X%uv
✓<*)s *&t< 8 X%uv
パッケージ A
パッケージ B
クラス
class X {
fun methodA(){
--- }
fun methodB(){
--- }
}
クラス クラス クラス クラス クラス クラス クラス クラス
クラス クラス クラス クラス 関数
(
メソッド)
関数
(
メソッド)
関数(メソッド)の行数が増えてきたら
クラス内の関数(メソッド)の数が 増えてきたら
パッケージ内のクラス数が増えてきたら
複数関数
(メソッド)に分割
複数クラスに分割
複数パッケージに分割
wB5x H 4-y0G06
wB5x H 4-y0G06 ⇡z{;⇡|}~ ⇡z{;⇡|}~
⇢fg ⇢fg - - ⌘=⇧ 8P ⌘=⇧ 8P 6 6 !⇡ÄÅÇ !⇡ÄÅÇ
【大原則】アプリのデータや状態をコンポーネントに保存してはいけない!
ÉÑ5H GH 5AÖ./012345
ÉÑ5H GH 5AÖ./012345 h h Üá Üá 8)⌅r 8)⌅r
àz⇡k=Râq⇤Jhäã⇡åçr àz⇡k=Râq⇤Jhäã⇡åçr
Üá Üá & & é"⌃ é"⌃ qèêëíì qèêëíì
UIクラスに書くのは
①:UI(ビュー)を表示するロジックと
②:OS(Androidシステム)とのやり取りだけ!!
UIに表示させるデータを処理する役割は、UIクラスとは別の人(クラス)に やってもらう(モデル=データを処理するコンポーネント)
役割を明確に分離してライフサイクルの影響を受けにくくする 役割を明確に分離してライフサイクルの影響を受けにくくする
Activity/Service/BroadcastReceiver /ContentProvider
îïBB1x 3
îïBB1x 3 ñóòô⇡fgöõúùû ñóòô⇡fgöõúùû ü ü E†°°† E†°°†
î°H ° 3Nü °†
î°H 3N†B¢3x ü †
î†B¢3x ü
データの表示
Activity/Fragment
Viewに表示する データの処理
(ビジネスロジック)
データの管理
Model-View-ViewModel
(Android Architecture Components(AAC)のパターン)
LiveData LiveData
ViewからViewModelの呼び出しは一方通行
(DataBinding or Livedataを使うことでコールバック地獄から解放!)
ViewからViewModelの呼び出しは一方通行
(DataBinding or Livedataを使うことでコールバック地獄から解放!)
£3§BWH 5B/A
データの取得
ViewModel内のプロパティを監視して 監視元(Activity等)に変更を 自動で通知できるオブジェクト ViewModel内のプロパティを監視して
監視元(Activity等)に変更を 自動で通知できるオブジェクト LiveDataによる自動通知 Or
DataBindingによる自動更新
DataBinding
DataBindingî°H ° 3Nü •
î•/3W3453/ü †
î†B¢3x ü
データの表示
Activity/Fragment
Viewに表示する データの処理
(ビジネスロジック)
データの管理
Model-View-Presenter
ViewからPresenterの呼び出しは双方向
(DataBindingもLivedataも不要だがコールバック地獄が残る!)
ViewからPresenterの呼び出しは双方向
(DataBindingもLivedataも不要だがコールバック地獄が残る!)
£3§BWH 5B/A
データの取得
î î lc¶⌦⇡fgöõúùû lc¶⌦⇡fgöõúùû üE†°• üE†°•
VB45/0Ñ5B/
( á453/ß0Ñ3 )
VとPをつなぐ Interface
ÉÉV ÉÉV ⇡⇢ ⇡⇢ °H3N†B¢3x °H3N†B¢3x !%& !%&
ライフサイクルの影響を受けずに、
UI関連のデータを保存・管理できるクラス
画面の回転等でActivityが破棄されても、データを 保持し続けることが出来る
Activityが
3回破棄されてる!
®©⇡°H 3N†B¢3x
®©⇡°H 3N†B¢3x
™´0/3¢°H 3N†B¢3x
™´0/3¢°H 3N†B¢3x
Fragment間でViewModelを共有しない
⇒ Fragment間でデータのやり取り不可
Fragment間でViewModelを共有する
⇒ Fragment間でデータのやり取り可能
ViewModelからはAndroidフレームワーク関連のクラス
(Activity/Fragment含む)を原則参照してはいけない ViewModelからはAndroidフレームワーク関連のクラス
(Activity/Fragment含む)を原則参照してはいけない
ApplicationContextを使いたい場合はAndroidViewModelを継承させる
アプリの「今の状態」にアクセスするための橋渡しをする抽象クラス
状況・事情・背景
ソースコード
クラス横断的なアプリケーションレベル でのオペレーションの状況
■ アクティビティの起動
■ ブロードキャスト
■ インテントの受け渡し等
Context
抽象クラスだが実装は Androidシステムが行っている
■ アプリ固有のリソース・クラス ■ アプリの環境情報
静的状態
動的状態
■ ActivityContext ⇒ Activityが破棄されたら破棄される
■ ApplicationContext ⇒ Applicationが生きている限り有効(★)
■ ActivityContext ⇒ Activityが破棄されたら破棄される
■ ApplicationContext ⇒ Applicationが生きている限り有効(★)
全く別物
⇢ ⇢ VB453¨5 VB453¨5 ! % & ' ! % & '
システム上の都合でActivityが破棄された場合にActivityの状態を一時的に保存する
■ 画面(Activity)の構成が変化したとき(画面の向きの変更・表示言語の変更等)
⇒ onDestroy()が呼び出されて破棄され、その直後にonCreate()が呼び出されて再生成される
■ Activityが停止状態(Paused)の時に、他のアプリなどでメモリが使われてしまった場合
⇒ onDestroy()が呼び出されて破棄されて、再生成してくれない(長時間の停止状態は良くない)
onSaveInstanceState()をオーバーライドして
Activityが破棄されても残しておきたい情報を保存しておく
onSaveInstanceState()で保存したBundle型の 情報がonCreate()の引数として渡される
ÉÑ5H GH 5A
ÉÑ5H GH 5A ⇡≠ÆØ∞± ≤≥ ⇡≠ÆØ∞± ≤≥
h h B4™0G3á4W504Ñ3™5053 B4™0G3á4W504Ñ3™5053 r r
ÉÉV ÉÉV ⇡⇢ ⇡⇢ @HG3M050 @HG3M050 !%& !%&
監視可能なデータを保持するためのクラス
(原則「ViewModel」クラスの中で使う)
データに変更があった場合は、監視元に自動で通知 させることが出来る (コールバック地獄からの解放!)
$ O=(¥=⌅
$ O=(¥=⌅ @H @H G3M050 G3M050
外注元から呼び出しを受けた上で 更新(コールバック要)
外注元から呼び出しを受けた上で
更新(コールバック要) 監視元の変更を自分でキャッチして 自分で更新(コールバック不要)
監視元の変更を自分でキャッチして 自分で更新(コールバック不要)
° á453/ß0Ñ3 •
外注先(P)のことは 何もわからない
° °†
LiveData
(スパイ)
LiveData
(スパイ)
監視先にスパイを 送って監視
変更があったら スパイ自ら監視元に通知 変更があったら
コールバックメソッド呼び出し
⇢ ⇢ M050DH M050DH 4¢H 4¢H 41@H 41@H µ/0/A µ/0/A !%& !%&
レイアウトファイル上で別のクラスと結びつけて(バインド) バインド先のメンバをレイアウトファイル上からも参照できる機能
【Kotlin Android Extentionsとの違い】
「’ndViewById」が不要になる点は同じだが、
他クラスのメンバをレイアウトファイル上からでも参照できる 【Kotlin Android Extentionsとの違い】
「’ndViewById」が不要になる点は同じだが、
他クラスのメンバをレイアウトファイル上からでも参照できる
コールバック撲滅のみならず ソースコード自体も削減出来る!
レイアウトファイルが所属するView
(Activity/Fragment等)とは違うクラス
î†0H 4ÉÑH ° 5H GH 5A∂∑5ü °†
î°H 3N†B¢3x ∂ ∑5ü
LiveData LiveData
î0Ñ5HGH
°
5A∏20H°
4∂¨2xü î0Ñ5HGH5A∏20H4∂¨2xüプロパティ メソッド
レイアウトファイル から参照可能
✕ View-ViewModel間の
ソースコード上でのやり取りが不要に
完成図(layout)でつけた「id」を説明書(kotlin)でそのまま使える!
完成図(layout)でつけた「id」を説明書(kotlin)でそのまま使える!
appレベルのbuild.gradleに「kotlin-android- extentions」プラグインを入れる
apply plugin: 'kotlin-android-extensions'
説明書(kotlin)に結びついている完成図(layout)
をインポートする
import kotlinx.android.synthetic.main.activity_main.*
完成図(layout)のファイル名
⇢ ⇢ ßH ßH 4¢°H 4¢°H 3NDAá 3NDAá ¢ ¢ ! !
7π∫) ⌫⇠⇡⌅ ✓
7π∫) ⌫⇠⇡⌅ ✓
†°°† †°°† h h ÉÉV ÉÉV r⇡IJKª r⇡IJKª
ViewModel+LiveData(DataBinging無し)
°H 3N†B¢3x
°H 3N ( Üá )
îÉÑH 5H GH 5AEÖE./012345ü
監視プロパティの設定 ViewModelインスタンス取得
(Fragmentの場合はonActivityCreatedで)
[普通のViewModel]
ViewModelProviders.of(this).get(ビューモデルクラス) [Fragment間でシェアする場合]
ViewModelProviders.of(activity).get(ビューモデルクラス)
ビジネスロジック
(処理の中身=メソッド)の実装 ⇒ 監視プロパティの値変更
値変更を受けてView側に自動的に通知され、
⑤の処理が実行される(呼び出し不要)
監視プロパティを「監視(
observe)」し、値変更を受けた処理を記述
監視プロパティ.observe(this, Observer{
値変更を受けた処理を記述 })
LiveData LiveData
処理の外注
(ビューのイベント処理等)(ViewModelのメソッド呼び出しだけ)
Viewクラス内に
ビジネスロジック(処理の中身)は書かない
†°°† †°°† h h ÉÉV ÉÉV r⇡IJKº r⇡IJKº
ViewModel+LiveData+DataBinging
°H 3N†B¢3x
°H 3N ( Üá )
îÉÑH 5H GH 5AEÖE./012345ü
監視プロパティの設定 LiveData LiveData
ビジネスロジック
(処理の中身=メソッド)の実装 ⇒ 監視プロパティの値変更
ViewとViewModel間のやり取りをレイアウトファイル に記述するのでソースファイルでのコーディング不要に!
DataBindingsの初期設定
①依存関係設定 ②ルートを「layout」に
②ViewModelクラスの結びつけ(dataに)
ビューの表示属性(text等)に
ViewModelの監視プロパティ設定
ビューのイベント属性(onClick等)にViewModelのメソッド設定 DataBindingsインスタンス取得
一旦ビルドしないとバインディングクラス使えない
(Fragmentの場合はonCreateViewで)
コード上でのViewModelとの結びつけ
① ViewModelインスタンス取得
② ②に①をセット
③ ②にLifeCycleOwnerをセット
監視プロパティの変更を レイアウトに反映させるため
ïBB1x 3
ïBB1x 3 ñó⇡òô ñó⇡òô fg b^ fg b^
Ω⌅,✓)8 Q⌅
Ω⌅,✓)8 Q⌅
ViewModel+LiveData+DataBinging
ViewModel(VM)はAndroidのフレームワーククラスを参照してはいけない
(VMはActivityよりライフサイクル長いのでメモリリーク引き起こす可能性 ⇒ ApplicationContextを持ちたい場合はAndroidViewModelを使う)
UIクラス(Activity/Fragment等:View)はなるべく小さく
(UIクラスの責務はデータの変更に伴うViewの更新とユーザー操作のViewModelへの伝達のみ)
データ処理に関するビジネスロジックはViewModelに記述
(ViewModelはUIクラス(View)とデータ(Model)との間のコネクター)
データの取得処理は、ViewModelに直接書かず、Repositoryを使う
(データソースが変わっても影響を受けずに済むように(VMから同じメソッドで呼び出せるように))
DataBindingを使う
(ViewとUIクラスの関係をクリーンに保ち、Viewの更新の関わるコーディングを最小限に出来る)
あらかじめ特定の役割を持ったアプリを構成する部品
自分で作ったコンポーネントはマニフェストファイルの
<application>タグ内への登録が必要
自分で作ったコンポーネントはマニフェストファイルの
<application>タグ内への登録が必要 Activity
Service Broadcast
Receiver Content Provider
1つのUIで1つの画面を表すもの
(1つのUIに複数の画面は持てない)
バックグラウンドでの処理
(UIはない=見えない)
システム全体へのアナウンス処理 [全体放送]
(設定情報変更・特定のイベント発生等)
端末内の他のアプリとのデータ共有
(FileProviderはここから派生)
Intentに よって起動
Intentに よって起動
É4¢/BH ¢
É4¢/BH ¢ ⌘✓◆⇡ ⌘✓◆⇡
æÄk ø=¿ ,h¡¬p√r
æÄk ø=¿ ,h¡¬p√r
使い方2ステップ
Applicationクラスを継承した自作クラスの作成
マニフェストファイルの”application”タグに①で作成し たクラスを設定
Androidアプリが起動したときに作成され、呼び出されるクラス
アプリが立ち上がるときに行いたい設定や、アプリ 内で共有したいデータを設定するときに使える
<application android name = “ .作成した自作クラス名”>
<application android name = “ .作成した自作クラス名”>
ɧ§x H Ñ05H B4
ɧ§x H Ñ05H B4 8)⌅%& 8)⌅%&
何もせずに、データを保持するためだけのクラス
"=O8)⌅
"=O8)⌅ -¢050EÑx -¢050EÑx 0WW6 0WW6
data class User (val name: String, val age: Int)
class宣言の頭に
「data」をつける
引数を最低1つ持つ
プライマリコンストラクタ引数は「val」か「var」
で宣言してしまう
⇒ 宣言の文無し!
データクラスが最初から持っているメソッド
equals(other: Any): Boolean データインスタンス(User)が他のオブジェクトと等しいかどうか判定 hashCode(): Int データインスタンス(User)がハッシュ値(各オブジェクトに割り
振った識別番号(=ID)を取得
toString(): String ”User(name=John, age=42)”形式の文字列に変換
to(that: B): Pair<User
, B>
データインスタンス(User)と他のクラス(B)とをペア付け copy(変更したい引数): User データインスタンス(User)内の一部のデータだけを変更var
val strName
(小文字から)クラスの種類(=型) :String
省略可(型推論)
= “きーぼー”
中身を途中で変える場合
(「=」を2回以上使う場合)
中身を途中で変えない場合
⇒ なるべくこっちを使う
▶ 基本データ型 ⇒ 値そのもの
▶ いわゆるクラス ⇒ インスタンス
K otlinは空っぽ(null)がキライ!!
■ どーしても今決められない場合 ⇒ 先頭に「lateinit」をつける
■ どーしても空っぽ(null)が入る場合 ⇒ 「型」のうしろに「?」をつける ■ どーしても今決められない場合 ⇒ 先頭に「lateinit」をつける
■ どーしても空っぽ(null)が入る場合 ⇒ 「型」のうしろに「?」をつける
(オブジェクト) 変数
8)⌅&ƒ≈⌦^a∆ «\⇤J%
8)⌅&ƒ≈⌦^a∆ «\⇤J%
Ic %⌥T»⇤Jh… ⇡Àr
Ic %⌥T»⇤Jh… ⇡Àr
インスタンスを経由せずにアクセスできるオブジェクト=静的オブジェクト
クラス内で「companion object」として設定
クラス外でトップレベルプロパティ・関数として設定
「クラス名(≠インスタンス名). オブジェクト名」でアクセス
「オブジェクト名」でダイレクトにアクセス可能
Javaでいうところの「static」
Kotlinにしか出来ない芸当
Ã⇡8)⌅qèÕl$ ⌅O ⌅Œœì[
Ã⇡8)⌅qèÕl$ ⌅O ⌅Œœì[
–t ¥8, ⌘8—⌅ ⌦⇡K“
–t ¥8, ⌘8—⌅ ⌦⇡K“
「オブジェクト=インスタンス=実体」⇒ 全部同じ意味
「オブジェクト=インスタンス=実体」⇒ 全部同じ意味
■ クラス(class): 実体を生み出すための金型(何度も生成できる)
■ オブジェクト(object):すでに生み出された実体(1度しか生成できない)
ヒト(class)はいくらでも 生み出すことができるけど
(object)はこの世にたった 一人しか存在しえない
みんな同じ「ヒト」だが 名前が違う
(別インスタンス)
8)⌅ 8)⌅ -Ñx -Ñx 0WW6 0WW6 %–t ¥8, %–t ¥8, -Bµ” -Bµ” 3Ñ56 3Ñ56 ⇡‘J ⇡‘J
onCreate onResume
onPause onDestroy
(オン・クリエイト)
(オン・レジューム)
(オン・ポーズ)
(オン・デストロイ)
・・・画面を立ち上げる時(1回だけ)
・・・画面を閉じる時(1回だけ)
画面が見える
画面が見えない
画面が 開いている
状態
開いている≠見える Androidでは
画面は1つしか 見ることができない
Androidでは 画面は1つしか
見ることができない’÷h ’÷h ÉÑ5H ÉÑ5H GH GH 5A 5A r⇡)$(7$8⌃ r⇡)$(7$8⌃
キーと値のペアでちょっとしたデータの読み取りと書き込みが出来るクラス
データの書き込み
val pref = context.getSharedPreferences(
“データを保存するファイル名”, Context.MODE_PRIVATE) val editor = pref.edit()
editor.putString(
“キーの名前”, userName) editor.commit()
データの読み取り
val pref = context.getSharedPreferences(
“データを保存するファイル名”, Context.MODE_PRIVATE) Val defaultUserName =
“名無しの権兵衛”val userName = pref.getString(
“キーの名前”,defaultUserName)
Contextを継承しているクラスで使う場合は「this」
「パッケージ名+ファイル名」⇒ 一意で識別できるように
ファイルは共有ファイル扱いなので アクセスは自アプリからだけに制限
データが存在しない場合の デフォルト値を設定しておく
◊ÿ`%U⌫"=O≤Ÿ
◊ÿ`%U⌫"=O≤Ÿ
-™´0/3¢•/3ß3/34Ñ3W6
-™´0/3¢•/3ß3/34Ñ3W6
「DialogFragment(サポートライブラリベース」
を継承したAlertDialogFragmentクラスの作成
使い方5ステップ
setPositiveButton() / setNegativeButton()の処理の中身
(ラムダ式の中)はコールバックメソッドの呼び出し
呼び出し元で実装するコールバックメソッドを
定義したインターフェースの作成
onCreateDialogメソッドをオーバーライドし て、AlertDialogを作成(基礎編の5ステップ)
onAttach()をオーバーライドしてインタフェー スリスナーを設定
呼び出し元のActivityでAlertDialogFragmentを
表示させ(show())、コールバックメソッドを実装
DialogFragmentで作成したDialogをActivityから呼び出す(ボタン押下処理も)DialogFragment作成段階ではcreate()にしておく onPositiveButtonClicked() / onNegativeButtonClicked()
MH 0x B1./012345
MH 0x B1./012345 I`⌫ I`⌫
Éx 3/5MH 0x B1
Éx 3/5MH 0x B1 ⇡ XK ⇡ XK -@H -@H G3M050 G3M050 ⁄I€S= ‹ 6 ⁄I€S= ‹ 6
apply / also / let / run
apply / also / let / run用途 関数 呼び出し元
へのアクセス 戻り値
オブジェクト の初期化
apply this
呼び出し元自身(this)also it
呼び出し元自身(it)NULL
(空っぽ)
チェック
let it
let関数の実行結果run this
run関数の実行結果自分で指定できる自分で指定できない
■ this : 呼び出し元のレシーバーそのもの
■ it : 呼び出し元のレシーバーが渡されたラムダ式の唯一の引数it ■ this : 呼び出し元のレシーバーそのもの
■ it : 呼び出し元のレシーバーが渡されたラムダ式の唯一の引数it
›`fiXfl‡ ⌅k=✓ä · ⇡IJå^
›`fiXfl‡ ⌅k=✓ä · ⇡IJå^
既存のクラスを継承せずにメソッド(関数)を追加できる機能
fun 既存のクラスの型.追加するメソッド名(引数):戻り値の型{処理の中身}
val soundPool = SoundPool(・・・)
soundPool.play(soundId1, 1.0f, 1.0f, 0, 0, 1.0f ) soundPool.play(soundId2, 1.0f, 1.0f, 0, 0, 1.0f ) soundPool.play(soundId3, 1.0f, 1.0f, 0, 0, 1.0f ) soundPool.play(soundId4, 1.0f, 1.0f, 0, 0, 1.0f )
fun SoundPool.play2 (soundId: Int) {
this .play(soundId, 1.0f, 1.0f, 0, 0, 1.0f ) }
soundPool. play2 (soundId1) soundPool. play2 (soundId2) soundPool. play2 (soundId3)
このおまじないを打つのが面倒! 余計なコーディングがなくなってスッキリ!
他のクラスのメンバをあたかも自分のクラスの メンバであるかのようにアクセスできる機能 他のクラスのメンバをあたかも自分のクラスの
メンバであるかのようにアクセスできる機能
レシーバ
(this)
レシーバ
(this)
‚„ ä ·
‚„ ä · -3¨534WH -3¨534WH B4EßC4Ñ5H B4EßC4Ñ5H B4W6 B4W6 %& %&
ListViewを進化させた大きなデータセットを表示するためのコンテナ
Dataset
Adapter ViewHolder
RecyclerView
x行目のデータを 表示されるで~
リスト1行分のView
表示させる段階になってからViewを生成させ、キャッシュに貯めて使いまわす
⇒ 少ないメモリで大きなデータを効率的に表示させる工夫
表示させる段階になってからViewを生成させ、キャッシュに貯めて使いまわす
⇒ 少ないメモリで大きなデータを効率的に表示させる工夫
LayoutManager
リストとデータのつなぎ役
表示の位置調整 画面に表示されるリスト
x行目のデータ 取ってくるわ~
取ってきたデータ からView作るわ~
作ったView送るから リストに表示して~
表示が終わったら キャッシュにためといて
使いまわすわ~
⇢ ⇢ £3ÑAÑx £3ÑAÑx 3/°H 3/°H 3N 3N !%&' !%&'
使い方8ステップ
リストの位置調整を行うLayoutManagerの設定 リストとデータのつなぎ役であるAdapterの設定
(RecyclerView.Adapterを継承したクラスの作成)
リスト1行分のViewを保持するViewHolderの設定
(RecyclerView.ViewHolderを継承・②で作ったAdapterクラスの内部クラスに)
ViewHolder用(リスト1行分)のレイアウトファイルの作成
(CardViewを使うことが多い)
ViewHolderの生成
(②で作ったAdapterクラス内でonCreateViewHolderメソッドをオーバーライド)
ViewHolderの中身のViewの設定 ⇒ ここでリスト1行分のViewが作られる
(②で作ったAdapterクラス内でonBindViewHolderメソッドをオーバーライド)
Adapterが保持しているリストの行数の取得 ⇒ LayoutManagerに伝える
(②で作ったAdapterクラス内でgetItemCountメソッドをオーバーライド)
RecyclerViewにAdapterをセット
⇢ ⇢ £3ÑAÑx £3ÑAÑx 3/°H 3/°H 3N 3N !⇡IJK !⇡IJK
Adapterクラスの 実装必須メソッド3つ
アプリ外(URL)からの画像取得処理を効率化してくれるライブラリ
val url = URL(“http:// ・・・ ” )
val inputStream = url.openStream() val bitmap = BitmapFactory
.decodeStream(inputStream) imageView = setImageBitmap(bitmap) inputStream.close
Glide.with(this)
.load(“http:// ” ・・ ) .into(imageView)
通常の画像取得処理 Glideを使うと
①:取得したい画像のURLを指定
②:InputStreamで画像を読み込み
③:読み込んだ画像をビットマップに変換(デコード)
④:ImageViewに画像をセット
⑤:InputStreamを閉じる
5つもの工程をたった1文でやってくれる(+メモリ管理までやってくれる)
5つもの工程をたった1文でやってくれる(+メモリ管理までやってくれる)
’ ‰Â ~ Ê ~)$t)◆⇢
’ ‰Â ~ Ê ~)$t)◆⇢ ïx ïx H H ¢3 ¢3 ! !
完成図
(レイアウトファイル)
に貼り付けたビューと その属性(プロパティ)は、アプリが開いたときに有効となり、ずっと変わらない
静的
アプリ
(プログラム)
を動かしている途中で変えたい場合、説明書
(kotlinファイル)
で設定する動的
【動的にビューを表示(非表示)】
⇒ View#visibility = View.VISIBLE (View.INVISIBLE) 【動的にビューを使える(使えない)】
⇒ View#isEnable = true (false) 【動的にビューを表示(非表示)】
⇒ View#visibility = View.VISIBLE (View.INVISIBLE) 【動的にビューを使える(使えない)】
⇒ View#isEnable = true (false)
✓<*) Á ëqUaJ Ë ∆Õ
✓<*) Á ëqUaJ Ë ∆Õ ÈÍ =h
ÈÍ =h °H °H 3N 3N r rÎ Î
一定時間(間隔)を開けてから行う処理のこと 使い方3ステップ
タイマー処理を行うクラス:Timerの準備
(インスタンス化)タイマー処理を実行する
使い終わったTimerをキャンセルする (Timer#cansel)
Timer#
schedule (最初の処理までの間隔, 2回目以降の処理間隔, {
●●} )
■ Timerはキャンセルしたら再利用できない(もう一度①から) ■ Timerは一度動かすと一時停止・再開ができない
■ Timerはキャンセルしたら再利用できない(もう一度①から) ■ Timerは一度動かすと一時停止・再開ができない
処理を何度も繰り返す場合
タイマー処理の中身
(ラムダ式)
タイマー処理の中身
(ラムダ式)
「for Timer in kotlin.concurrent」の方を使う
O$✏= ÏÌ %&' Ó
O$✏= ÏÌ %&' Ó Ô Ô H H 23/ 23/
Androidは処理を流すベルトコンベア(スレッド)が1本しかない
タイマー処理用に別のベルトコンベアを作る
文字を出す メインスレッド(UIスレッド)
メインスレッド(UIスレッド)
サブスレッド(バックグラウンドスレッド)
計算する
タイマー処理
画像を出す 音を出す 文字を出す
画像を出す 音を出す 音を出す
文字を出す
タイマー処理
文字を出す 画像を出す 音を出す 文字を出す 画像を出す 音を出す
文字を出す 画像を出す 音を出す 文字を出す
別のベルトコンベア(スレッド)で 処理をするためのクラス
⇒正確には「インターフェース」 Runnable
サブスレッドでは画面に何かを表示する処理はできない サブスレッドでは画面に何かを表示する処理はできない
O$✏= ÏÌ %&' Ó
O$✏= ÏÌ %&' Ó Ô Ô H H 23/ 23/
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::tobeSynchronized )
ナゾを解く3つのカギ
Kotlinでは関数の引数に関数を 持つことができる(高階関数)
引数となる関数はラムダ式(名前 のない関数)として直接処理を書 くことができる
val result = lock(lock,
{ sharedResource.operation() } )
最後の引数が関数の場合、Kotlin では()の外に出す
「::」+引数とする関数名
val result = lock(lock )
{ sharedResource.operation() }
関数名の直後の{}は引数となっている関数の中身!!
関数名の直後の{}は引数となっている関数の中身!!
ä · ƒ⇡ ÒÚ ⌥
ä · ƒ⇡ ÒÚ ⌥ -6 -6 Õ&⇤fi Õ&⇤fi
ÛÙ ⇤`aJ ∫ ı ˆ fi
ÛÙ ⇤`aJ ∫ ı ˆ fi
名前を付けずに(=宣言をせずに)、処理の中身を直接記述した関数
【普通の関数】fun 関数名(引数:型):戻り値の型 { 処理の中身 }
fun addCalc (a:Int, b:Int): Int { return a + b}
【ラムダ式】 { 引数:型 -> 戻り値の型 処理の中身 }
{a:Int, b:Int -> Int a + b}
▶ 引数、戻り値の型は省略できる
▶ 引数が1つだけの場合は暗黙の引数「it」が使える
① 関数の引数として
② 関数型インターフェースの実装(clickListener等)
① 関数の引数として
② 関数型インターフェースの実装(clickListener等)
2つの使い道
メソッドが1つだけのインターフェース
) Á˜¯˘ ƒ≈⇡⇤Jä ·
) Á˜¯˘ ƒ≈⇡⇤Jä · - - +j R +j R 6 6
バックグラウンドでの処理(UIはない=見えない ⇔ Activity)
アプリの4大構成要素(コンポーネント)の1つ
■ 別のアプリを使っている間にバックグラウンドで音楽再生
■ 別のActivityを操作している間にネットワークからデータを取得 など ⇒ 一度起動すると処理が終了するまで動作が継続する(他人に邪魔されない)
開始されたサービス
バインドされたサービス
startService()で起動するサービス
実装は簡単だが、Activityからの操作は サービスの起動と停止しか出来ない
bindService()で起動するサービス
実装は複雑だが、サービスの起動・停止 に加え、Activity・Service間の相互操 作も出来る
簡単だけど小機能
難しいけど多機能
中断されると問題のある 処理の実装に向いている
サービスはバックグラウンド処理ではあるが、実行はメインスレッドで行われる サービスはバックグラウンド処理ではあるが、実行はメインスレッドで行われる
™3/GH Ñ3
™3/GH Ñ3 h7= h7= È È ⌅r%&' ⌅r%&'
■ プロセス = 実行中のプログラム(1アプリに1プロセス ⇒ 他のアプリから独立)
■ スレッド = 処理を流すためのベルトコンベア(基本は1本:シングルスレッド)
OSがCPUに割り当てる最小の実行単位=関数 (Thread of Execution)
アプリ同士のやり取りは出来ず、1度に実行できる処理(関数)は1つだけ
■ アプリ間でやり取りをしたい場合 ⇒ プロセス間通信(IPC)を使う
■ 一度に複数の処理を実行したい場合 ⇒ 別スレッド(ワーカースレッド)を立てる ■ アプリ間でやり取りをしたい場合 ⇒ プロセス間通信(IPC)を使う
■ 一度に複数の処理を実行したい場合 ⇒ 別スレッド(ワーカースレッド)を立てる
アプリA用 のプロセス
アプリB用 のプロセス
アプリC用 のプロセス
Androidシステム 計算する
時間の かかる処理 文字を出す
画像を出す 音を出す サービス
時間のかかる処理
別スレッド(ワーカースレッド) メインスレッド(UIスレッド)
時間のかかる処理を 行う場合に作成
⇒ 画面は操作できない 普段はこれしかない
(サービスもここで実行)
全体のメモリ管理 プロセス間(アプリ間)
のやり取り×
⇢✓<—⌅!%⇢⌅ ˙ R!
⇢✓<—⌅!%⇢⌅ ˙ R!
Activityが閉じられても続く処理(中断されると問題のある処理)か?
Service内の メインスレッドで実行
YES
Service内の
別スレッドで実行集中的な作業や
ブロック操作を行う処理か?
ネットワークへのアクセス・DBへの問い合わせ等
NO
Activity内の
別スレッドで実行 Activity内の
メインスレッドで実行
YES
集中的な作業や
ブロック操作を行う処理か?
ネットワークへのアクセス・DBへの問い合わせ等
NO
YES NO
Service IntentService
⇢7= È ⌅!%⇢ ˚ ⌅ ˙ R!⇡IJå^
⇢7= È ⌅!%⇢ ˚ ⌅ ˙ R!⇡IJå^
開始されたサービス バインドされたサービス
startService()呼び出し直後に 呼ばれるコールバック
⇒ Serviceの開始
bindService()呼び出し直後に 呼ばれるコールバック
⇒ IBinderの取得
自分で止めるか(stopSelf)
誰かに止めてもらう(stopService)まで動き続ける 自分で止めるか(stopSelf)
誰かに止めてもらう(stopService)まで動き続ける サービスがバインドされている間だけ実行
(unBindされたら自動的に破棄)
サービスがバインドされている間だけ実行
(unBindされたら自動的に破棄)
画面がないのでライフサイクルは単純
™3/GH Ñ3
™3/GH Ñ3 ⇡)$(7$8⌃ ⇡)$(7$8⌃
startService()によって起動する簡単なサービス(開始・停止しか出来ない)
■ 一旦開始されると原則意図的に止めない限り止まらない
■ 呼び出し元に結果を返さない
Androidシステム
Activity
Service
onStartCommand() onStartCommand()
サービスの開始 startService()
サービスの開始 startService()Intent Intent
Intent Intent
サービスの停止 stopService()
サービスの停止 stopService()onDestroy() onDestroy()
呼び出し元に 結果を返さない
⇢ ¸˝ í\⌫7= È ⌅
⇢ ¸˝ í\⌫7= È ⌅ -™3/GH -™3/GH Ñ36 Ñ36 !%& !%&
bindService()によって起動する複雑なサービス
■ サービスがバインドされている間だけ実行(バインドが外れたら自動的に破棄)
■ 開始・停止だけでなく、Activity・Service間の相互操作、呼び出し元に結果も返せる
Binderクラスの拡張
サービスが他のアプリ(プロセス)で使われているか?
(自分のアプリから自分以外のアプリのServiceを使いたいケースか?)
Messengerの利用 AIDLの利用
NO 別スレッド(マルチスレッド化)が必要か?
YES
NO YES
プロセス間通信
(IPC)
他のアプリ(プロセス) からの複数の要求を 同時に処理する必要があるか?
複雑なので Google先生は おすすめしていない
⇢S$ Rí\⌫7= È ⌅
⇢S$ Rí\⌫7= È ⌅ -™3/GH -™3/GH Ñ36 Ñ36 ! !
短い音と長い音では何が違うのか?
短い音
(sound)
長い音
(music)
使う道具(クラス)
SoundPool MediaPlayer
ファイルの大きさ
小さい 大きい
使い方
メモリにロード ストリーミング
(メモリにロードしない)
É4¢/BH ¢
É4¢/BH ¢ Õ Õ ˛ ˛ T ⌫⇠⇡⌦⇡K“ T ⌫⇠⇡⌦⇡K“
「MediaPlayer」より高機能なBGM・ビデオ再生用ライブラリ
⇢ ⇢ ˇ ˇ ¨B•x0A3/ ¨B•x0A3/ ! % & ' ! % & '
ExoPlayerがMediaPlayerより高機能な点
BGMのループ再生を途切れずに(シームレスに)やってくれる
「Dynamic Adaptive Streaming over HTTP(DASH)」や
「Smooth Streaming」といった高度なストリーミング技術に対応
■
実装が簡単だが低機能な「MediaPlayer」
(Androidフレームワーク内)■
実装は複雑だが高機能な「ExoPlayer」
(オープンソース)■
実装が簡単だが低機能な「MediaPlayer」 (Androidフレームワーク内)
■
実装は複雑だが高機能な「ExoPlayer」 (オープンソース)
単体のライブラリなので自分でバージョンコントロール・開発者 による拡張・修正が可能
(MediaPlayerはAndroidフレームワーク(SDK)に組み込まれており出来ない)
サポートされているファイルフォーマットが多い など
ˇ ¨B•x 0A3/
ˇ ¨B•x 0A3/ Õ Õ /3W /3W ( ⌃ ( ⌃ ˜ ˜ «\⌫ «\⌫ Dï† Dï†
⇥ óíì Ø ⇤⌅⇧ ⇤K“
⇥ óíì Ø ⇤⌅⇧ ⇤K“
使い方9ステップ
ExoPlayerのライブラリをセット(build.gradleに依存関係設定)
「SimpleExoPlayer」のインスタンスを作成(初期化)
BGMを読み込むための「RawResourceDataSource」の設定
=> 「DataSource」は「InputStream」的なもの(BGMデータを読み込むためのクラス)
resフォルダ内のBGMのリソースIDをUriスキームに変換
=> ExoPlayerでの再生対象は全て一旦Uriスキームに変換される(rawresource://resourceId)
④で設定したUriから再生する部分(DataSpec)を設定し、
③で設定した「RawResourceDataSource」を開く
③で設定した「RawResourceDataSource」インスタンスを返す DataSource.Factoryの設定
ExoPlayerの再生対象となる「MediaSource」インスタンスの設定
=> DataSource.Factory(⑥で設定)・ExtractorFactory・Uri(④で設定)を使う
②で設定したSimpleExoPlayerに⑦で設定したMediaSourceを設定し再生
=> setPlayWhenReady(true):再生、setPlayWhenReady(false):一時停止、ループはsetRepeatMode()
使い終わったらSimpleExoPlayerインスタンスの破棄(release)+
RawResourceDataSourceを閉じる(close)
ˇ ¨B•x 0A3/
ˇ ¨B•x 0A3/ Õ Õ /3W /3W ( ⌃ ( ⌃ ˜ ˜ «\⌫ «\⌫ Dï† Dï†
⇥ óíì Ø ⇤⌅⇧ ⇤K“
⇥ óíì Ø ⇤⌅⇧ ⇤K“
simpleExoPlayer.apply{
prepare(mediaSource)
repeatMode = REPEAT_MODE_ALL playWhenReady = true
}
val mediaSource =
ExtractorMediaSource
.Factory(dataSourceFactory)
.setExtractorFactory(MP3Extractor.FACTORY)
.createMediaSource(dataSourceUri)
val dataSourceUri = RawResource
.buildRawResourceUri (BGM
のリソースID )
val dataSourceFactory = DataSource.Factory{
return rawResourceDataSource
} val rawResourceDataSource
= RawResourceDataSource(this)
rawResourceDataSource .open(dataSpec) val dataSpec
= DataSpec(dataSourceUri)
■ File:ファイルの保存先の絶対パス
■ Uri : スキームが付与された特定の書式に変換されたファイルのパス
’leスキーム contentスキーム
Uri.fromFile()で取得可能
FileProvider(ContentProvider) で保存されたファイルのみ取得可能
他のアプリとの共有ファイルへは
「contentスキームのUri」経由でないとアクセスできない 他のアプリとの共有ファイルへは
「contentスキームのUri」経由でないとアクセスできない
File
/storage/emulated/0/Pictures/FOOTPRINT/image.jpgUri
Çleスキーム
Çle:///storage/emulated/0/Pictures/FOOTPRINT/image.jpgcontent スキーム
content:// パッケージ名 .Çleprovider/external_Çles/
Pictures/FOOTPRINT/image.jpg
Uniform Resourse Identi’ers
APIレベル24(Android7.0)以降は原則使用不可
É4¢/BH ¢
É4¢/BH ¢ b^ ( b^ ( ⌃ ⌃ $⌃⇡ $⌃⇡ ⌥ ⌥ J J
h h .H .H x x 3 3 % % Ü/H Ü/H r r
ファイルを少しずつ(バイト単位で)読み込むための仕組み [Stream=小川]
一見きれいな画像も 実はただの数字の羅列!!
InputStream
画像全体を一度に取り込むと
メモリが足りなくなるので
バイト単位で少しずつ取り込む 1ビット(bit): 1桁の2進数1バイト(byte)=8ビット
ビットマップ にデコード
(出典:CODINGEEK)
á 4§C5™5/302
á 4§C5™5/302 %& %& - - S$,⌅,◆= S$,⌅,◆= Á Á 6 6
効果音ファイルをrawフォルダに入れる
効果音ファイルをメモリにロードして
サウンドID(整数)を取得する
使い終わった音ファイルをメモリの上から後片付け
完成図
layout説明書
kotlin
resフォルダの下に自分で作る
サウンドIDを使って効果音を鳴らす
loadメソッド loadメソッド
playメソッド playメソッド
releaseメソッド releaseメソッド
効果音を出すためのクラス:SoundPoolを準備する
(インスタンス化)
(≠リソースID)
onResumeに onResumeに
onPauseに onPauseに
⌦ ˛
⌦ ˛ -™BC4¢6 -™BC4¢6 T ⌫⇠⇡ T ⌫⇠⇡ ↵ ↵ ⌅ ⌅ ✓ ✓
™33∑D0/
™33∑D0/ ëqUa ëqUa ˛ ˛ K“ K“
h ✏⇣ ⇡ ⌘ ◆ Í = Á k ,<=⌃%◆ 8íì K“r h ✏⇣ ⇡ ⌘ ◆ Í = Á k ,<=⌃%◆ 8íì K“r
使い方6ステップ
Context経由でAudioManagerインスタンス取得 音楽再生時
(AudioManager.STREAM_MUSIC)の
端末の最大ボリューム値の取得SeekBarにsetOnSeekBarListernerをセット
③の引数のラムダ式内で
onProgressChangedメソッドを実装 し、SeekBarの現在値(progress)を取得
④で取得したprogressを②で取得した端末の 最大ボリューム値で換算