2015-06-30 3 views
24

У меня есть lib, который использует аспекты и доступен через maven, теперь я пытаюсь использовать эту lib в приложении Android.Aspectj с андроидной библиотекой

Если бы я включил в Gradle файл приложения this plug-in, все работает отлично, но моя цель состоит в том, чтобы извлечь classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+' и apply plugin: 'android-aspectj' (требуется плагин) к Gradle файл my.lib вместо того, чтобы объявить в моем приложении.

Возможно ли это?

приложение Gradle файла:

classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+' 

apply plugin: 'android-aspectj' 

dependencies { 
    compile 'my.lib:example:1.0.0' 
} 

ЦЕЛЬ:

приложение Gradle файла:

dependencies { 
    compile 'my.lib:example:1.0.0' 
} 

my.lib Gradle файл:

classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+' 

apply plugin: 'android-aspectj' 

dependencies { 
    compile 'org.aspectj:aspectjrt:1.7.3' 
} 

ответ

12

У меня была такая же проблема. Это все, что я сделал, чтобы решить эту проблему.

Root/Main Project

В корневом проекте добавить инструменты AspectJ, которые содержат АЕК компилятор, который необходим для ткачества классы. (Вы также можете добавить это в файл build.gradle вашей библиотеки, но это лучше, чтобы добавить его здесь как Gradle плагин, который вы будете создавать для размещения вашей библиотеки будет использовать AJC.

buildscript { 
    repositories { 
     jcenter() 


    } 
    dependencies { 
     classpath 'com.android.tools.build:gradle:1.2.3' 
     classpath 'org.aspectj:aspectjtools:1.8.5' 
    } 

проекта Библиотека

в файле build.gradle вашей библиотеки убедитесь, что это выглядит примерно так. Основные дополнения являются операторы импорта в верхнем и код под андроидом свойств сборки.

import com.android.build.gradle.LibraryPlugin 
import org.aspectj.bridge.IMessage 
import org.aspectj.bridge.MessageHandler 
import org.aspectj.tools.ajc.Main 

apply plugin: 'com.android.library' 


dependencies { 
    compile 'org.aspectj:aspectjrt:1.8.5' 
} 
android { 
    compileSdkVersion 22 
    buildToolsVersion "22.0.1" 

    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 22 
     versionCode 1 
     versionName "1.0" 
    } 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 
} 

android.libraryVariants.all { variant -> 
    LibraryPlugin plugin = project.plugins.getPlugin(LibraryPlugin) 
    JavaCompile javaCompile = variant.javaCompile 
    javaCompile.doLast { 
     String[] args = [ 
       "-showWeaveInfo", 
       "-1.5", 
       "-inpath", javaCompile.destinationDir.toString(), 
       "-aspectpath", javaCompile.classpath.asPath, 
       "-d", javaCompile.destinationDir.toString(), 
       "-classpath", javaCompile.classpath.asPath, 
       "-bootclasspath", android.bootClasspath.join(File.pathSeparator) 
     ] 

     MessageHandler handler = new MessageHandler(true); 
     new Main().run(args, handler) 

     def log = project.logger 
     for (IMessage message : handler.getMessages(null, true)) { 
      switch (message.getKind()) { 
       case IMessage.ABORT: 
       case IMessage.ERROR: 
       case IMessage.FAIL: 
        log.error message.message, message.thrown 
        break; 
       case IMessage.WARNING: 
       case IMessage.INFO: 
        log.info message.message, message.thrown 
        break; 
       case IMessage.DEBUG: 
        log.debug message.message, message.thrown 
        break; 
      } 
     } 
    } 
} 

Итак, что происходит, когда проект компилируется, команда ajc (aspectJ's weaver) компилирует и вставляет файлы AspectJ и Java source и .class, создавая файлы .class, совместимые с любой виртуальной машиной Java.

Для этого задача требует аргументов в отношении вашей библиотеки. В этом причина создания переменной args.

String[] args = [ 
        "-showWeaveInfo", 
        "-1.5", 
        "-inpath", javaCompile.destinationDir.toString(), 
        "-aspectpath", javaCompile.classpath.asPath, 
        "-d", javaCompile.destinationDir.toString(), 
        "-classpath", javaCompile.classpath.asPath, 
        "-bootclasspath", android.bootClasspath.join(File.pathSeparator) 
      ] 

Тогда обработчик сообщений, который создается просто передается в АЕК аккумулировать сообщения о событиях, которые происходят в то время как AJC компилирует/сотка классы. Затем он передается в регистратор проектов, который затем выводит любые важные ошибки или предупреждения, которые создаются ajc. Например, если pointcut не может ссылаться на совет, он будет обнаружен и показан в консоли gradle. enter image description here

Так что все, что описано выше, в основном происходит прямо здесь. Где args и обработчик сообщений передаются на главную функцию ajc (компилятор AspectJ).

MessageHandler handler = new MessageHandler(true); 
     new Main().run(args, handler) 

     def log = project.logger 
     for (IMessage message : handler.getMessages(null, true)) { 
      switch (message.getKind()) { 
       case IMessage.ABORT: 
       case IMessage.ERROR: 
       case IMessage.FAIL: 
        log.error message.message, message.thrown 

Gradle Плагин

точка срезы/советы вашей библиотеки не были срабатывает, потому что вы целились модуль приложения в то время как аспекты были вплетены только в модуль вашей библиотеки с com.uphyca.gradle:gradle-android-aspectj-plugin AspectJ плагина. Поэтому для того, чтобы Аспекты вашей библиотеки были вплетены в ваш модуль App, вы должны создать плагин для вашего проекта. Итак, что вы определили как свою цель, ваш вопрос невозможен, это единственный способ сделать это.

Вот как должен выглядеть плагин. (Плагин выполнен в паре).

build.gradle

apply plugin: 'groovy' 

targetCompatibility = JavaVersion.VERSION_1_7 
sourceCompatibility = JavaVersion.VERSION_1_7 

dependencies { 
    compile gradleApi() 
    compile localGroovy() 
    compile 'com.android.tools.build:gradle:1.1.0-rc3' 
    compile 'org.aspectj:aspectjtools:1.8.5' 
    compile 'org.aspectj:aspectjrt:1.8.5' 
} 

Тогда фактический класс плагина.

import com.android.build.gradle.AppPlugin 
import com.android.build.gradle.LibraryPlugin 
import org.aspectj.bridge.IMessage 
import org.aspectj.bridge.MessageHandler 
import org.aspectj.tools.ajc.Main 
import org.gradle.api.Plugin 
import org.gradle.api.Project 

public class YourPlugin implements Plugin<Project> { 
    @Override void apply(Project project) { 
     def hasApp = project.plugins.withType(AppPlugin) 
     def hasLib = project.plugins.withType(LibraryPlugin) 
     if (!hasApp && !hasLib) { 
      throw new IllegalStateException("'android' or 'android-library' plugin required.") 
     } 

     final def log = project.logger 
     final def variants 
     if (hasApp) { 
      variants = project.android.applicationVariants 
     } else { 
      variants = project.android.libraryVariants 
     } 

     project.dependencies { 
      compile 'com.name:example:1.0' 
      // TODO this should come transitively 
      compile 'org.aspectj:aspectjrt:1.8.5' 
     } 

     variants.all { variant -> 

      variant.dex.doFirst { 
       String[] args = [ 
         "-showWeaveInfo", 
         "-1.5", 
         "-inpath", javaCompile.destinationDir.toString(), 
         "-aspectpath", javaCompile.classpath.asPath, 
         "-d", javaCompile.destinationDir.toString(), 
         "-classpath", javaCompile.classpath.asPath, 
         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator) 
       ] 
       log.debug "ajc args: " + Arrays.toString(args) 

       MessageHandler handler = new MessageHandler(true); 
       new Main().run(args, handler); 
       for (IMessage message : handler.getMessages(null, true)) { 
        switch (message.getKind()) { 
         case IMessage.ABORT: 
         case IMessage.ERROR: 
         case IMessage.FAIL: 
          log.error message.message, message.thrown 
          break; 
         case IMessage.WARNING: 
          log.warn message.message, message.thrown 
          break; 
         case IMessage.INFO: 
          log.info message.message, message.thrown 
          break; 
         case IMessage.DEBUG: 
          log.debug message.message, message.thrown 
          break; 
        } 
       } 
      } 
     } 
    } 
} 

Я знаю, это может показаться много, но это много копирования и вставки, потому что решение остается тем же самым. Если вы внимательно посмотрите на класс, то те же самые вещи, которые выполняются в вашем библиотечном модуле, теперь применяются к модулю вашего приложения. Основная модификация, которую вы сделали бы с этим: добавьте свой библиотечный модуль в зависимости от проекта через плагин, который здесь делается.

project.dependencies { 
       compile 'com.letz:example-library:1.0' 
       // TODO this should come transitively 
       compile 'org.aspectj:aspectjrt:1.8.5' 
      } 

Для вашей библиотеки должны быть доступны для вашего плагина при разработке вы должны убедиться, что она развертывается в локальный репозиторий Maven. Это можно сделать, применив этот плагин (https://github.com/dcendents/android-maven-gradle-plugin) к вашему библиотечному модулю и запустив задачу gradle install.

Заключительные шаги

После того, как все это будет сделано, то вы можете применить его к пример приложения для тестирования, добавив в него это build.gradle файл

buildscript { 
    repositories { 
     mavenCentral() 

     //Only necessary when developing locally. 
     mavenLocal() 
    } 

    dependencies {    

     classpath 'com.letz:example-plugin:1.0' 
    } 
} 
apply plugin: 'example-plugin' 

После того, как это сделано в вашей библиотеке будет доступно для приложения, потому что он добавляется в проект после применения плагина.

Если все еще запутано, вам очень повезло, потому что проект, который я реализовал это решение, находится на Github, поэтому вы можете его разветвить, скопировать проект плагина и внести необходимые изменения.

Проект называется Flender и используется для аннотирования методов, требующих проверки связи. Вот ссылка https://github.com/jd-alexander/flender

Надеюсь, что этот ответ поможет.

Смежные вопросы