可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
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...
回答2:
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.
回答3:
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.
回答4:
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']
}
}