9
votes

I am developing android applications and I am using Android Studio.

There is a problem though. When I change the source and I run the app it works fine, But when I change a resource it does not change until I do a Clean Project. e.g. I add a button and run the app, the button does not exist but when I do a clean project it is there. Android studio shows an error when accessing the id of the newly added view.

Please help me solve this problem. Any help would be much appreciated.

Additional information: -Android Studio version: 1.3.1 -Operating system: windows -Gradle version: 2.6

EDIT:

I am having multiple directories as my resources with gradle like this:

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'
        ]
    }
}

When i try to run the project with changed layout it says:

No apk changes detected. Skipping file upload, force stopping package instead. DEVICE SHELL COMMAND: am force-stop com.my.package

5
Android studio shows an error when accessing the id of the newly added view ... And the error is?Phantômaxx
@FrankN.Stein it becomes red as it didn't find the idSaeed Entezari
May be it is bug? Try switch to java-file after changing layout. And re-save java-file by Ctrl-S. This is must be faster that project cleaningTapa Save
I will try to add more information.Saeed Entezari
I also have the same problem. I'm using v1.3.2 and had this problem since v1.3.0. I'm on Ubuntu. It seems there has been a change in gradle tasks which somehow skips building the changed files.2hamed

5 Answers

4
votes

I resolved my problem by not using nested resource folders. I have multiple resource folders though. The workaround is creating your additional resource folders beside your res folder not inside it. My gradle changes like this:

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'
        ]
    }
}

All the changes in additional resource folders now take effect without cleaning and rebuilding the project. I hope it helps you.

4
votes

Here is an updated way to get the same effect regardless of flavor config:

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 }
  }
}

This is still an absolutely terrible workaround to the root issue. It nullifies any up to date or task output caching that would otherwise make your non resource changed builds much faster by skipping these tasks.

The correct fix which is the accepted answer, is to not use nested resource directories as the android gradle plugin originally was designed to support. By not nesting the task inputs should be declared correctly when adding directories to the source sets and gradle will correctly determine when to run the tasks.

If you still want to nest your resource dirs then please read through this readme: https://github.com/davebren/ResourceNestingExample Specifically this: The quirk is that you can't declare a container resource folder before you declare that folder's child resource folders.

My original answer:

A hacky and inefficient workaround is this.

In your module_dir/build.gradle add the following code.

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 }
            }
        }
    }
}

This will make the gradle build regenerate and merge the resources for every build. This loss of incremental generation on top of being a loop that is three levels deep makes this a terrible blob of code.

Before adding this in my project with a single java line changed builds in ~31 seconds and ~35 after adding it in.

Hope they fix the plugin soon.

3
votes

The solution is not to have nested resources directories. Keep all your layouts under /res/layout/ folder. You can short them using some prefix like test_xxxx, or login_xxxx.

Anyway, for some reason Gradle is only taking count of the changes in the layout main directory when it makes an incremental build. The changes in any other sub folder will not be noticed until you make a clean and a complete rebuild of the project.

If anyone knows how to include resources sub-folders and avoid that problem at the same time please let me know...

0
votes

First, Thank you Saeed Enterzari above.

Second, We should care about this: Those res scr dirs in 'res.srcDirs' should exist, otherwise, you have to rebuild your project every time once your changed your xml files.

This is what Saeed Enterzari said, but we can simply it.

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'
        ]
    }
}

After

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

Gradle:6.5, Android Gradle Plugin:4.1.2 Solutions above with source sets did not work. I have changed flavors according to 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
        }
    }
}