9

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

Anindya Dutta
  • 1,972
  • 2
  • 18
  • 33
Saeed Entezari
  • 3,685
  • 2
  • 19
  • 40

5 Answers5

4

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.

TrevJonez
  • 949
  • 8
  • 12
  • 1
    This is setup for an android application that uses flavors and types. If it is a library you get the variant names differently. If you don't use flavors you may be able to skip the whole top section and reference the `android.applicationVariants.all` directly in the task loop. – TrevJonez Nov 16 '15 at 21:52
  • @CoolMind I added a github link with a resource nesting example if you still want to nest your dirs. Also updated the gradle groovy dsl example to use the android variant directly and not try to reverse engineer the cartesian coordinates. Much has been learned in the years since the original answer :) – TrevJonez Sep 06 '18 at 15:28
  • 1
    You should update to latest Gradle asap. Named is for lazy task configuration. – TrevJonez Sep 06 '18 at 23:27
  • @TrevJonez, thanks! You know, after upgrade AS to 3.2 something has changed. When I move a new layout (or drawable) from `res/layout` to another folder, it's first tag is underlined with red, and in other layouts or code it is not visible. Rebuild now doesn't help, only Invalidate > Restart and invalidate cache. – CoolMind Oct 11 '18 at 16:28
  • Also we can clear previous dialog, I deleted my records. – CoolMind Oct 11 '18 at 16:34
  • The first code snippet has solved my problem. Thank you so much, you saved my hours – Ercan Akkök Jun 27 '20 at 20:00
4

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.

Saeed Entezari
  • 3,685
  • 2
  • 19
  • 40
  • Works, thanks you. Adding a res- prefix to each resourse group makes structure look mutch pretty. – N. Chebotarev Jun 17 '16 at 09:12
  • Did not work in my case, i am creating 35 different flavors for the same app, solution in the below answer works like a charm. – Atif Rehman Mar 20 '18 at 07:50
  • At the time that answer from TrevJonez was added I already came up with this solution and it worked for me. So I didn't test that solution. You can test it for yourself. @CoolMind – Saeed Entezari Sep 04 '18 at 09:03
  • Are you now using separation of resources into subfolders? For me it's interesting, but I have to press Build > Rebuild Project. A solution of @TrevJonez hasn't changed this behaviour for me. – CoolMind Sep 04 '18 at 09:30
  • It works for me. Thank you very much. I don't know why that guy who built such a suck project and didn't fix this question and left... It wasted me a day. Thank you again. – Chinese Cat Oct 12 '18 at 06:26
3

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

ÁngelBlanco
  • 438
  • 4
  • 11
  • 1
    [Please use Answers exclusively to answer the question](//meta.stackoverflow.com/q/92107). You should [edit] your answer to remove the follow-up question. If you wish, you could post that as a separate, new question. – Mogsdad Mar 15 '16 at 18:11
0

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']
    }
}
Chinese Cat
  • 5,359
  • 2
  • 16
  • 17
0

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
        }
    }
}
Vouskopes
  • 167
  • 1
  • 4