Android Studio Like a Boss

Mastering your IDE is a great way to become more productive. However, it seems like every blog post or video rehashes the same basic tricks. In this 360AnDev talk, we will take a detailed look at powerful but lesser known features. We will bend structural search and replace to our will, develop our own Live Templates, force Android Studio to interact with external tools and we will take a look at lesser known refactorings that you should be using.

Because remember, a productive developer is a happy developer!


Introduction (0:00)

Welcome to Android Studio Like a Boss. My name is Phil, and today we will look at three different features from Android Studio that you might not know about:

Structural search and replace: a way to inspect your code, reason about your code and find interesting patterns; • Live templates: a means to generate code faster; • External tools: a way to have tools that are external to Android Studio. It’s using something else in Android Studio without necessarily leaving your IDE.

Structural Search & Replace (0:52)

Let’s start with structural search and replace. Here is a simple use case:

public class HungarianUser {
    String mID;
    String mFirstName;
    String mLastName;
}

public class User {
    String ID;
    String FirstName;
    String LastName;
}

Let’s say that you and a couple of colleagues were writing an app and realized that you didn’t have any coding centered. Some of them were using the AOSP style, which is the Hungarian notation, so for each of their fields, they would prefix it with the m letter. Other people didn’t like it, so they would use the variable names directly. One would be string mID, the other would be string ID.

After much discussion, you decide to not care about the Hungarian notation. The stuff on the top is bad. Let’s do what’s on the bottom. You can start pre-factoring but after that, you need to know if you did a good job. Is there any m left anywhere? One very bad way to go about it could be to do a “Find in Files” for the letter m. This doesn’t scale; it doesn’t really work.

You might get clever and try to craft the perfect RegExp, but there is a better tool for the job, and it’s “Structural Search and Replace”. How does it work?

Structural search is search, but smarter. It’s a search that understands the structure of Java, so it understands what an if-else statement is and what a field is. You can craft a precise search to find, for example, fields that start with the letter m.

When you develop it, it’s a mix and match of Java structures. You have your curly braces, semicolons, and m variables that you can tweak, and these variables are enclosed in dollar signs. For example:

for ($Type$ $Variable$ : $Expressions$) {
    $Statement$
}

try {
    $TryStatement$;
}    catch($ExceptionType$ $ExceptionDcl$) {
    $CatchStatement$;
}

if ($Condition$) {
    $ThenStatement$;
}    else {
    $ElseStatement$;
}

enum $Enum$ {}

There are many kinds of templates that you can use for everything in the Java language. Those will work for some other languages, but we’re going to focus on Java for this post. You could look at for loops. You could look at ifs, try/catch, even enums.

Constraints (3:17)

Once you have your variables in a search template, you can start to add constraints to refine your search. For example, if you’re looking for a field name, if you want to know if it starts with the letter m, you could put the constraint in the form of a RegExp only on that field, not on the whole structure. And you can also add constraints for the number of occurrences.

Let’s take a look at how this is implemented in real life.

Structural Search Demo

First of all, how do you get to structural search and replace? The easiest way to get to it, with no shortcut by default, is to do the good old searching action or Command-Shift-A. That lets you find pretty much anything in the IDE without using a mouse.

What you’re going to look for is search structurally. The first time you open it, you will see nothing. It will be a black canvas, and it might seem a little bit daunting at first, because now you will need to look at the documentation, and it’s hard.

You don’t need to do this though because there is a nifty feature in structural search: Copy existing template. If you click on it, you will realize that there are a bunch of pre-based search templates. They cover a wide range of use cases, such as searching in for loops, searching for boxing, non-boxing, or even searching for all the fields of a class.

If you ever use structural search, I would advise to open a copy from the template and try to find the one that matches closely with your needs and tweak it from there.

My use case is I want to find all the fields that start with the letter m. Class-based -> fields of the class, seems like a good option:

class $Class$ { 
  $FieldType$ $FieldName$ = $Init$;
}

If I do a Find now, it’s finding every field. Let’s see what we can do to refine this. We have variables so $Class$ would be the class name and $FieldType$ would be an int, for example. The field name is what we are looking for.

So we can click on “Edit variables…”. Go to FieldName, so every variable defined here ends up on this string. Then you can add a constraint. In our case, if we want to know if it’s a Hungarian notation, it should start with the letter m and then there should be an uppercase letter after that and pretty much whatever after that: m[A-Z]*. That’s probably not the best RegExp, but I know that this one does work.

