【问题标题】:Classpath resource gets duplicated by (probably) sbt类路径资源被(可能)sbt 复制
【发布时间】:2013-04-18 11:59:36
【问题描述】:

我正在尝试使用 sbt 和 jberkel/android-plugin 在 Scala 和 Akka 中设置简单的 Android 项目。我成功地创建了基于getting started tutorial of android-plugin 的简单应用程序。我还设法组装了 ProGuard 配置,使我能够将 Akka 混合到项目中。

现在我想添加 Akka 配置文件,但我无法做到。我在src/main/resources 文件夹中创建了它,并希望它能够正常工作。不幸的是,在 APK 组装过程中,sbt 失败并出现以下错误:

[trace] Stack trace suppressed: run last Akkdroid/android:package-debug for the full output.
[error] (Akkdroid/android:package-debug) 
[error] Using keystore: /home/ghik/.android/debug.keystore
[error] Packaging akkdroid-0.1.apk
[error] /data/Studia/S10/Mobilne/akkdroid/target/resources.apk:
[error] => res/layout/main.xml
[error] => AndroidManifest.xml
[error] => resources.arsc
[error] /data/Studia/S10/Mobilne/akkdroid/target/classes.dex => classes.dex
[error] /data/Studia/S10/Mobilne/akkdroid/target/classes.min.jar:
[error] => akkdroid.conf
[error] => library.properties
[error] => reference.conf
[error] => org/jboss/netty/container/spring/beans.xml
[error] /data/Studia/S10/Mobilne/akkdroid/src/main/resources/akkdroid.conf => akkdroid.conf
[error] 
[error] Error packaging /data/Studia/S10/Mobilne/akkdroid/target/akkdroid-0.1.apk: Duplicate files at the same path inside the APK
[error] Total time: 20 s, completed 2013-04-24 20:32:47

从这些消息中,我推断我的 akkdroid.conf 文件首先被复制(由 ProGuard?)到 classes.min.jar,后来 sbt 尝试构建包含来自 classes.min.jar 和 @987654330 的资源的包@ 它最终会看到我的文件两次。

很遗憾,我不知道为什么会发生这种情况以及如何解决它。

