Mavenを使用したAndroidアプリケーションでのJava 8ライブラリの使用

Java 8は2014年初頭に登場し、Java開発者が非常に便利な技術革新を使用して簡単なタスクのプログラミングを簡単に行えるようにしました。 その中には、ラムダ式、メソッドとコンストラクターへの参照、言語とJVMレベルでのデフォルトのインターフェースメソッドの実装、標準ライブラリレベルでのStream APIの使用があります。 残念ながら、このような導入の導入の遅れは、他のJava指向のソフトウェアプラットフォームでのこれらのツールのサポートに影響します。 GWTとAndroidは、少なくともJava 8言語ツールを公式にサポートしていませんが、GWT 2.8.0の春のSNAPSHOTバージョンは、ラムダ式を既にサポートしています。 Androidの場合、ラムダ式の動作はコンパイラ自体だけでなくランタイムにも依存するため、状況は異なります。 しかし、Mavenを使用すると、Java 8を使用する問題を比較的簡単に解決できます。



そのため、私はプロジェクトのコードベース全体をMavenに保持しているため、





このコードベースの汎用ライブラリは、Java SEプロジェクトとGWTまたはAndroidの両方で使用できるように作成され、他のモジュールに接続されます。 しかし、AndroidがJava 8に不利であるという事実を考慮すると、これらのライブラリは、Javaコードベースのアプリケーション自体と同様に、Java 6または7に残り続けます。 それでも、GWTでラムダを正常に使用した後、Java 8コードベース全体を移行したいという要望がありました。



<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin>
      
      





ライブラリをローカルリポジトリにインストールした後、原則として、アプリケーション自体をビルドできます。 ただし、「dex」プロセス中に、次のエラーが発生します。



 [INFO] UNEXPECTED TOP-LEVEL EXCEPTION: [INFO] com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) [INFO] at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472) [INFO] at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406) [INFO] at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388) [INFO] at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251) [INFO] at com.android.dx.command.dexer.Main.processClass(Main.java:665) [INFO] at com.android.dx.command.dexer.Main.processFileBytes(Main.java:634) [INFO] at com.android.dx.command.dexer.Main.access$600(Main.java:78) [INFO] at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:572) [INFO] at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284) [INFO] at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166) [INFO] at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144) [INFO] at com.android.dx.command.dexer.Main.processOne(Main.java:596) [INFO] at com.android.dx.command.dexer.Main.processAllFiles(Main.java:498) [INFO] at com.android.dx.command.dexer.Main.runMonoDex(Main.java:264) [INFO] at com.android.dx.command.dexer.Main.run(Main.java:230) [INFO] at com.android.dx.command.dexer.Main.main(Main.java:199) [INFO] at com.android.dx.command.Main.main(Main.java:103) [INFO] ...while parsing foo/bar/FooBar.class
      
      





このエラーは、 dx



がJava 8コンパイラによって生成されたクラスファイルを処理できないことを意味するため、理論的には状況を修正する必要があるRetrolambdaを接続します。



 <plugin> <groupId>net.orfjackal.retrolambda</groupId> <artifactId>retrolambda-maven-plugin</artifactId> <version>2.0.6</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>process-main</goal> <goal>process-test</goal> </goals> </execution> </executions> <configuration> <defaultMethods>true</defaultMethods> <target>1.6</target> </configuration> </plugin>
      
      





残念ながら、 foo/bar/FooBar.class



はライブラリに属し、エラーがfoo/bar/FooBar.class



ます。 retrolambda-maven-plugin



は、原則としてアプリケーションライブラリをインスツルメントするタスクを処理できません。これは、現在のモジュールのクラスファイルのみを処理できるためです(そうでない場合は、リポジトリでクラスファイルを直接処理する必要があります)。 つまり、アプリケーションはJava 8ライブラリを使用できませんが、現在のモジュールのJava 8コードのみを使用できます。 これは次のように解決できます。





android-maven-plugin



の現在の実装は、指定されたすべての依存関係でdx



を開始します。これにより、Java 8での依存関係のインストルメンテーションがさらに複雑になりandroid-maven-plugin







 $JAVA_HOME/jre/bin/java -Xmx1024M -jar "$ANDROID_HOME/sdk/build-tools/android-4.4/lib/dx.jar" --dex --output=$BUILD_DIRECTORY/classes.dex $BUILD_DIRECTORY/classes $M2_REPO/foo1-java8/bar1/0.1-SNAPSHOT/bar1-0.1-SNAPSHOT.jar $M2_REPO/foo2-java8/bar2/0.1-SNAPSHOT/bar2-0.1-SNAPSHOT.jar $M2_REPO/foo3-java8/bar3/0.1-SNAPSHOT/bar3-0.1-SNAPSHOT.jar
      
      





ここでは、3つのJava 8ライブラリすべてがdx



処理のために送信されます。 プラグイン自体には、 dx



に渡す必要がある依存関係のフィルターを管理する方法はありません。 このようなフィルターを制御できることが重要なのはなぜですか? いくつかの依存関係は、アーティファクトリポジトリよりも処理に便利な場所に既にあると想定できます。 たとえば、 ${project.build.directory}/classes



ます。 ここで、 retrolambda-maven-plugin



