Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android AsyncTask アンチパターン

Android AsyncTask アンチパターン

Hiroshi Kurokawa

December 04, 2014
Tweet

More Decks by Hiroshi Kurokawa

Other Decks in Technology

Transcript

  1. ネタ元参考文献 Android の非同期処理についての解説。Thread や Looper か ら解説してあって勉強になる。 Processes and Threads

    | Android Developers Handling Runtime Changes | Android Developers Efficient Android Threading (Anders Goransson)
  2. 事前知識(おさらい) Android では View の操作を UI スレッド(メインスレッ ド)からしかできない 重い処理を UI

    スレッドで行うと、ユーザーからはアプリが 反応しないように見える 5秒以上応答がないと ANR (Application Not Responding) エ ラーが表示される 描画に関係しない重い処理はバックグラウンドで処理する
  3. AsyncTask とは http://developer.android.com/reference/android/os/AsyncTask.html バックグラウンド処理を行って結果を UI スレッドに渡すた めのクラス。 Thread や Handler

    のヘルパークラス。 基本的には数秒程度の処理を行うのに使うもので、それ以 上の処理を行いたい場合は、Executor などの使用が推奨さ れている。 非常によく使われている。というか、非同期処理は、 AsyncTask がむやみに使われている印象。
  4. AsyncTask の罠 (その1) @ O v e r r i

    d e p u b l i c v o i d o n C r e a t e ( B u n d l e s a v e d I n s t a n c e S t a t e ) { s u p e r . o n C r e a t e ( s a v e d I n s t a n c e S t a t e ) ; . . . n e w A s y n c T a s k < V o i d , S t r i n g , V o i d > ( ) { @ O v e r r i d e p r o t e c t e d v o i d o n P r e E x e c u t e ( ) { s u p e r . o n P r e E x e c u t e ( ) ; m P r o g r e s s D i a l o g . s h o w ( ) ; } @ O v e r r i d e p r o t e c t e d S t r i n g d o I n B a c k g r o u n d ( V o i d . . . v a l u e ) { t r y { T h r e a d . s l e e p ( 1 5 0 0 0 ) ; } c a t c h ( I n t e r r u p t e d E x c e p t i o n e ) { } r e t u r n " A s y n c T a s k D o n e " ; } @ O v e r r i d e p r o t e c t e d v o i d o n P o s t E x e c u t e ( S t r i n g r e s u l t ) { m P r o g r e s s D i a l o g . d i s m i s s ( ) ; m T e x t V i e w . s e t T e x t ( r e s u l t ) ; } } . e x e c u t e ( ) ;
  5. IllegalArgumentException 画面回転などで Configuration Chage が発生すると、Activity の再生成が行われる → その際に Fragment が

    detach されるので m P r o g r e s s D i a l o g . d i s m i s s ( ) で I l l e g a l A r g u m e n t E x c e p t i o n が発生する。 ウェブの記事では、画面固定を推奨しているものもあるが、 画面回転以外(キーボードの表示とか)にも Configuration Change は起き得るので、これではダメ。
  6. IllegalArgumentException の とりあえずの対処 o n D e s t r

    o y 時にダイアログを閉じて n u l l にしておく @ O v e r r i d e p u b l i c v o i d o n C r e a t e ( B u n d l e s a v e d I n s t a n c e S t a t e ) { . . . n e w A s y n c T a s k < V o i d , V o i d , S t r i n g > ( ) { . . . @ O v e r r i d e p r o t e c t e d v o i d o n P o s t E x e c u t e ( S t r i n g r e s u l t ) { i f ( m P r o g r e s s D i a l o g ! = n u l l ) { m P r o g r e s s D i a l o g . d i s m i s s ( ) ; } i f ( m T e x t V i e w ! = n u l l ) { m T e x t V i e w . s e t T e x t ( r e s u l t ) ; } } } . e x e c u t e ( ) ; } @ O v e r r i d e p u b l i c v o i d o n D e s t r o y ( ) { i f ( m P r o g r e s s D i a l o g ! = n u l l & & m P r o g r e s s D i a l o g . i s S h o w i n g ( ) ) { m P r o g r e s s D i a l o g . d i s m i s s ( ) ; } m P r o g r e s s D i a l o g = n u l l ; } というか、ガイドライン通り、ProgressDialog は使わない!
  7. 問題点 Configuration change が起きて、Activity が再生成になる と、AsyncTask の結果は捨てられることになる それが嫌なら、T h r

    e a d や A s y n c T a s k L o a d e r などを使っ て、再生成された Activity にスレッドをアタッチする
  8. AsyncTask の罠 (その2) F r a g m e n

    t # g e t A c t i v i t y ( ) が n u l l になることがある 画面回転などで Configuration Chage が発生すると、Fragment が detach されてしまう @ O v e r r i d e p u b l i c V i e w o n C r e a t e V i e w ( L a y o u t I n f l a t e r i n f l a t e r , V i e w G r o u p c o n t a i n e r , B u n d l e s a v e d I n s t a n c e S t a t e ) { . . . n e w A s y n c T a s k < V o i d , V o i d , S t r i n g [ ] > ( ) { . . . @ O v e r r i d e p r o t e c t e d v o i d o n P o s t E x e c u t e ( S t r i n g [ ] r e s u l t ) { m A d a p t e r = n e w A r r a y A d a p t e r < S t r i n g > ( g e t A c t i v i t y ( ) , R . l a y o u t . l i s t r o w , r e s u m L i s t V i e w . s e t A d a p t e r ( m A d a p t e r ) ; } } . e x e c u t e ( ) ; }
  9. NullPointerException の対処 F r a g m e n t

    # i s A d d e d ( ) で確認する あと、個人的には m A d a p t e r はコールバックで作らない方 が好き @ O v e r r i d e p u b l i c V i e w o n C r e a t e V i e w ( L a y o u t I n f l a t e r i n f l a t e r , V i e w G r o u p c o n t a i n e r , B u n d l e s a v e d I n s t a n c e S t a t e ) { . . . n e w A s y n c T a s k < V o i d , V o i d , S t r i n g [ ] > ( ) { . . . @ O v e r r i d e p r o t e c t e d v o i d o n P o s t E x e c u t e ( S t r i n g [ ] r e s u l t ) { i f ( i s A d d e d ( ) & & m A d a p t e r ! = n u l l ) { m A d a p t e r . a d d A l l ( A r r a y s . a s L i s t ( r e s u l t ) ) ; m A d a p t e r . n o t i f y D a t a s e t C h a n g e d ( ) ; } } } . e x e c u t e ( ) ; }
  10. AsyncTask の罠 (その3) 長い処理を行うとメモリリークの可能性がある t a s k = n

    e w A s y n c T a s k < V o i d , S t r i n g , V o i d > ( ) { . . . } . e x e c u t e ( ) ; non-static な内部クラスは暗黙に親オブジェクト(Activity オブジェクト)への参照を持っている AsyncTask が内部的に利用しているスレッドが生きている 限り、このオブジェクトは GC されない 出典: Efficient Android Threading
  11. メモリリークの対処法 non-static な内部クラスにせず、static な内部クラスにする か別クラスにする A c t i v

    i t y # o n D e s t o r y ( ) 時に Activity への参照を破棄し て A s y n c T a s k # c a n c e l ( ) を呼ぶ もしくは、Activity への参照を弱参照(Weak Reference) にする @ O v e r r i d e p u b l i c v o i d o n C r e a t e ( B u n d l e s a v e d I n s t a n c e S t a t e ) { . . . m T a s k = n e w M y T a s k ( t h i s ) ; m T a s k . e x e c u t e ( ) ; } @ O v e r r i d e p r o t e c t e d v o i d o n D e s t r o y ( ) { s u p e r . o n D e s t r o y ( ) ; m T a s k . s e t A c t i v i t y ( n u l l ) ; m T a s k . c a n c e l ( t r u e ) ; } p r i v a t e s t a t i c c l a s s M y T a s k e x t e n d s A s y n c T a s k < S t r i n g , B i t m a p , V o i d > { . . . }
  12. メモリリークの対処法(再び A s y n c T a s k

    # c a n c e l ( f a l s e ) は、i s C a n c e l e d をt r u e に セットして、実行中のタスクが終わるまで待機する。 A s y n c T a s k # c a n c e l ( t r u e ) にしても、即座にスレッドが 終了することを保証しない。 p r i v a t e s t a t i c c l a s s M y T a s k e x t e n d s A s y n c T a s k < V o i d , V o i d , S t r i n g > { @ O v e r r i d e p r o t e c t e d S t r i n g d o I n B a c k g r o u n d ( V o i d . . . p a r a m s ) { f o r ( i n t i = 0 ; i < N U M _ T A S K S ; i + + ) { i f ( t h i s . i s C a n c e l l e d ( ) ) { r e t u r n n u l l ; } e l s e { t r y { / / D o a n y t a s k } c a t c h ( I n t e r r u p t e d E x c e p t i o n i e x ) { / / t h e b l o c k i n g m e t h o d t h r o w s a n I n t e r r u p t e d E x c e p t i o n r e t u r n n u l l ; } } } r e t u r n " A s y n c T a s k D o n e " ; } }
  13. AsyncTask の罠 (その4) AsyncTask を複数実行したときに、逐次実行される (sequential)か、同時実行される(concurrent)かは、呼 び出し方、APIレベルで変わる 実行環境はアプリケーション全体で同一(ある Service の

    AsyncTask が別 Activity の AsyncTask をブロックしうる) API targetSdkVersion e x e c u t e e x e c u t e O n E x e c u t o r 1-3 Any Sequential Not available 4-10 Any Concurrent Not available 11-12 Any Concurrent Sequential/Concurrent (customizable) 13+ <13 Concurrent Sequential/Concurrent (customizable) 13+ ≥13 Sequential Sequential/Concurrent (customizable)
  14. API レベルに関わらず処理を 同じにするには 逐次実行 2.3系をサポートする限り無理です。A s y n c T

    a s k を諦めてく ださい。 同時実行 targetSdkVersion を < 13 にするか、API レベルによって処理 を変える必要があります。 i f ( B u i l d . V E R S I O N . S D K _ I N T < = B u i l d . V E R S I O N _ C O D E S . H O N E Y C O M B _ M R 1 ) { n e w M y A s y n c T a s k ( ) . e x e c u t e ( ) ; } e l s e { n e w M y A s y n c T a s k ( ) . e x e c u t e O n E x e c u t o r ( A s y n c T a s k . T H R E A D _ P O O L _ E X E C U T O R ) ; }
  15. まとめ オススメの A s y n c T a s

    k の使い方 軽い処理だけにしておく non-static な inner class にしない A c t i v i t y # o n D e s t r o y ( ) で、キャンセルと Activity の参 照の解放を忘れずに行う 場合に応じて A s y n c T a s k L o a d e r 、 E x e c u t o r 、 H a n d l e r T h r e a d の利用も検討する