如果有帮助,这是我的 sbt 构建文件(主要从 jberkel's template 生成):

import sbt._

import Keys._
import AndroidKeys._

object General {
  val settings = Defaults.defaultSettings ++ Seq(
    name := "Akkdroid",
    version := "0.1",
    versionCode := 0,
    scalaVersion := "2.10.1",
    platformName in Android := "android-10",
    javacOptions ++= Seq("-source", "1.6", "-target", "1.6")
  )

  val proguardSettings = Seq(
    useProguard in Android := true,
    proguardOption in Android :=
      """-keepclassmembers class * {
        |  ** MODULE$;
        | }
        |-keep public class akka.actor.LocalActorRefProvider {
        |public <init>(...);
        |}
        |-keep public class akka.remote.RemoteActorRefProvider {
        |public <init>(...);
        |}
        |-keep class akka.actor.SerializedActorRef {
        |*;
        |}
        |-keep class akka.remote.netty.NettyRemoteTransport {
        |*;
        |}
        |-keep class akka.serialization.JavaSerializer {
        |*;
        |}
        |-keep class akka.serialization.ProtobufSerializer {
        |*;
        |}
        |-keep class com.google.protobuf.GeneratedMessage {
        |*;
        |}
        |-keep class akka.event.Logging*
        |-keep class akka.event.Logging$LogExt{
        |*;
        |}
        |-keep class scala.Option
        |-keep class scala.Function1
        |-keep class scala.PartialFunction
        |-keep class scala.collection.SeqLike {
        |public protected *;
        |}
        |-keep class akka.**
        |-keepclassmembers class akka.**
        |-keep class org.omg.**
        |-keep class scala.Tuple2
        |-dontskipnonpubliclibraryclassmembers
        |-dontskipnonpubliclibraryclasses
      """.stripMargin
  )

  lazy val fullAndroidSettings =
    General.settings ++
      AndroidProject.androidSettings ++
      TypedResources.settings ++
      proguardSettings ++
      AndroidManifestGenerator.settings ++
      AndroidMarketPublish.settings ++ Seq(
      keyalias in Android := "change-me",
      libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test"
    )
}

object AndroidBuild extends Build {
  javacOptions ++= Seq("-target", "1.6")

  lazy val main = Project(
    "Akkdroid",
    file("."),
    settings = General.fullAndroidSettings ++ Seq(
      libraryDependencies += "com.typesafe.akka" % "akka-actor_2.10" % "2.1.2",
      libraryDependencies += "com.typesafe.akka" % "akka-remote_2.10" % "2.1.2"
    )
  )

  lazy val tests = Project(
    "tests",
    file("tests"),
    settings = General.settings ++
      AndroidTest.androidSettings ++
      General.proguardSettings ++ Seq(
      name := "AkkdroidTests"
    )
  ) dependsOn main
}

如果能得到一些帮助,我将不胜感激。我几乎没有 Android 或 SBT 方面的经验。

【问题讨论】:

    标签: android scala sbt


    【解决方案1】:

    我也遇到了同样的问题。我不知道这是否是最好的处理方法,但我把我的配置文件放在了android项目的raw目录下。然后我将其解析为Config 对象并将其传递给ActorSystem 构造函数。

    object AkkaSystem {
      private var systemOption: Option[ActorSystem] = None
    
      def system(implicit context: Context): ActorSystem = synchronized {
        systemOption match {
          case None =>
            val reader = new InputStreamReader(
              context.getResources.openRawResource(R.raw.configuration)
            )
            systemOption = Option (
              ActorSystem("MySystem", ConfigFactory.parseReader(reader))
            )
            reader.close()
            systemOption.get
          case Some(existingSystem) => existingSystem
        }
      }
    }
    

    然后在我的Activity 类中,我可以使用该AkkaSystem 对象来获取配置的ActorSystem

    class MainActivity extends Activity {
      implicit val context = this
    
      private def mainActor = AkkaSystem.system.actorOf(Props[MainActor], "main")
    }
    

    我同意,如果我可以避免所有这些代码并且需要传递 Context 以获取 Android 资源,那会更好。

    【讨论】:

      【解决方案2】:

      这是另一种似乎可行的方法,避免了使用 Android Context 将 Akka 配置加载为原始 android 资源的需要。我不确定这是否比将配置文件视为原始 Android 资源更“正确”。

      我在 sbt 项目的顶层创建了一个名为 config 的目录,并将名为 application.conf 的 Akka 配置文件放入该目录中。

      myapp/
          config/
              application.conf
          project/
              Build.scala
              plugins.sbt
          src/
              main/
                  scala/
      

      然后我将以下设置添加到我的 Build.scala:

      unmanagedClasspath in Runtime <+= (baseDirectory) map { bd => Attributed.blank(bd / "config") }
      

      我正在使用Jan Berkel's Android application template,这是我的Build.scala 的来源。上下文中的相关设置如下所示

      object General {
        val settings = Defaults.defaultSettings ++ Seq (
          name := "AkkaWeb",
          version := "0.1",
          <other settings elided>...
          unmanagedClasspath in Runtime <+= (baseDirectory) map { bd => Attributed.blank(bd / "config") }
      )
      

      我必须执行 sbt cleanreload,但之后 application.conf 文件与 Akka 的默认 reference.conf 文件一起出现在 .apk 文件中,并且 Akka 在我读取它时读取它构造一个ActorSystem

      有一点值得注意:如果我在主线程中构造ActorSystem,那么在strict mode中运行时读取配置文件会导致磁盘访问冲突。这是一个单独的问题,与此问题无关,但需要注意一些事项,尤其是当我在构建 ActorSystem 时可以看到明显的延迟。

      此解决方案直接从 Akka 文档 here 复制而来,您可以在其中阅读更多详细信息以了解它到底在做什么。

      更新

      我刚刚注意到别的东西。我的 Build.scala 中的 Maven 工件的顺序会影响 .apk 文件中出现的 reference.conf。如果我希望 akka-actor jar 中的 reference.conf 在我的 @ 中显示为 reference.conf 987654348@ 文件。例如:

      libraryDependencies ++= Seq(
          "com.typesafe.akka" % "akka-actor_2.10" % "2.1.4",
          "io.spray" % "spray-util" % "1.1-M7.1-openssl-M6"
      )
      

      如果这两个工件的顺序颠倒,则生成的 .apk 文件中的 reference.conf 文件将是来自 Spray 工件而不是 Akka 工件的文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-01
        • 2015-11-02
        • 2014-01-05
        • 2016-01-11
        • 1970-01-01
        • 1970-01-01
        • 2013-03-20
        • 2010-12-08
        相关资源
        最近更新 更多