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

Android Annotations

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Arne Handt Arne Handt
October 31, 2012

Android Annotations

Vorstellung von Android Annotations beim Berliner Android-Stammtisch, 31.10.2012

Avatar for Arne Handt

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/