If I press Find now, I find every field that starts with the letter m in my app. There won’t be any false positive because that is not the way clear text searches. It’s really looking only for fields, which is pretty nifty.

But even cooler than that, let’s say that you removed everything, but now you don’t want another engineer to add new ones. What you can do is create some sort of an automatic link check from this, and the way it works is that you go into your “Settings” and look in the “Inspections”. In the Inspections, you’re going to see under “General” a “Structural search inspection”. What this does is it’s going to run a structural search all the time and flag it as a warning if something matches.

To do that, first, you check the checkbox next to it to make sure it’s activated, and then you press the plus sign on the bottom-right corner. You add a search template, and by default, it’s going to take the last structural search that you wrote. You press OK, give it a name like “Don’t use Hungarian notation”, press OK, and it’s going to be flagged as a warning. If you hover over a m variable, you will see that it is flagged as a warning and that you should not be doing this. You could put more details under this so that the engineer knows what to do, but that’s still a very good start.

Now, if you look at your source control, there is a new file or at least a file that was edited under the IDE in special profiles, which means that this structural search can be part of your project. It’s in source control, so every engineer that checks out the code will also get this warning. It’s a great way also to teach the new hires not to do something. That’s the way that structural search works.

Another great feature, even better than a structural search, is Structural Search and Replace. It’s a great way to be able to reason about your code and automate some refactorings that might be more difficult than others. We will take a use case as an example.

Let’s say I have a bunch of Logs. We have Log.d and Log.i. I’m logging errors, but I’m using the default Android logs. I heard about this cool library called Timber by Jake Wharton, and I want to use that instead, but I have these logs all over the place. What am I going to do? I’m going to try to automate this old refactoring.

Before we begin, go to slide X to see what a timber log looks like. The API surface is very similar; instead of log.d, it will be Timber.d and there won’t be a tag:

Log.d(TAG, "debug message");

Timber.d("debug message");

``
`

That's pretty much the only difference. The second difference being in the case of an error. We don't have the tag, but the error comes first instead of last.

The best way to go about doing something like this would be to craft the perfect search query. I usually start from the simplest thing that could work and try to refine it from there. First, I will do a search query like this:

```java

Log.d(TAG, "debug message");

Now, I want to expand this to search for a call with more parameters. I’m going to take my message here and replace it with a variable.

Log.d(TAG, $message$);

Now I am able to also match the method names:

Log.$method$(TAG, $message$);

``
`

And if a log method has a string instead of the `TAG` variable being passed, same thing again. I'm going to add my dollar signs, and now I have covered all but one of my bases:

```java

Log.$method$($TAG$, $message$);

What if there’s another parameter, what can I do for that? I could try to add a variable and try to find it, but the problem is that if I do that, suddenly it’s the only one that gets found. Why is that? It’s because if you look at your search template, by default it expects everything that’s there to occur at least once. It doesn’t really care what the content is, but it does expect the parameter to be there:

Log.$method$($TAG$, $message$, $EXCEPTION$);

What we can do is go to “Edit variables…” and say that $EXCEPTION$ could be there zero or one time. We don’t care. It might be there, it might not be there. And now we do a “Find”, we get all Log calls.

I would like to highlight one of the great features of structural search, which is since it understands the structure of the code, you don’t have to worry as much about smaller details. One of those details is that there is an additional comma usually when there is another parameter, but you don’t have to specify that it’s going to be there or not.

That being said, now we have the perfect search query. Now we will automate our refactoring. We will invoke the structural search with Command+Shift+A, but with a different name. Instead of search structurally, we will do “replace structurally”.

You will see a very similar dialog with a couple of differences. The first one is, just as in any find and replace, you have the replace input box. You also have a couple of other things:

  • “Shorten fully qualified names,” so if you put the fully qualified names in it’s going to put the right thing, but do the auto import when you do your replacement.
  • “Reformat according to style,” which means that you don’t need to worry about matching the current style of your style guide. It’s going to re-run the formatter to make sure that everything is in its place.
  • “Use the static import if possible,” is going to try to only use the static method, and not show the class before it. Sometimes it’s fine. In the case of a logging, it’s not really good because our Log would just be d, instead of Timber.d, so let’s just not do that.

