Streamlining Android Development 5: Splitting Android resources like a pro!

How-To Android

Working on a project for some time? Is it starting to get really big and hard to navigate in? Have hundreds, even thousands of drawables, layouts or other resource files? It's easy to get lost. So, let's discuss resource directory splitting.

Dislaimer: This will only work in old-school Project View! Which I still favor over the new Android view.

We've established our problem. Hundreds or thousand of files in just a few folders. This can be a real pain. I'm not very used to double-shift file opening, since I often don't even remember the file names. Which is why I was looking for a useful solution.

The answer turned out to be pretty simple. Let's pick a resource folder as an example.

Say we're dealing withlayouts. Usually, you would have all your layouts in the res/layout/ folder. You know that you have, for example, some kind of Article screen, and you have about twenty layouts which are a part of it. Ideally, you'd like to put them in the sub-folder. But this is not possible. Fortunately, there is a possible workaround.

We can't just create res/layout/layouts_article/ and put our .xml files in there. What we can do, however, is create a new resource structure — for example, res/layouts/layouts_article/layout/ — and put the layout folder there. We can then also create res/layouts/layout for the rest of the layout files. Now we just need to tell Gradle that we have multiple resource folders! Gradle will then look in their root for any known resource directories, like layout, drawable, mipmap, etc.

Here is an example of how the split layout resources look in my project:

Pretty nice, huh? You can split anything you want with this approach.

Now, let's get down to how to edit the Gradle file. It's really simple; we edit the build.gradle file in our module. Here's a small example of how to do that:

android {
    sourceSets {
        main.res.srcDirs = [
            'src/main/res/drawables/drawables_en',
            'src/main/res/drawables/drawables_fr',
            'src/main/res/drawables/drawables_button',
            'src/main/res/drawables/drawables_gradient',
            'src/main/res/drawables/drawables_selector',
            'src/main/res/drawables/drawables_shape',
            'src/main/res/drawables',
            'src/main/res/layouts/layouts_main/feed',
            'src/main/res/layouts/layouts_main/video',
            'src/main/res/layouts/layouts_main/podcasts',
            'src/main/res/layouts/layouts_main/community',
            'src/main/res/layouts/layouts_main/search',
            'src/main/res/layouts/layouts_main/standing',
            'src/main/res/layouts/layouts_main/score',
            'src/main/res/layouts/layouts_main',
            'src/main/res/layouts/layouts_article',
            'src/main/res/layouts/layouts_settings',
            'src/main/res/layouts',
            'src/main/res/mipmaps',
            'src/main/res/values',
            'src/main/res'
        ]
    }
}

You'll notice that I am also splitting my drawables. I like splitting them at least according to shapes, buttons, gradients, selectors and the rest. Putting all mipmaps into one folder is also a great idea, as you usually don't interact with them, and you don't need those X folders visible all the time. Grouping values works well, too, since you often split them by the API, screen density or language.

Just don't forget that you are specifying the resource root directories in the Gradle file. You still need to include proper sub-directories within, which will tell you what kind of resource can be found there. For example, having the directory src/main/res/drawables/drawables_en is not enough; you still need to create the path, like this: src/main/res/drawables/drawables_en/drawable.

And one more a warning!

If you're using numerous flavors, you need to use this small fix. Not sure why, but the compiler is going to think that the resource has not changed, even when you change something in the layout or drawable. This would result in a situation where although you'd be making changes to your resources, you'd still see the old resource after each compilation. The following lines should tell the compiler that the resource has changed and should be invalidated. Remember, if you aren't using flavors, this step shouldn't be necessary.

// Because of flavors and multiple res folders.
afterEvaluate {
    android.applicationVariants.all { variant ->
        tasks.named("generate${variant.name.capitalize()}ResValues").configure { task ->
            task.outputs.upToDateWhen {false}
        }
    }
}

I hope this will make navigating your resources easier and cleaner for you!

Previous Post