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

Android AsyncTask アンチパターン

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Android AsyncTask アンチパターン

Avatar for Hiroshi Kurokawa

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 の利用も検討する