We have a search and a replacement template from before, so we can start with that. We’re going to work with the fully qualified class name:

// Search template:
Log.$method$($TAG$, $message$, $EXCEPTION$);

// Replacement template:
timber.log.Timber.$method$($EXCEPTION$, $message$);

We know that the $TAG$ does not exist and that the $EXCEPTION$ should go first if there is one. That’s all there is to it. If you do a Find, you’re going to see the preview replacement. It should replace it by timber.log.Timber.d. It did work. I just migrated everything to timber in what, three minutes? It’s not too bad. It could run every file. It would work all over the place.

But even cooler than that, just as we were able to do the inspection, we can always do the same thing in the structural search Settings. We can add our replace template, and give it a name such as “Use timber instead of Android log.” Now, if you have a Log in our project, we still get the error, so it’s good documentation for new hires, but what’s interesting now is that if you do an alt-enter, it allows you to replace it in one liner. If somebody starts doing Android logs, they’re going to see an error. They can do their good old quick fix with alt-enter, and they learned something else. And, as always, this thing is checked into source control, so everybody will get access to it.

That was it for structural search and replace. It’s a great way to reason about your code. You can automate some cool refactorings, and if you want to learn more about it, I suggest you start playing with it. Copy some existing templates, see how they react and just tweak them.

Live Templates (15:25)

The second thing I will talk about is live templates. We talked about how to reason about your code. Let’s generate new code. What is a live template?

public class LiveTemplates {
    
    // logt
    |

    public void someMethod() {
        // logd

    }
}

You can type a couple of letters and have it expanded to something that’s really interesting for you. It’s not a simple expansion of text like you would get with other tools. What it’s doing is code expansion but understanding the context in which you are right now. When we are generating an Android Log tag, which is a live template that comes built-in with Android Studio, you are not only expanding. What you’re doing is taking the name of the class, so it understands the name of the class and prefills it for you. In the case of the logd, it already takes the name of the method, so you don’t have to type it again. What does a template look like?

android.util.Log.d(TAG, "$METHOD_NAME$: $content$");

It looks strangely similar to the structural search templates. It steals some part of our coded Java and also a couple of variables that you can tweak. But instead of being used for constraints, it will be to generate new code. Those are variables that you will be able to use to add expressions, which are built-in, and methods that you can call that will provide you with the context that you need.

For example, what is the current method name? What are the parameters of the current method? What is the class name, the file name, the current time? A couple of different things. You can also provide default values, like skip, and those kinds of things.

Live Templates Demo

Let’s see how it would be done. As we said, this is good for logs. But we don’t want to do logs; we want to do Timber logs. Let’s just implement our own timberd.

The easiest way to create a live template is to start by writing the code snippet that you want to generate. What’s really interesting is that once you get your snippet, you can go to Tools -> Save as Live Template. That is going to create a user category in the “Live Templates” and allow you to customize it.

The first step is to create an abbreviation. That’s the same as logd, the letters that you will type to invoke this live template. In our case, let’s call it timberd. Now if I go there and type timberd, I have my snippet.

The way you find live templates is by searching for “live templates” in your Settings. To get there, go to the user folder and start adding variables. For example, you could add a message variable instead:

timber.log.Timber.d("$MESSAGE$");

And now if I type timberd, my cursor is right in the middle of where I want to be, it replaces $MESSAGE. When I press enter, I’m right at the end of the line. Again, I can go on with my life.

This is kind of interesting, but I want to do more. Let’s say I want to have the name of the method we’re in first. Why not? I’m going to add a new variable:

timber.log.Timber.d("$METHOD$ - $MESSAGE$");

And then you can go into Edit variables. What you can do here is add default values, or expressions. We can set our variable $METHOD$ defaut value to methodName. Now if I try to generate my code, I have the method name, so I don’t have to type it, and I can go on with my life yet again.

But there’s still one useless keystroke. I don’t know why I would ever want to change the method name. It doesn’t make much sense. Let’s do away with this altogether. When you are into your variable editing, what you can do is “Skip if defined”. That means that it won’t put your cursor there at all; it will just write it.

Surround With Template

This is one of the simplest kinds of live templates that you can do. There is a slightly more difficult one, which is called a “Surround With…” template. Now we are expanding code. What we can do is wrap a selection of something else. A good example would be a surround with a Callable, for example. It takes the current selection, and surrounds it with another code block. You can think about Runnable, or about action in Rx or something like this.

