Как же утомителен процесс инициализации UI при разработке android-приложений. Раз за разом приходится писать горы шаблонного кода: findViewbyId, setOnClickListener, getResources().getDrawable, … Возникает естественное желание переложить эту работу на плечи AOP. Беглый поиск готовых решений, адаптированных под android, навел разве что на RoboGuice, о котором уже упоминалось на хабре. Однако библиотека имеет значительный размер (~0.5 mb), что для многих приложений недопустимо много, и к тому же требует наследования ваших классов application и activity от RoboApplication и RoboActivity, чего не всегда хочется делать. Потому и появился Aibolit, легкая (~40kb), простая в использовании и функциональная библиотека, использующая dependency injection для инициализации UI на android.

Подключаем зависимость:
и инжектим
Код очень прост, он полностью избавлен от вызовов методов findViewById, setOnClickListener и им подобных. Вместо этого:
Вызов 2-ух методов
может быть заменен на один:
Aibolit также позволяет инжектить прикладные сервисы приложения, как это сделано в примере выше:
Более подробно об этом можно узнать из документации к классу Aibolit.
Исходники кода открыты и документированы. Что же происходит за сценой? Aibolit анализирует класс на наличие полей и методов, помеченных Inject* аннотациями. Для каждой аннотации определен свой класс-инжектор, который ответственен за инициализацию поля тем или иным ресурсом. Ниже пример такого инжектора, ответственного за инициализацию view:
Чтобы не давать поводов для холиваров, приведу просто сухие цифры. Измерения производились на эмуляторе. Необходимо было проинициализировать 35 элементов (view, лисенеры, ресурсы приложения). При использовании классического подхода на переход с одной активити на другую требовалось 40-50 ms, при использовании aibolit — 90-100 ms. Как и ожидалось, подход, основанный на рефлексии, более времязатратный, однако полученная разница, имхо, несущественна.
Aibolit можно использовать как готовый продукт, так и в качестве основы для написания своего решения. Код библиотеки открыт и распространяется под Apache License, Version 2.0.
Буду рад замечаниям и предложениям.
UPD 0:
Схожие библиотеки
roboguice
androidannotations

Возможности
- инициализация view;
- добавление лисенеров событий для view;
- инициализация ресурсов приложения: drawable, string, animation, boolean, dimension, integer, array, color, array adapter;
- инициализация системных сервисов;
- инициализация прикладных сервисов приложения.
Пример использования
Подключаем зависимость:
repositories { maven { url 'https://dl.bintray.com/alexeydanilov/maven' } } dependencies { compile 'com.danikula:aibolit:1.0' }
и инжектим
public class AibolitChatActivity extends Activity { // annotate fields to be injected... @InjectView(R.id.messageEditText) private EditText messageEditText; @InjectView(R.id.historyListView) private ListView historyListView; @InjectResource(R.string.symbols_count) private String symbolsCountPattern; @InjectSystemService(Context.NOTIFICATION_SERVICE) private NotificationManager notificationManager; @InjectService private HttpManager httpManager; @InjectResource(R.layout.content) private View content; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.chat_activity); // initialize annotated fields and methods Aibolit.doInjections(this); // or just Aibolit.setInjectedContentView(this); ... } // annotate event handlers... @InjectOnClickListener(R.id.sendButton) private void onSendButtonClick(View v) { // handle onClick event } @InjectOnClickListener(R.id.clearHistoryButton) private void onClearHistoryButtonClick(View v) { // handle onClick event } @InjectOnTextChangedListener(R.id.messageEditText) public void onMessageTextChanged(CharSequence s, int start, int before, int count) { // handle text changed event } ... }
Код очень прост, он полностью избавлен от вызовов методов findViewById, setOnClickListener и им подобных. Вместо этого:
- помечаем view, которые должны быть инициализированы при помощи аннотации InjectView;
- определяем обработчики событий и помечаем их соответствующими аннотациями InjectOn*. В данном примере были определены обработчики события нажатия на кпопку и изменения текста. Aibolit позволяет добавить обработчики всех основных событий:
OnClick, OnLongClick, OnTouch, OnKey, OnTextChanged, OnCheckedChange, OnFocusChange, OnItemClick, OnItemSelected, OnEditorAction, OnCreateContextMenu; - вызываем метод
Aibolit.doInjections(this);после того как установили контент для активити.
Вызов 2-ух методов
setContentView(layoutId); Aibolit.doInjections(this);
может быть заменен на один:
Aibolit.setInjectedContentView(this);
Aibolit также позволяет инжектить прикладные сервисы приложения, как это сделано в примере выше:
@InjectService private HttpManager httpManager;
Более подробно об этом можно узнать из документации к классу Aibolit.
Как это работает
Исходники кода открыты и документированы. Что же происходит за сценой? Aibolit анализирует класс на наличие полей и методов, помеченных Inject* аннотациями. Для каждой аннотации определен свой класс-инжектор, который ответственен за инициализацию поля тем или иным ресурсом. Ниже пример такого инжектора, ответственного за инициализацию view:
class ViewInjector extends AbstractFieldInjector<InjectView> { @Override public void doInjection(Object fieldOwner, InjectionContext injectionContext, Field field, InjectView annotation) { int viewId = annotation.value(); View view = injectionContext.getRootView().findViewById(viewId); if (view == null) { // throw exception... } if (!field.getType().isAssignableFrom(view.getClass())) { // throw exception... } try { field.setAccessible(true); field.set(fieldOwner, view); } catch (IllegalArgumentException e) { // throw exception... } catch (IllegalAccessException e) { // throw exception... } } }
Производительность
Чтобы не давать поводов для холиваров, приведу просто сухие цифры. Измерения производились на эмуляторе. Необходимо было проинициализировать 35 элементов (view, лисенеры, ресурсы приложения). При использовании классического подхода на переход с одной активити на другую требовалось 40-50 ms, при использовании aibolit — 90-100 ms. Как и ожидалось, подход, основанный на рефлексии, более времязатратный, однако полученная разница, имхо, несущественна.
Aibolit можно использовать как готовый продукт, так и в качестве основы для написания своего решения. Код библиотеки открыт и распространяется под Apache License, Version 2.0.
Буду рад замечаниям и предложениям.
UPD 0:
Схожие библиотеки
roboguice
androidannotations