Slide 1

Slide 1 text

Android Annotations Android Stammtisch / 31.10.2012 Arne Handt

Slide 2

Slide 2 text

@handtwerk ‣ Freiberuflicher Softwareentwickler in Berlin ‣ Android-Entwicklung seit Anfang 2009 ‣ Bisherige Auftraggeber (Auswahl):

Slide 3

Slide 3 text

Android Annotations ‣ Das Projekt, das Werkzeug ‣ Feature-Tour ‣ Component Enhancement ‣ Dependency Injection ‣ Event Binding ‣ Simplified Threading ‣ Beispiel

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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: ‣ Starten: startActivity(new Intent(context, MyActivity_.class)); ‣ ...oder IntentHelper/Builder, s.u.

Slide 8

Slide 8 text

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;  }

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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:

Slide 11

Slide 11 text

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:

Slide 12

Slide 12 text

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  scores;            @Overview          protected  void  onNewIntent(Intent  pIntent)  {                                  setIntent(pIntent);          }  } scores  =  (Collection)getIntent().getSerializableExtra(„scores“); Zum Vergleich: MyActivity_.intent(context)                      .title(„Fancypants“)                      .scores(Arrays.asList(0))                      .start();

Slide 13

Slide 13 text

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  scores;  } scores  =  (Collection)getArguments().getSerializable(„scores“); Zum Vergleich: MyFragment  frag  =            MyFragment_.builder()                                  .title(„Fancypants“)                                  .scores(Arrays.asList(0))                                  .build();

Slide 14

Slide 14 text

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:

Slide 15

Slide 15 text

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()  {          }  }

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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:

Slide 18

Slide 18 text

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)  {          }  }

Slide 19

Slide 19 text

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()  {          }  }

Slide 20

Slide 20 text

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;          }  }

Slide 21

Slide 21 text

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)  {          }  }

Slide 22

Slide 22 text

Beispiel

Slide 23

Slide 23 text

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  findAll(); }

Slide 24

Slide 24 text

View & Adapter @EBean public  class  PersonListAdapter  extends  BaseAdapter  {        List  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);        } }

Slide 25

Slide 25 text

Activity @EActivity(R.layout.person_list) public  class  PersonListActivity  extends  ListActivity  {        @Bean        PersonListAdapter  mAdapter;        @AfterInject        void  bindAdapter()  {                setListAdapter(mAdapter);        }        @ItemClick        void  personListItemClicked(Person  pPerson)  {        } }

Slide 26

Slide 26 text

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/

Slide 27

Slide 27 text

@Ende