Slide 1

Slide 1 text

Kotlin コルー チンで作ってみる画像ロー ダ Yoshihiro Wada(@e10dokup) Potatotips #43 @ 2017/8/28

Slide 2

Slide 2 text

自己紹介みたいな Yoshihiro Wada いろんなSNS のアカウントが @e10dokup CyberAgent, Inc / AdtechStudio / AI Messenger( 新卒) ライブラリのある開発がしたい

Slide 3

Slide 3 text

今日話すこと Kotlin コルー チンでPicasso っぽい感じの画像ロー ダを作ってみる OkHttp とコルー チンで車輪の再発明をします 今日話さないこと ちゃんと画像を扱う上でのお話 キャッシュとか画像のシュリンクとか

Slide 4

Slide 4 text

こういう感じのものを作る 画像ギャラリー っぽく多数の画像をインター ネットからロー ドする 表示時の画像は B i t m a p F a c t o r y . O p t i o n s で縮小する I m a g e L o a d e r . w i t h ( c o n t e x t ) . l o a d ( u r l ) . i n t o ( i m a g e V i e w ) みたいな感じでImageView に ロー ドさせたい

Slide 5

Slide 5 text

とりあえず画像ロー ド以外… c l a s s I m a g e L o a d e r p r i v a t e c o n s t r u c t o r ( p r i v a t e v a l c o n t e x t : C o n t e x t ) { p r i v a t e v a r u r l : S t r i n g ? = n u l l c o m p a n i o n o b j e c t { f u n w i t h ( c o n t e x t : C o n t e x t ) : I m a g e L o a d e r { r e t u r n I m a g e L o a d e r ( c o n t e x t ) } } f u n l o a d ( u r l : S t r i n g ) : I m a g e L o a d e r { t h i s . u r l = u r l r e t u r n t h i s } f u n i n t o ( t a r g e t : I m a g e V i e w ) { / / ここで画像ロー ド } }

Slide 6

Slide 6 text

画像ロー ドで考える処理( 一応) 非同期で行う処理 画像のダウンロー ド(OkHttp でenqueue したら大丈夫) 画像の縮小 画像のキャッシュとか UI スレッドで行う処理 画像のImageView への反映 View のサイズの変更とか

Slide 7

Slide 7 text

いつも通りのロー ド f u n i n t o ( t a r g e t : I m a g e V i e w ) { / / R e q u e s t の生成は省略… c l i e n t . n e w C a l l ( r e q u e s t ) . e n q u e u e ( o b j e c t : C a l l b a c k { o v e r r i d e f u n o n R e s p o n s e ( c a l l : C a l l ? , r e s p o n s e : R e s p o n s e ? ) { v a l b o d y = r e s p o n s e ? . b o d y ( ) b o d y ? : r e t u r n / / 取得した画像バイト配列から縮小画像生成 v a l b y t e A r r a y = b o d y . b y t e s ( ) v a l o p t i o n s = g e t B i t m a p O p t i o n s ( b y t e A r r a y ) v a l b i t m a p = g e t S c a l e d B i t m a p ( t a r g e t , o p t i o n s , b y t e A r r a y ) / / 対象のI m a g e V i e w に画像を表示 ( c o n t e x t a s A c t i v i t y ) . r u n O n U i T h r e a d { t a r g e t . s e t I m a g e B i t m a p ( b i t m a p ) } } } ) }

Slide 8

Slide 8 text

思うこと ネストが深い。 つらい callback 記述 runOnUiThread ブロック 他の非同期処理を組み合わせるとコー ルバック地獄が… 正直この手の処理ならPromise(RxJava 等) とかで記述したい callback された結果を利用したり 複数のcallback の結果を待ったり

Slide 9

Slide 9 text

Coroutine を使ってみる( 導入) app/build.gradle d e p e n d e n c i e s { i m p l e m e n t a t i o n " o r g . j e t b r a i n s . k o t l i n x : k o t l i n x - c o r o u t i n e s - c o r e : 0 . 1 8 " i m p l e m e n t a t i o n " o r g . j e t b r a i n s . k o t l i n x : k o t l i n x - c o r o u t i n e s - a n d r o i d : 0 . 1 8 " } k o t l i n { e x p e r i m e n t a l { c o r o u t i n e s " e n a b l e " } }

Slide 10

Slide 10 text

ところでCoroutine って? 中断・ 再開可能な関数 Kotlin 1.1 でexperimental として実装。 Android 向けの実装もあるよ Kotlin のasync/await はCoroutine の機能の一部として実装されている

Slide 11

Slide 11 text

例えばこんなコー ドが f u n r e q u e s t I t e m s ( ) { i t e m A p i . g e t I t e m s ( o b j e c t : C a l l b a c k { o v e r r i d e f u n o n S u c c e s s ( i t e m : I t e m ) { t e x t V i e w . t e x t = i t e m . n a m e } } ) }

Slide 12

Slide 12 text

こんな感じになる f u n r e q u e s t I t e m s ( ) { v a l j o b = l a u n c h ( U I ) { / / 一旦ここで関数の実行がi t e m A p i . g e t I t e m s ( ) が完了するまで止まる v a l i t e m = a s y n c ( C o m m o n P o o l ) { i t e m A p i . g e t I t e m s ( ) } . a w a i t ( ) t e x t V i e w . t e x t = i t e m . n a m e } }

Slide 13

Slide 13 text

