Как же утомителен процесс инициализации 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