Annotation Processing を使ってソースを生成する

Annotation Processing を使ってソースを生成する

979d93b360f80486b121486a9d063ad5?s=128

Hiroshi Kurokawa

December 17, 2014
Tweet

Transcript

  1. Annotation Processing を使 ってコンパイル時にソース を生成する 2014-12-17 Potatotips #12 黒川 洋

    / @hydrakecat
  2. ある Androider の悩み ( ^ω ^) ここに Android のコードがあるじゃろ? これをこうして...

    g e t L o a d e r M a n a g e r ( ) . i n i t L o a d e r ( L O A D E R _ I D _ 1 , n u l l , n e w L o a d e r C a l l b a c k s < D a t a > ( ) { @ O v e r r i d e p u b l i c L o a d e r < D a t a > o n C r e a t e L o a d e r ( i n t i d , B u n d l e a r g s ) { r e t u r n n e w D a t a L o a d e r ( ) ; } @ O v e r r i d e p u b l i c v o i d o n L o a d F i n i s h e d ( L o a d e r < D a t a > l o a d e r , D a t a d a t a ) { o n D a t a L o a d F i n i s h e d ( d a t a ) ; } . . . } ) ; g e t L o a d e r M a n a g e r ( ) . i n i t L o a d e r ( L O A D E R _ I D _ 2 , n u l l , n e w L o a d e r C a l l b a c k s < M e s s a g e L i s t > ( ) { @ O v e r r i d e p u b l i c L o a d e r < M e s s a g e L i s t > o n C r e a t e L o a d e r ( i n t i d , B u n d l e a r g s ) { r e t u r n n e w M e s s a g e L i s t L o a d e r ( ) ; } @ O v e r r i d e p u b l i c v o i d o n L o a d F i n i s h e d ( L o a d e r < M e s s a g e L i s t > l o a d e r , M e s s a g e L i s t d a t a ) { o n M e s s a g e L i s t L o a d F i n i s h e d ( d a t a ) } . . . } ) ;
  3. こうしたい! p r o t e c t e d

    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 ) ; M u l t i L o a d e r . i n j e c t ( t h i s , L O A D E R _ I D _ 1 , L O A D E R _ I D _ 2 ) ; } @ O n C r e a t e L o a d e r ( L O A D E R _ I D _ 1 ) p u b l i c L o a d e r < D a t a > o n C r e a t e D a t a L o a d e r ( i n t i d , B u n d l e a r g s ) { . . . } @ O n L o a d F i n i s h e d ( L O A D E R _ I D _ 1 ) p u b l i c v o i d o n D a t a L o a d F i n i s h e d ( L o a d e r < D a t a > l o a d e r , D a t a d a t a ) { . . . } @ O n C r e a t e L o a d e r ( L O A D E R _ I D _ 2 ) p u b l i c L o a d e r < M e s s a g e L i s t > o n C r e a t e M e s s a g e L i s t L o a d e r ( i n t i d , B u n d l e a r g s ) { . . . } @ O n L o a d F i n i s h e d ( L O A D E R _ I D _ 2 ) p u b l i c v o i d o n M e s s a g e L i s t L o a d F i n i s h e d ( L o a d e r < M e s s a g e L i s t > l o a d e r , M e s s a g e L i s t d a t a ) { . . .
  4. サンプル ということで、今回は annotation processing を使ってソース を自動生成する話をします。

  5. Annotation Processing アノテーションでアレコレやるには2通りの方法がある。 ランタイム時 リフレクションをバリバリ使う方法。それほど複雑にならな い(?)が、ランタイム時のオーバーヘッドが大きい。 コンパイル時 Annotation Processing を使う方法。コンパイル時にソースコ

    ードを生成するのでランタイム時のオーバーヘッドはない。 黒魔術。Jack & Jill は未サポート。
  6. Annotation Processing と言えば モダンな DI ライブラリは、コンパイル時にソースを生成して いる印象。というか、Jake の好みなだけなのかも。 Dagger Dagger2

    ButterKnife
  7. Annotation Processing Java SE 6 で導入されたアノテーションを javac で処理する仕 組み。J2SE 5.0

    では apt という非標準ツールがあった。 j a v a x . a n n o t a t i o n . p r o c e s s i n g . A b s t r a c t P r o c e s s o r を継承したクラスを定義して、 p a c k a g e c o m . h k u r o k a w a . m u l t i l o a d e r . i n t e r n a l ; p u b l i c c l a s s M u l t i L o a d e r P r o c e s s o r e x t e n d s A b s t r a c t P r o c e s s o r { @ O v e r r i d e p u b l i c b o o l e a n p r o c e s s ( S e t < ? e x t e n d s T y p e E l e m e n t > a n n o t a t i o n s , R o u n d E n v i r o n m e n t r o u n d . . . } } クラス名を M E T A - I N F / s e r v i c e s / j a v a x . a n n o t a t i o n . p r o c e s s i n g . P r o c e s s o r に書いておけば良い。 c o m . h k u r o k a w a . m u l t i l o a d e r . i n t e r n a l . M u l t i L o a d e r P r o c e s s o r
  8. Android Studio での開発 Android の JDK には、 j a v

    a x . a n n o t a t i o n . p r o c e s s i n g . A b s t r a c t P r o c e s s o r が含まれていない。 従って、Processor のコードと Android のコードを同じプロジ ェクトで一緒に gradle ビルドすることはできない(Maven だ とできる?)。
  9. プロジェクト構成 以下のようなプロジェクト構成にする必要がある multiloader Android ライブラリモジュー ル。Android アプリから呼び出 す API を提供。

    multiloader-compiler Annotation Processing を行う コード。Android アプリには含 まれない。 multiloader-sample ライブラリを呼び出すサンプ ルアプリ。
  10. build.gradle b u i l d . g r a

    d l e には以下のように書いておく。m u l t i l o a d e r - c o m p i l e r が dependency に入るけれど apk に入らない、という 状態を作るために、 を利用する。 android-apt multiloader/build.gradle multiloader-compiler/build.gradle multiloader-sample/build.gradle a p p l y p l u g i n : ' c o m . a n d r o i d . l i b r a r y ' d e p e n d e n c i e s { c o m p i l e p r o j e c t ( ' : m u l t i l o a d e r - c o m p i l e r ' ) } a p p l y p l u g i n : ' j a v a ' a p p l y p l u g i n : ' c o m . a n d r o i d . a p p l i c a t i o n ' a p p l y p l u g i n : ' a n d r o i d - a p t ' d e p e n d e n c i e s { a p t p r o j e c t ( ' : m u l t i l o a d e r - c o m p i l e r ' ) c o m p i l e p r o j e c t ( ' : m u l t i l o a d e r ' ) }
  11. Processor の実装 文字列連結... b u i l d e r

    . a p p e n d ( " / / G e n e r a t e d c o d e f r o m M u l t i L o a d e r . D o n o t m o d i f y ! \ n " ) ; b u i l d e r . a p p e n d ( " p a c k a g e " ) . a p p e n d ( c l a s s P a c k a g e ) . a p p e n d ( " ; \ n \ n " ) ; b u i l d e r . a p p e n d ( " p u b l i c c l a s s " ) . a p p e n d ( c l a s s N a m e ) . a p p e n d ( " i m p l e m e n t s L o a d e r M a n a g e r . L o a d e b u i l d e r . a p p e n d ( " p r i v a t e " + e n c l o s i n g C l a s s N a m e + " m A c t i v i t y ; \ n " ) ; StringBuilder じゃなくてストリームでも書けます プログラム構造を解析して、いろいろチェックもできます
  12. デモ

  13. まとめ Annotation Processing のときに構文的なもの (Enclosing/Enclosed)を見れるのは、おもしろい 複雑なことはやらない方が良い コンパイル時にチェックできるのが、地味にうれしい 文字列連結でソースコードを書くの、つらい → square/javawriter