API クラスに分けて c l a s s I m a g e A p i { p r i v a t e v a l c l i e n t = O k H t t p C l i e n t ( ) f u n g e t I m a g e ( u r l : S t r i n g ? ) : R e s p o n s e B o d y { v a l r e q u e s t = R e q u e s t . B u i l d e r ( ) . u r l ( u r l ) . b u i l d ( ) v a l b o d y = c l i e n t . n e w C a l l ( r e q u e s t ) . e x e c u t e ( ) / / a s y n c させるしe x e c u t e で良さそう? . b o d y ( ) b o d y ? : t h r o w E x c e p t i o n ( " R e s p o n s e B o d y i s n u l l " ) r e t u r n b o d y } }

Slide 14

Slide 14 text

async/await させてみる f u n i n t o ( t a r g e t : I m a g e V i e w ) { v a l j o b = l a u n c h ( U I ) { v a l b y t e A r r a y = a s y n c ( C o m m o n P o o l ) { I m a g e A p i ( ) . g e t I m a g e ( u r l ) . b y t e s ( ) } . a w a i t ( ) v a l o p t i o n s = a s y n c ( C o m m o n P o o l ) { g e t B i t m a p O p t i o n s ( b y t e A r r a y ) } . a w a i t ( ) v a l b i t m a p = a s y n c ( C o m m o n P o o l ) { g e t S c a l e d B i t m a p ( t a r g e t , o p t i o n s , b y t e A r r a y ) } . a w a i t ( ) t a r g e t . s e t I m a g e B i t m a p ( b i t m a p ) } }

Slide 15

Slide 15 text

これで良さそう…? ということはなかった ロー ドする画像が増えるとタイムアウトを起こす スレッドプー ルの中で実行するので大量に処理を実行させようとすると待ちが発生する…? ( 調査不足なので憶測) Call を e x e c u t e ( ) するとキュー イングされないし… ここはCall を e x e c u t e ( ) せずに e n q u e u e ( ) した結果を扱うようにしたほうが良さそう

Slide 16

Slide 16 text

Call の e x e c u t e ( ) をawait する拡張サスペンド関数を作る s u s p e n d f u n C a l l . a w a i t ( ) : R e s p o n s e B o d y { r e t u r n s u s p e n d C a n c e l l a b l e C o r o u t i n e { c o n t i n u a t i o n - > e n q u e u e ( o b j e c t : C a l l b a c k { o v e r r i d e f u n o n R e s p o n s e ( c a l l : C a l l , r e s p o n s e : R e s p o n s e ) { v a l r e s p o n s e B o d y = r e s p o n s e . b o d y ( ) i f ( r e s p o n s e B o d y = = n u l l ) { c o n t i n u a t i o n . r e s u m e W i t h E x c e p t i o n ( E x c e p t i o n ( " R e s p o n s e B o d y i s n u l l " ) ) } e l s e { c o n t i n u a t i o n . r e s u m e ( r e s p o n s e B o d y ) } } o v e r r i d e f u n o n F a i l u r e ( c a l l : C a l l , e : I O E x c e p t i o n ) { i f ( c o n t i n u a t i o n . i s C a n c e l l e d ) r e t u r n c o n t i n u a t i o n . r e s u m e W i t h E x c e p t i o n ( e ) } } ) } }

Slide 17

Slide 17 text

ImageApi のメソッドもCall を返すようにする c l a s s I m a g e A p i { p r i v a t e v a l c l i e n t = O k H t t p C l i e n t ( ) f u n g e t I m a g e ( u r l : S t r i n g ? ) : C a l l { v a l r e q u e s t = R e q u e s t . B u i l d e r ( ) . u r l ( u r l ) . b u i l d ( ) r e t u r n c l i e n t . n e w C a l l ( r e q u e s t ) } }

Slide 18

Slide 18 text

async/await するときに C a l l . a w a i t ( ) を呼ぶ f u n i n t o ( t a r g e t : I m a g e V i e w ) { v a l j o b = l a u n c h ( U I ) { v a l b y t e A r r a y = a s y n c ( C o m m o n P o o l ) { / / R e s p o n s e を待ってからバイト配列を取得 I m a g e A p i ( ) . g e t I m a g e ( u r l ) . a w a i t ( ) . b y t e s ( ) } . a w a i t ( ) v a l o p t i o n s = a s y n c ( C o m m o n P o o l ) { / / 結果からB i t m a p F a c t o r y . O p t i o n s を取得 g e t B i t m a p O p t i o n s ( b y t e A r r a y ) } . a w a i t ( ) v a l b i t m a p = a s y n c ( C o m m o n P o o l ) { / / 上2 つが終わったらB i t m a p を縮小 g e t S c a l e d B i t m a p ( t a r g e t , o p t i o n s , b y t e A r r a y ) } . a w a i t ( ) t a r g e t . s e t I m a g e B i t m a p ( b i t m a p . a w a i t ( ) ) / / b i t m a p が出力されたらt a r g e t に表示 } } r u n O n U i T h r e a d とかしなくていいのでcontext が不要になった

Slide 19

Slide 19 text

まとめ 明示的にUI スレッド/ サブスレッドでの動作と処理の完了待機が宣言できるので良い 非同期処理が簡潔に書ける Rx とも連携可能っぽい r x S i n g l e で S i n g l e < T > を返すコルー チンを作ってくれるみたい 標準ライブラリに早く入ってくれないかな