Аспектно ориентированное программирование в Android

Услышав об аспектно ориентированном программировании я, как Android разработчик, сразу подумал, что на Android это вряд ли заработает, однако, решил попробовать. Я был очень удивлен, когда уже через 5 минут приложение, использующее AspectJ, успешно заработало на телефоне.

Не буду убеждать в необходимости использования аспектов в Android и приводить примеры «uses cases», просто приведу пример того, как добавить к своему приложению возможность использования аспектов. Что такое аспекты и для чего их использовать прекрасно описано, например, на wiki.

В качестве IDE для разработки я уже давно перешел на Android Studio, а следовательно использую gradle как систему сборки. Для нее и буду приводить пример конфигурирования.

Весь процесс подключения AspectJ состоит из следующих шагов:

  1. Подключаем плагин для сборки:
    В разделе buildscript скрипта сборки вашего проекта добавляем в dependencies строку:
    classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+'

  2. В build.gradle модуля приложения добавляем:
    apply plugin: "android-aspectj"

  3. … а в зависимости модуля добавляем:
    dependencies {
        compile 'org.aspectj:aspectjrt:1.8.+'
    }
    

  4. Пишем сам аспект.
    Аспекты можно писать, используя специальный синтаксис, в файлах с расширением as, либо используя java + ряд анотаций. Поскольку Android Studio не включает AspectJ плагин, то я предпочел второй вариант (Для обладателей Ultimate версии IDEA доступны оба варианта):
    
    @Aspect
    public class MainActivityAspect {
        private static final String TAG = "helloaspectj";
    
        @Pointcut("execution(* ru.hoticecream.helloaspectj.ui.MainActivity.onCreate(..))")
        public void onCreateMethod(){}
    
        @Before("onCreateMethod()")
        public void doBeforeOnCreate(JoinPoint joinPoint) {
            Log.i(TAG, "AspectJ was here");
        }
    }
    

    Данный аспект встраивается до вызова метода MainActivity.onCreate, выводя соответствующее сообщение в лог.


Теперь, запустив приложение и открыв MainActivity, мы увидим в логе:
... I/helloaspectj﹕ AspectJ was here

Приведу еще пример использования аспекта.
Для этого в MainActivity добавим метод, устанавливающий определенному текстовому полю сообщение:

    private TextView mGreetingText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mGreetingText = (TextView) findViewById(R.id.greeting_message);
        setGreetingMessage("Hello Android");
    }

    private void setGreetingMessage(String message) {
        mGreetingText.setText(message);
    }

Как видите, поле принимает значение «Hello Android», запустив приложение можно в этом убедиться;)

Теперь добавим все в тот же класс аспекта следующие методы:

    @Pointcut("execution(* ru.hoticecream.helloaspectj.ui.MainActivity.setGreetingMessage(..))")
    public void setGreetingMessageMethod(){}

    @Around("setGreetingMessageMethod()")
    public void makeNasty(final ProceedingJoinPoint pjp)
            throws Throwable {
        Log.d(TAG, pjp.getSignature().toLongString());
        pjp.proceed(new Object[] { "Hello AspectJ" });
    }

В теперь этот аспект еще ищет метод setGreetingMessage и заменяет его вызов на содержимое метода makeNasty, используя для этого анотацию @Around

Запустив приложение, мы уже увидим «Hello AspectJ». При этом MainActivity.java мы не изменяли.

На этом все. Надеюсь статья была вам полезна.

Напоследок пару ссылок:
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 9

    +1
    > Что такое аспекты и для чего их использовать прекрасно описано, например, на wiki.
    Я бы не был столь категоричен
      0
      Посмотрите лекцию, на которую есть ссылка в конце поста — там довольно интересно об этом рассказывается.
        +1
        Лекцию я в закладки добавил.
        У меня есть сомнения в качестве пояснений на отсылаемую страницу вики.
      0
      А как у этой штуки на счет производительности, тестируемости и поддерживаемости?
        0
        Производительность: после компиляции java в class файлы, в них добавляется еще байткод из аспектов. Так что производительность не должна отличаться от того, как если бы вы код из аспекта написали прямо в методе, к которому этот аспект применяется.
        Про тестируемость: опять же, в той же лекции говорится, что бы в аспектах не было много кода — весь код выносить в отдельные классы, к которым уже можно написать тесты.
        Про поддержку: подозреваю, что аспекты могут очень сильно удивить человека, который в дальнейшем будет поддерживать этот код, поэтому применять их стоит очень осторожно.
          0
          А вот при дебаге человек увидит, что это прокся или интерфейс будет изначальным?
        0
        Извечный вопрос, зачем? Только тот кто не узрел во что превращает aspectJ байт-код java-программы, способен его использовать. Как насчет времени компиляции и финального размера вашего бинарника?
          0
          Время компиляции в Android Studio изменилось незаметно (учитывая, что gradle и так не особо быстр). Про размер бинарника: почему он должен увеличиться? И с ними и без на выходе получается одинаковый байткод (приблизительно), разница в организации java кода.
          Зачем? Воспринимайте это как еще один инструмент, а как применять этот инструмент уже думайте сами.
          Приведу пример того, что пришло мне в голову: в многопоточном приложении иногда, что бы отладить работу требуется вставить логи в каждый метод. Если таких методов пару десятков, то задача это не самая приятная, да и засоряет код. Используя аспекты можно это сделать быстро и просто) После того, как проблема решена достаточно удалить класс аспекта и код больше не будет засорен лишними вызовами логов.
            0
            Но ведь как раз для такой задачи хватает штатных средств IoC контейнера (я не имею в виду spring), ну или на худой конец есть аннотации. АОП выглядит простым на простых задачах, но эта простота быстро улетучивается(возьмите к примеру spring-roo).

        Only users with full accounts can post comments. Log in, please.