Home » Android » Rebuild required after changing xml layout files in Android Studio

Rebuild required after changing xml layout files in Android Studio

Posted by: admin June 15, 2020 Leave a comment

Questions:

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

How to&Answers:

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.

Answer:

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…

Answer:

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.

Answer:

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