在Android Studio中更改xml布局文件后需要重新构建

9
我正在开发Android应用程序,并使用Android Studio。
然而,存在一个问题。当我更改“源”并运行应用程序时,它可以正常工作。但是当我更改“资源”时,直到我进行“清理项目”操作后才会生效。例如,我添加一个按钮并运行应用程序,该按钮不存在,但是当我进行“清理项目”操作后它就出现了。在访问新添加视图的“id”时,Android Studio会显示错误。
请帮助我解决这个问题。非常感谢任何帮助。
附加信息: - Android Studio版本:1.3.1 - 操作系统:Windows - Gradle版本:2.6
编辑: 我有多个目录作为我的Gradle资源,如下所示:
sourceSets{
     main  {
         manifest.srcFile 'src/main/AndroidManifest.xml'
         java.srcDirs = ['src/main/java', '.apt_generated']
         aidl.srcDirs = ['src/main/aidl', '.apt_generated']
         res.srcDirs = [
            'src/main/res',
            'src/main/res/layouts/test',
            'src/main/res/layouts/login',
            'src/main/res/layouts/main',
            'src/main/res/layouts/includes'
        ]
    }
}

当我尝试使用更改后的布局运行项目时,它会显示:

未检测到apk更改。跳过文件上传,强制停止包。 设备shell命令:am force-stop com.my.package


Android Studio在访问新添加视图的ID时显示错误。...那么错误是什么? - Phantômaxx
@FrankN.Stein它变成了红色,因为它找不到该ID。 - Saeed Entezari
我会尝试添加更多信息。 - Saeed Entezari
@FrankN.Stein,我应该添加哪些更多的信息? - Saeed Entezari
1
我也有同样的问题。我正在使用v1.3.2,自v1.3.0以来就一直存在这个问题。我在Ubuntu上。似乎gradle任务发生了变化,导致跳过构建已更改的文件。 - 2hamed
显示剩余5条评论
5个回答

4

这里是一种更新的方法,可以在不考虑口味配置的情况下实现相同的效果:

android.applicationVariants.all { variant ->
  tasks.named("generate${variant.name.capitalize()}Resources").configure { mergeResourceTask ->
    mergeResourceTask.outputs.upToDateWhen { false }
  }
  tasks.named("merge${variant.name.capitalize()}Resources").configure { mergeResourceTask ->
    mergeResourceTask.outputs.upToDateWhen { false }
  }
}

这仍然是一个非常糟糕的解决方法,它会使你的非资源更改构建更快,因为跳过这些任务而使得最新的或任务输出缓存无效。

正确的修复方法是使用Android Gradle插件最初设计支持的非嵌套资源目录。通过不嵌套资源,当向源集添加目录并且Gradle将正确地确定何时运行任务时,应正确声明任务输入。

如果您仍想嵌套您的资源目录,请阅读此自述文件:https://github.com/davebren/ResourceNestingExample 特别是这句话:奇怪的是,在声明该文件夹的子资源文件夹之前,您不能声明容器资源文件夹。

我的原始答案:

一个麻烦且低效的解决方法是这样的。

在您的module_dir/build.gradle中添加以下代码。

afterEvaluate {
  def flavors = []
  def types = []

  android.applicationVariants.all { variant ->
    def flyp = variant.name.split("(?=\\p{Upper})")
    if (!flavors.contains(flyp[0]))
    {
        flavors.add(flyp[0])
    }
    if (!types.contains(flyp[1]))
    {
        types.add(flyp[1])
    }
  }

  tasks.all { Task task ->

    for (String fl : flavors)
    {
      for (String ype : types)
        {
            if (task.name.contains("generate${fl.capitalize()}${ype.capitalize()}Resources") || 
                task.name.contains("merge${fl.capitalize()}${ype.capitalize()}Resources"))
            {
                task.outputs.upToDateWhen { false }
            }
        }
    }
}

这将使gradle构建在每次构建时重新生成并合并资源。这种失去增量生成的做法加上三级深度的循环,使得它成为了一段糟糕的代码。

在我的项目中添加它之前,单个Java代码行的构建时间约为31秒,添加后为约35秒。

希望他们尽快修复插件。


1
这是为使用flavors和types的Android应用程序设置的。如果它是一个库,您会以不同的变体名称获取它们。如果您不使用flavors,则可以跳过整个顶部部分,并直接在任务循环中引用android.applicationVariants.all - TrevJonez
@CoolMind,如果您仍然想嵌套您的目录,我已经添加了一个带有资源嵌套示例的Github链接。此外,我更新了Gradle Groovy DSL示例,直接使用Android变体而不是尝试反向工程笛卡尔坐标系。自原始答案以来,我们学到了很多东西 :) - TrevJonez
1
你应该尽快更新到最新的Gradle版本。Named用于惰性任务配置。 - TrevJonez
@TrevJonez,谢谢!你知道的,升级AS到3.2之后有些东西发生了变化。当我将新的布局(或可绘制对象)从res/layout移动到另一个文件夹时,它的第一个标签会被红色下划线标出,在其他布局或代码中不可见。重新构建无效,只有使其无效>重启并使缓存失效才能解决问题。 - CoolMind
另外,我们可以清除先前的对话,我删除了我的记录。 - CoolMind
第一段代码片段已经解决了我的问题。非常感谢,你节省了我好几个小时的时间。 - Ercan Akkök

