Как стать автором
Обновить

Aibolit для android

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


Возможности


  • инициализация 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 и им подобных. Вместо этого:
  1. помечаем view, которые должны быть инициализированы при помощи аннотации InjectView;
  2. определяем обработчики событий и помечаем их соответствующими аннотациями InjectOn*. В данном примере были определены обработчики события нажатия на кпопку и изменения текста. Aibolit позволяет добавить обработчики всех основных событий:
    OnClick, OnLongClick, OnTouch, OnKey, OnTextChanged, OnCheckedChange, OnFocusChange, OnItemClick, OnItemSelected, OnEditorAction, OnCreateContextMenu;
  3. вызываем метод 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
Теги:
Хабы:
+50
Комментарии26

Публикации

Истории

Работа

Ближайшие события