The way you do this is rather simple. Let’s say we want to wrap with a Runnable. The first thing to do would be to write the code that we want to generate. Let’s go with a new Runnable:

new Runnable() {
    @Override public void run() {
        Timber.d("debug message");
    }
}

We want this thing to happen. What we can do is select it, save the lines as a Live Template, and then what we’re going to do is call it “wrap with runnable”. The interesting thing is that we need to use a special variable that is called $SELECTION$. As I type this you will see that the Edit variable button turns gray. That means this is not a variable that I can edit. This is a reserved keyword. What that means is that this live template will become a “Surround With” live template.

Now, what I can do is, take my selection, do a “Surround With…”, and wrap with runnable. It can be very useful to wrap stuff around, but it’s not perfect. My cursor is right at the beginning of the template. I would prefer if it was somewhere after the log call. Here’s how we can do this.

There is a second reserve keyword that’s called $END$. The $END$ keyword, is where your cursor will be at the end of your code generation. That’s about it for the surround with runnable. They are slightly more complicated, but very useful.

Groovy & Live Template

Now, where things can get crazy is you can run Groovy code as part of a live template to generate more code. An example of this is one of the built-in logs that comes with Android Studio, which is called logm. What logm does is it will log the argument that will emit its name and its argument. For example:

private boolean saySomething(String param1, boolean myBoolean) {
    Log.d(TAG, "saySomething() called with: " + "param1 = [" + param1 + "], myBoolean = [" + myBoolean + "]");
    return true;
}

The name of method code with the name of the program is going to concatenate it at the value, and so on, for every single parameter in your method. Which is pretty cool.

How would you go about writing something like this? There’s no black magic here. All the live templates that are built into Android Studio, you can see and edit them. So for logm what you can see in Settings -> Live Templates -> AndroidLog, is that the template looks reasonable:

android.util.Log.d(TAG, "$METHOD_NAME$() called with: " +  $args$);

But where it gets really crazy is in the content variable. If you go to Edit variables and look at args, it’s harder to understand, I will grant you that:

groovyScript("'\"' + _1.collect { it + ' = [\" + ' + it + ' + \"]'}.join(', ') + '\"'", methodParameters())

One of the interesting features in Android Studio is when you have a string, you can tell it that there is a language in it. You can do an Alt+Enter, do a “Inject Language” or reference and tell Android Studio that, for example, it’s Groovy inside. If I do this, it’s going to change a little bit. What I can do now is there’s now a new edit Groovy fragment and underneath I can see the Groovy code.

What’s interesting with this, is that it’s going to replicate what I type in the screen above, and it’s going to escape the correctors that need to be escaped. For example, if I was to add a double quote on the Groovy editor, it would add the backslash in the top part of the screen.

Let’s take a look at how Groovy scripts work inside of live templates. You need to call groovyScript first, and you need to pass it at least one parameter which is going to be the string that is the Groovy script. But you can also pass in other parameters to this Groovy script, in the name of methodNames, or class names, as we did earlier for live templates.

What you do is you pass the Groovy string itself. At the end, you pass every other parameter that you want to be usable inside of your live template. Inside of the Groovy string, these things will become available as _1, _2, _3, and so on. Looking at our example again:

groovyScript("'\"' + _1.collect { it + ' = [\" + ' + it + ' + \"]'}.join(', ') + '\"'", methodParameters())

You can see _1, that’s actually the methodParameters(). My Groovy is a little rusty, but the concept is right there. You can pass a parameter, you can run the Groovy script, and you can collect collection. You can do pretty much anything that you want. That’s for an inline Groovy script.

Another way to go about this would be having a Groovy script on your drive. You can reference this file directly from your live template. I have one example of such a thing, where I have one little eval script, where everything that I do is simply trying to evaluate some Groovy code. Let’s say one plus one will equal two. You can type whatever you want as an input. What it does is just call a Groovy script that is somewhere on my project, and it will pass the content of the previous variables in my template. I could do something like eval, and do like one plus one equal two, or plus five equals six. It’s just executing Groovy. The Groovy itself is rather easy. But at the very least if you’re using a file, you can have something that’s a bit easier to read.

Sharing Live Templates