4

我解决了我的问题,方法是不使用嵌套的资源文件夹。尽管我有多个资源文件夹,但这个解决方案是将您的其他资源文件夹创建在您的 res 文件夹旁边而不是里面。我的 gradle 更改如下:

sourceSets{
     main  {
         manifest.srcFile 'src/main/AndroidManifest.xml'
         java.srcDirs = ['src/main/java', '.apt_generated']
         aidl.srcDirs = ['src/main/aidl', '.apt_generated']
         res.srcDirs = [
            'src/main/res',
            'src/main/test',
            'src/main/login',
            'src/main/main',
            'src/main/includes'
        ]
    }
}

现在,额外资源文件夹中的所有更改都会生效,无需清理和重建项目。希望对您有所帮助。


可以工作了,谢谢你。在每个资源组前添加一个 res- 前缀会使结构看起来更加美观。 - N. Chebotarev
在我的情况下没有起作用,我正在为同一个应用程序创建35种不同的版本,下面答案中的解决方案十分奏效。 - Atif Rehman
在TrevJonez的回答被添加的时候,我已经想出了这个解决方案并且它对我起作用了。所以我没有测试那个解决方案。你可以自己测试一下。@CoolMind - Saeed Entezari
你现在是否将资源分离到子文件夹中了?对我来说很有趣,但我必须按Build > Rebuild Project。@TrevJonez的解决方案对我没有改变这种行为。 - CoolMind
它对我有效。非常感谢你。我不知道那个建造了这样一个糟糕的项目并且没有解决这个问题而离开的人为什么会这样。这浪费了我一天的时间。再次感谢你。 - Chinese Cat

3
解决方案不是有嵌套的资源目录。将所有布局保留在/res/layout/文件夹下。你可以使用一些前缀,如test_xxxx或login_xxxx来简短地表示它们。
无论如何,由于某种原因,Gradle只在进行增量构建时考虑布局主目录中的更改。任何其他子文件夹中的更改都不会被注意到,直到您清理并完全重建项目。
如果有人知道如何包括资源子文件夹并同时避免这个问题,请告诉我...

1
请仅使用答案回答问题。您应该编辑您的答案以删除后续问题。如果您愿意,可以将其作为单独的新问题发布。 - Mogsdad

0

首先,感谢Saeed Enterzari上面提到的内容。

其次,我们应该关注以下内容: 'res.srcDirs'中的那些res scr目录必须存在,否则,每次更改xml文件后都必须重新构建项目。

这就是Saeed Enterzari所说的,但我们可以简单地概括一下。

sourceSets{
     main  {
         manifest.srcFile 'src/main/AndroidManifest.xml'
         java.srcDirs = ['src/main/java', '.apt_generated']
         aidl.srcDirs = ['src/main/aidl', '.apt_generated']
         res.srcDirs = [
            'src/main/res',
            'src/main/test',
            'src/main/login',
            'src/main/main',
            'src/main/includes'
        ]
    }
}

之后

sourceSets {
    main {
        jniLibs.srcDirs = ['libs'] 
        res.srcDirs = ['src/main/res']
    }
}

0

Gradle:6.5,Android Gradle插件:4.1.2 上述使用源集的解决方案没有起作用。我已根据https://developer.android.com/studio/build/build-variants更改了口味。

ie.:

android{
    flavorDimensions  'system' , 'subtype'
    productFlavors {

        fl1 {
            dimension 'system'
        }

        fl2 {
            dimension 'system'
        }

        fl2sub {
            dimension 'subtype'
        }

        fl1service {
            dimension 'subtype'
        }

        fl1production {
            dimension 'subtype'
        }
    }
    // Remove redundant build variants
    variantFilter { variant ->
        def names = variant.flavors*.name
        if (names.contains("fl1") && names.contains("fl2")) {
            variant.ignore = true
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接