を使用してJava 8の依存関係を処理できretrolambda-maven-plugin







Maven用のプラグインがあり、これを使用して目的のディレクトリに依存関係を展開できます。これにより、必要な依存関係を適切な方法で処理できます。 例:



 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.10</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>unpack-dependencies</goal> </goals> <configuration> <includeScope>runtime</includeScope> <includeGroupIds>foo1-java8,foo2-java8,foo3-java8</includeGroupIds> <outputDirectory>${project.build.directory}/classes</outputDirectory> </configuration> </execution> </executions> </plugin>
      
      





依存関係フィルターを管理するためのいくつかのオプションのandroid-maven-plugin



サポートをforkに追加しました。 それらの中には、グループ識別子、アーチファクト識別子、およびバージョンによるフィルタリングおよび包含( excludes



および包含)がありincludes



。 アーティファクト識別子とそのバージョンは省略できます。 成果物または成果物のグループを識別するすべての要素は、コロンで区切る必要があります。 ただし、AndroidアプリケーションでJava 8とJava 8の相互依存を試すことはできますが、親リポジトリへのマージ要求はまだ受け入れられていません。 これを行うには、まずプラグフォーク自体をビルドする必要があります。



 #      upstream  : PLUGIN_REVISION=a79e45bc0721bfea97ec139311fe31d959851476 #  : git clone https://github.com/lyubomyr-shaydariv/android-maven-plugin.git #   ,    : cd android-maven-plugin git checkout $PLUGIN_REVISION #  : mvn clean package -Dmaven.test.skip=true #   target,        Maven-: cd target cp android-maven-plugin-4.3.1-SNAPSHOT.jar android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar #  pom.xml: cp ../pom.xml pom-$PLUGIN_COMMIT.xml sed -i "s/<version>4.3.1-SNAPSHOT<\\/version>/<version>4.3.1-SNAPSHOT-$PLUGIN_COMMIT<\\/version>/g" pom-$PLUGIN_COMMIT.xml #   : unzip android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar META-INF/maven/plugin.xml sed -i "s/<version>4.3.1-SNAPSHOT<\\/version>/<version>4.3.1-SNAPSHOT-$PLUGIN_COMMIT<\\/version>/g" META-INF/maven/plugin.xml zip android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar META-INF/maven/plugin.xml # , , : mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file -DpomFile=pom-$PLUGIN_COMMIT.xml -Dfile=android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar
      
      





このすべての後、アプリケーションのpom.xml



構成できます。



 <!--   Java 8    --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!--      Java 8     --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.10</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>unpack-dependencies</goal> </goals> <configuration> <includeScope>runtime</includeScope> <!--    Java 8  --> <includeGroupIds>foo1-java8,foo2-java8.foo3-java8</includeGroupIds> <outputDirectory>${project.build.directory}/classes</outputDirectory> </configuration> </execution> </executions> </plugin> <!--   --> <plugin> <groupId>net.orfjackal.retrolambda</groupId> <artifactId>retrolambda-maven-plugin</artifactId> <version>2.0.6</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>process-main</goal> <goal>process-test</goal> </goals> </execution> </executions> <configuration> <defaultMethods>true</defaultMethods> <target>1.6</target> </configuration> </plugin> <!-- DEX-   Java 8  (    target/classes   ,     dx)     APK --> <plugin> <groupId>com.simpligility.maven.plugins</groupId> <artifactId>android-maven-plugin</artifactId> <version>4.3.1-SNAPSHOT-a79e45bc0721bfea97ec139311fe31d959851476</version> <executions> <execution> <phase>package</phase> </execution> </executions> <configuration> <androidManifestFile>${project.basedir}/src/main/android/AndroidManifest.xml</androidManifestFile> <assetsDirectory>${project.basedir}/src/main/android/assets</assetsDirectory> <resourceDirectory>${project.basedir}/src/main/android/res</resourceDirectory> <sdk> <platform>19</platform> </sdk> <undeployBeforeDeploy>true</undeployBeforeDeploy> <proguard> <skip>true</skip> <config>${project.basedir}/proguard.conf</config> </proguard> <excludes> <exclude>foo1-java8</exclude> <exclude>foo2-java8</exclude> <exclude>foo3-java8</exclude> </excludes> </configuration> <extensions>true</extensions> <dependencies> <dependency> <groupId>net.sf.proguard</groupId> <artifactId>proguard-base</artifactId> <version>5.2.1</version> <scope>runtime</scope> </dependency> </dependencies> </plugin>
      
      





実際、それがすべてです。 このアプローチはJava 8言語ツールのみの使用を意味し、Stream APIなどの標準ライブラリの使用は意味しないことに注意してください。 また、この手法を使用すると、AndroidアプリケーションやJava 8で記述された依存関係と友達になるだけでなく、サードパーティの依存関係のバイトコードも好きなように処理できることを強調したいと思います。 優雅さという点でこのソリューションが完全に気に入っているとは言えません。



おそらく他のプロジェクトビルドシステムでは、物事はずっと簡単です。 Maven自体でこれがもっと簡単かどうか、そしてこのハックがすべて自転車の建物の一部ではないかどうかさえ知りませんが、それにもかかわらず、Mavenに必要なことを強制することに興味がありました。



All Articles