Here's how I would approach it. Which isn't necessarily the best approach.
The first step is to find out what kind of obfuscation the app is using by looking around with your favorite decompiler. Most apps actually aren't obfuscated, or only have name obfuscation. If the names are obfuscated, it will still be annoying to RE, but the code is at least intact.
If the app has name obfuscation, you can try to see if they also obfuscated any popular libraries or sdks. Google some of the string literals and see if anything is recognizable as an open source library. If so, you can write a script to match up the obfuscated code with the open source library code and replace everything with the unobfuscated names. This usually takes some heuristics and manual tuning since things won't match up exactly. Once you've removed the libraries, the amount of code you need to manually RE is greatly reduced.
A few apps have more complicated obfuscation methods. For example, string encryption. Some people will actually run the code to dump the decrypted strings, which has the advantage of (mostly) working regardless of encryption method. I prefer to do everything statically, so I just look at what the decryption functions do and write a script to decrypt all the strings in the code and replace them with the original strings.
I actually haven't seen any Android apps which use reflection for obfuscation (using it to access private apis is another story). But I have seen it in Java programs, and it's still pretty simple to handle. Once you've decrypted all the strings, you can write another script to replace the reflection calls with the original methods.
That's about it as far as Java code obfuscators go. Packers and native code loaders are a different story. I don't have experience with them, but the usual approach is to run the app and dump the unpacked code.