The last thing I would like to talk about with live templates is that contrary to structural search and replace, this thing is not part of your project, it’s part of your Android Studio installations. It’s part of your computer. They are not shared when you push something. The good thing is that it’s available in all of your projects, but it’s not available for your teammates. There is a way you can share with your teammates, which strangely enough, is just copy pasting. And you’re going to think, but how does that help me?

The interesting thing is, that when you do a copy-paste, you’re not copying some binary object that only works on your machine. What you’re doing is copying some XML. This XML can be pasted in Slack and HipChat. It could be sent as an email. It could be kept as a text file in your project so that you could share it more easily. Once you get back to your live templates, you can paste it back, and everything’s going to work. You can share code very easily.

That’s live templates in a nutshell. It allows you to generate code. It’s rather easy to do. You type the code that you want to generate, you save it as a template, you tweak it as you wish if you want to add more variables, and you are good to go.

External Tools (28:22)

The last thing I wanted to talk about is external tools, which is a way to use tools outside of Android Studio. What it allows you to do is add a hook to one of these tools, that resides on your computer, and run them straight from Android Studio. You can think of a command-line tool, you could be running ls, or some sort of external lint checker. Or you could open one of the current files inside another editor.

The way that this thing works is you have a new menu called “External Tools” inside of Android Studio. What you can do there is specify a couple of parameters to be able to interact with those. The most obvious one are, you name it, you can assign it categories, and you can synchronize the files after the execution. If you are running a tool that will modify the source code in any shape or form, you can notify Android Studio that it would need to refresh everything to make sure that everything is up to date.

There are also some options, so if you’re using a tool that is a command-line tool, you probably want to open a console, and you can do that. It indicates where something is printed to the standard output or printed to the errors so that you are not overloaded with information.

After that is where you specify the tool itself, and you can add still the same kind of variables that we are getting used to, for example $FilePath$. These variables are a bit different, but they provide good information about the context of the file that you’re currently working on. You can think of what is the filename itself, what is the file path, what is the class path, and what is the current date. Or you could even ask a user for something else.

External Tools Demo

Let’s see how this works. One thing I really like to do is open different files with Atom, which is a great editor. It’s also very useful, for example, with Markdown files. Or if you are looking at pictures, you can open them with Pixelmator or Photoshop. You don’t have to start finding the files and dragging them into the other application.

How would we go about doing this? Open up your “Preferences,” in “Tools,” and go to “External Tools”. Then start adding different tools. For example, let’s add Atom. You can add groups if you want to categorize your tools. It’s also going to give a different menu item. Under “Tool settings,” you find your program, and where it gets interesting is all the parameters that you can pass. You can click on “Insert macro…”, and you don’t have to look at the documentation or try to understand exactly what everything is because, first of all, the names are descriptive. But even when they are not, there’s always a live preview of what it would look like for the current file.

Doing it that way means that you can insert the $FilePath$, press OK, and the next time you right-click on a file, you’re going to get into your external tool, a new one and you can actually open it.

This is external tools in a nutshell. It is very simple, yet very powerful, especially if you want to open it in different kind of apps. You can run command-line apps and get the result directly inside of Android Studio. That’s about it.

Conclusion (33:04)

“Structural Search and Replace” is a great way to reason about your code and do some great automated refactorings. You also have “Live Templates,” where you can generate code, you can have “Surround With” templates, and you can even run Groovy script to generate more code. And using external tools helps you interact with tools outside of the IDE, without quitting the IDE.

Q&A

Q: Could you use live templates to make getters and setters easier?

Philippe: There are already great shortcuts to create getters and setter inside of Android Studio. You can do Command+N, which will open the Generate menu. You can generate getter and setters. It will provide you with a nice dialog where you can tell it exactly which fields you want to generate getters or setters for. That’s one of the best ways to go.

Q: Can structural search and replace be used on things like XML?

Philippe: Yes. It doesn’t work in every language, though. It works for Java, Groovy, XML… It really depends on the language.


Philippe Breault

Philippe Breault

Philippe is a software engineer at American Express and has been passionate about Android since 2010. He worked on a wide variety of Android apps for banks, newspapers, startups, television providers and more. He is a GDE (Google Developer Expert), one of the founding organizers of Droidcon Montréal, and is very active in the Montréal Android Community. Philippe is also the author of the ADB IDEA plugin for Android Studio.