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

Android Annotations

Arne Handt
October 31, 2012

Android Annotations

Vorstellung von Android Annotations beim Berliner Android-Stammtisch, 31.10.2012

Arne Handt

October 31, 2012
Tweet

Other Decks in Programming

Transcript

  1. Android Annotations ‣ Das Projekt, das Werkzeug ‣ Feature-Tour ‣

    Component Enhancement ‣ Dependency Injection ‣ Event Binding ‣ Simplified Threading ‣ Beispiel
  2. Das Projekt ‣ Ziel: Generierung von Boilerplate-Code ‣ Weniger Code

    zu schreiben, weniger Code zu lesen ‣ Open-Source-Projekt, Apache 2.0 ‣ https://github.com/excilys/androidannotations ‣ Ca. 30 Commits/Merges in der letzten Woche ‣ Letztes Release: Juni 2012
  3. Das Werkzeug ‣ Code-Generator ‣ Durch Java-Annotations gesteuert ‣ Integriert

    sich nahtlos in den Build-Prozess ‣ Unterstützt Eclipse, Maven, Ant, Gradle, IDEA ‣ Laufzeitbibliothek ‣ 50kb Speicherfootprint
  4. Features @EActivity(R.layout.translate)  //  Sets  content  view  to  R.layout.translate public  class

     TranslateActivity  extends  Activity  {        @ViewById  //  Injects  R.id.textInput        EditText  textInput;        @ViewById(R.id.myTextView)  //  Injects  R.id.myTextView        TextView  result;        @AnimationRes  //  Injects  android.R.anim.fade_in        Animation  fadeIn;        @Click  //  When  R.id.doTranslate  button  is  clicked          void  doTranslate()  {                  translateInBackground(textInput.getText().toString());        }        @Background  //  Executed  in  a  background  thread        void  translateInBackground(String  textToTranslate)  {                  String  translatedText  =  callGoogleTranslate(textToTranslate);                  showResult(translatedText);        }              @UiThread  //  Executed  in  the  ui  thread        void  showResult(String  translatedText)  {                  result.setText(translatedText);                  result.startAnimation(fadeIn);        } } Dependency Injection Simplified Threading Event Binding Component Enhancement
  5. Component Enhancement ‣ Steht zur Verfügung für ‣ Android-Anwendungskomponenten @EApplication,

    @EActivity, @EService, @EFragment, @EReceiver, @EProvider, @EView, @EViewGroup ‣ Framework-unabhängige Klassen @EBean ‣ MyClass → MyClass_ ‣ Generierte Klassen sind final ‣ Deklarieren: <activity android:name=".MyActivity_" /> ‣ Starten: startActivity(new Intent(context, MyActivity_.class)); ‣ ...oder IntentHelper/Builder, s.u.
  6. Klassen-Annotation ‣ Voraussetzung für weitere Annotationen ‣ Bei Activities und

    ViewGropus optional Deklaration des Layouts ‣ @EBean für eigene Klassen ‣ Contract: Ein Konstruktor, Context- Parameter optional ‣ Optional: Scope ‣ Default: Neue Instanz pro Injektion ‣ Singleton: Nur eine Instanz ‣ Injection nur eingeschränkt  @EApplication  public  class  MyApplication  extends  Application  {                  @Bean          MyDatastore  dataStore;            }  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @App          MyApplication  app;  }  @EBean(scope  =  Scope.Singleton)  public  class  MyBean  {                  @Bean          MyDatastore  dataStore;  }
  7. Dependency Injection ‣ Objekte aus dem Laufzeitkontext werden als Instanzvariablen

    gesetzt ‣ Kein findViewById mehr, kein getStringExtra mehr, etc. ‣ Automatischer Typecast ‣ Was kann injiziert werden? ‣ Views ‣ Projektressourcen ‣ Intent-Extras ‣ Systemdienste ‣ Fragment-Argumente ‣ Non-Configuration Instances ‣ HTTPS-Client (Key store & trust store aus Ressourcen) ‣ Eigene Objekte ‣ Konsequenz: Entsprechende Instanzvariablen können nicht private sein
  8. Injection: Views ‣ @ViewById ‣ Setzt Views anhand ihrer ID

    aus dem Layout ‣ Ohne ID: Variablenname wird als ID verwendet ‣ @AfterViews ‣ Annotiert Methoden, die nach dem Aufbauen der Views aufgerufen werden  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @ViewById(R.id.input)          EditText  input;          @ViewById  //  Injects  R.id.image          ImageView  image;          @AfterViews          void  doMyStuff()  {                                  input.setOnEditorActionListener(...);          }  } input  =  (EditText)findViewById(R.id.text_input) Zum Vergleich:
  9. Injection: Ressourcen ‣ @StringRes, ... ‣ Setzt Ressourcen aller Art

    ‣ ID wieder explizit oder implizit möglich  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @StringRes(R.string.message)          String  message;          @AnimationRes  //  Injects  R.anim.fadeIn          Animation  fadeIn;          @DrawableRes  //  Injects  R.drawable.icon          Drawable  icon;  } icon  =  getResources().getDrawable(R.drawable.icon); Zum Vergleich:
  10. Injection: Extras ‣ @Extra ‣ Setzt Extras aus einem Intent

    ‣ Injection passiert in setIntent ‣ Intent Helper:  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @Extra(„title“)          String  title;          @Extra  //  Injects  „scores“          Collection<Integer>  scores;            @Overview          protected  void  onNewIntent(Intent  pIntent)  {                                  setIntent(pIntent);          }  } scores  =  (Collection<Integer>)getIntent().getSerializableExtra(„scores“); Zum Vergleich: MyActivity_.intent(context)                      .title(„Fancypants“)                      .scores(Arrays.asList(0))                      .start();
  11. Injection: Argumente ‣ @FragmentArg ‣ Setzt Fragment-Argumente ‣ Funktioniert analog

    zu @Extra ‣ Fragment Builder:  @EFragment  public  class  MyFragment  extends  Fragment  {                  @FragmentArg(„title“)          String  title;          @FragmentArg  //  Injects  „scores“          Collection<Integer>  scores;  } scores  =  (Collection<Integer>)getArguments().getSerializable(„scores“); Zum Vergleich: MyFragment  frag  =            MyFragment_.builder()                                  .title(„Fancypants“)                                  .scores(Arrays.asList(0))                                  .build();
  12. Injection: Systemdienste ‣ @SystemService ‣ Setzt einen Systemdienst ‣ Identifikation

    anhand der Klasse  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @SystemService  //  Injects  NOTIFICATION_SERVICE          NotificationManager  notifier;  } notifier  =  (NotificationManager)getSystemService(NOTIFICATION_SERVICE); Zum Vergleich:
  13. Injection: Verschiedenes ‣ @App ‣ Setzt Application-Objekt ‣ @Bean ‣

    Setzt Instanz einer @EBean ‣ „Entkopplung“ durch Übergabe des Klassenobjektes ‣ @RootContext (nur @EBeans) ‣ Setzt Context-Objekt einer Bean ‣ View-bezogene Annotations auch für eigene Klassen, wenn der RootContext eine Activity ist ‣ @AfterInject ‣ Annotierte Methode wird nach dependency injection aufgerufen ‣ Aber: Views noch nicht injiziert  @EBean  public  class  MyBean  {                  @App          MyApplication  application;          @Bean          MyDatastore  dataStore;          @Bean(MyImplementation.class)          MyInterface  mySomething;          @RootContext          Activity  activity;          @ViewById(R.id.input)          EditText  input;          @AfterInject          void  init()  {          }  }
  14. Event Binding ‣ Annotierte Methoden werden bei UI-Events aufgerufen ‣

    Kein setOnClickListener(new OnClickListener() { ... }) ‣ Bindet an welche Events? (Listener) ‣ Touch/Click ‣ Allgemein (Click, LongClick, Touch) ‣ Adapter-Items (ItemClick, ItemLongClick, ItemTouch) ‣ SeekBar (SeekBarChange) ‣ Text (TextWatcher) ‣ Options Menu ‣ Konsequenz: Entsprechende Methoden können nicht private sein
  15. Events: Touch ‣ @Click ‣ Annotierte Methode wird bei onClick

    aufgerufen ‣ Wahlweise mit View als Parameter oder ohne ‣ Ohne ID: Methodenname wird als ID verwendet ‣ Mehrere Views pro Methode möglich ‣ Nur eine Methode pro View möglich ‣ @LongClick, @Touch ‣ analog  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @Click(R.id.submit)          void  onSubmit(View  pClickedView)  {          }          @Click  //  When  R.id.cancel  is  clicked          void  cancel()  {          }          @Click({R.id.this,  R.id.that})          void  onThisOrThat()  {          }          @LongClick(R.id.foo)          void  onFooLongClicked()  {          }          @Touch(R.id.bar)          void  onBarTouched(MotionEvent  pEvent)  {          }  } findViewById(R.id.submit).setOnClickListener(        new  OnClickListener()  {                public  void  onClick(View  pView)  {                        onSubmit();                }        }); Zum Vergleich:
  16. Events: Adapter Items ‣ @ItemClick, @ItemLongClick ‣ Annotierte Methode wird

    bei onClick bzw. onLongClick aufgerufen ‣ ID des ListViews als Annotations- parameter ‣ Entweder Item-Objekt oder Position (int) als Methodenparameter ‣ @ItemSelect ‣ analog ‣ Flag als zusätzlicher Parameter  @EActivity(R.layout.my_list_layout)        public  class  MyActivity  extends  Activity  {                  @ItemClick(android.R.id.list)          void  onItemClicked(MyItem  pItem)  {          }          @ItemClick(android.R.id.list)          void  onItemClicked(int  pPosition)  {          }          @ItemLongClick(android.R.id.list)          void  onItemLongClicked(MyItem  pItem)  {          }          @ItemSelect          void  onItemSelected(                                      boolean  pSelected,                                      MyItem    pItem)  {          }  }
  17. Events: Text ‣ @TextChange ‣ Annotierte Methode wird bei Änderungen

    aufgerufen ‣ Mehrere Views pro Methode möglich ‣ Parameter (optional, Auswahl möglich): ‣ TextView ‣ CharSequence ‣ Startposition ‣ Textlänge (vor Änderung) ‣ Anzahl geänderter Zeichen ‣ @BeforeTextChange, @AfterTextChange ‣ analog  @EActivity(R.layout.my_layout)        public  class  MyActivity  extends  Activity  {                  @TextChange({R.id.text1,  R.id.text2})          void  onTextChanged(                                      TextView  view,                                      CharSequence  text,                                      int  start,                                      int  before,                                      int  count)  {          }          @TextChange  //  When  myText  changes          void  myText(CharSequence  text)  {          }          @BeforeTextChange(R.id.foo_text)          void  onBeforeFooChanged(TextView  view)  {          }          @AfterTextChange(R.id.bar_text)          void  onAfterBarChanged()  {          }  }
  18. Events: Options Menu ‣ @OptionsMenu ‣ Annotiert Menü-Ressource zu Activity

    oder Fragment ‣ @OptionsItem ‣ Annotiert Handler zu Menü-Item ‣ Identifikation wieder via ID oder Methodenname ‣ Optional: Boolescher Rückgabewert  @EActivity(R.layout.my_layout)        @OptionsMenu(R.menu.my_options)      public  class  MyActivity  extends  Activity  {                  @OptionsItem(R.id.menuShare)          void  onShareSelected()  {          }          @OptionsItem  //  When  R.id.exit  is  clicked          boolean  exit()  {                        return  true;          }  }
  19. Simplified Threading ‣ @Background ‣ Methode wird auf einem Hintergrundthread

    ausgeführt ‣ Verwendet gemeinsamen ThreadPoolExecutor ‣ Mit Event Binding (z.B. @Click) kombinierbar ‣ @UiThread ‣ Methode wird auf dem UI-Thread ausgeführt ‣ Delay-Parameter für verzögerte Ausführung  @EActivity(R.layout.my_layout)      public  class  MyActivity  extends  Activity  {                  @Click(R.id.calculate)          void  onCalculateClicked()  {                  performComplicatedCalculation();          }          @Background          void  performComplicatedCalculation()  {                        int  result  =  1  +  1;                  displayResult(result);          }          @UiThread(delay=1000)          void  displayResult(int  result)  {          }  }
  20. Model public  class  Person  {        public  final

     String  firstName;        public  final  String  lastName;        public  Person(String  pFirstName,                                    String  pLastName)  {                firstName  =  pFirstName;                lastName  =  pLastName;        } } public  interface  PersonFinder  {        List<Person>  findAll(); }
  21. View & Adapter @EBean public  class  PersonListAdapter  extends  BaseAdapter  {

           List<Person>  mPeople;                @Bean(InMemoryPersonFinder.class)        PersonFinder  mPersonFinder;                @RootContext        Context  mContext;        @AfterInject        void  initAdapter()  {                mPeople  =  mPersonFinder.findAll();        }        @Override        public  View  getView(int  pPosition,  View  pConvertView,  ViewGroup  pParent)  {                                PersonItemView  personItemView;                if  (convertView  ==  null)  {                        personItemView  =  PersonItemView_.build(context);                }  else  {                        personItemView  =  (PersonItemView)  convertView;                }                personItemView.bind(getItem(position));                return  personItemView;        }        (...) } @EViewGroup(R.layout.person_item) public  class  PersonItemView  extends  LinearLayout  {        @ViewById(R.id.firstNameView)        TextView  mFirstNameView;        @ViewById(R.id.lastNameView)        TextView  mLastNameView;        public  PersonItemView(Context  pContext)  {                super(pContext);        }        public  void  bind(Person  pPerson)  {                mFirstNameView.setText(pPerson.firstName);                mLastNameView.setText(pPerson.lastName);        } }
  22. Activity @EActivity(R.layout.person_list) public  class  PersonListActivity  extends  ListActivity  {    

       @Bean        PersonListAdapter  mAdapter;        @AfterInject        void  bindAdapter()  {                setListAdapter(mAdapter);        }        @ItemClick        void  personListItemClicked(Person  pPerson)  {        } }
  23. Android Annotations ‣ Weniger Code, einfacherer Code ‣ Views, Beans

    und andere Komponenten injizieren lassen ‣ Event Handling bequem über annotierte Methoden ‣ Deklaratives Threading ‣ Kleinere technische Einschränkungen ‣ Kein Performanceverlust durch Reflection ‣ Siehe auch: Android Kickstartr: http://androidkickstartr.com/