Some assignments for filling knowledge gaps. And for your pleasure.
This list of questions was given to one of our Junior Developers — Heinrich Wesson — as a last exercise before becoming an Intermediate Developer. It contains information I wish I, when I started, would have known earlier and some things to avoid that I learned the hard way. This is the first half of the results, a collection of handy skills and information.
This is a series of two articles, the second part can be found here.
1. Private Storage
How do you browse the private storage of an app? Explain the contents.
On any emulator, use the DDMS File Explorer and navigate to data/data/com.mycompany.myapp. As an alternative, you can copy the app’s private directory to a local folder using: adb pull /data/data/com.mycompany.myapp/. Android Studio 3+ will contain a Device File Explorer.
The private storage has a cache folder with temporary files, a files folder containing more persistent content of the app and a shared_prefs folder containing saved preferences. On a real, unrooted device browsing an apps private storage is not possible.
What is a mapping file and why is it important?
When using ProGuard to obfuscate a (signed) APK’s code, a mapping file is generated which provides a translation between the original and the obfuscated code. We keep this file to be able to de-obfuscate crash reports’ stack traces. The crash reporting system (Fabric, Firebase, etc.) does this translation for us, if we provide it with the mapping file.
3. Forbidden Singletons
What is a Singleton? Why are you not allowed to implement this pattern?
A Singleton Pattern restricts the instantiation of a class to one object, using a static reference to it. Singletons seem easy to use because they can be accessed from everywhere. However, they can cause major problems e.g.:
- When multithreading, Singletons can change state from any thread. This may lead to unexpected behaviour.
- Singletons can leak your context.
A great many more drawbacks are mentioned in Singletons in Android. In short: don’t create them, there is always a better solution. Note that not all developers agree with me on this, see for example these opinions.
4. Browsing Preferences
What is an easy way to browse your app’s current (shared) preference values?
You can also browse your app’s private directory and look at the contents of the preference file.
Explain JRebel 2.0 in one sentence. Should we use it? Why? What are the downsides?
I tried it with one of our projects. On small changes in code and xml the build time went down from 13 seconds to 1 second. Procedure of installing it on the emulator went down to 2 seconds. However, almost all of this was lost when the (expensive) Enterprise Edition expired.
6. “Don’t Keep Activities” settings
What is the “Don’t Keep Activities” developer setting and why is it useful?
When the developer option Don’t Keep Activities is checked on the device, all activities that are stopped will be destroyed as well. This simulates the OS’ behaviour when your app is in the background and the system is short on memory; it will destroy your activities.
The setting is useful for testing saved (instance) states and detecting memory leaks.
7. MVP and MVVM
Give two reasons why we should switch from MVP to MVVM and two reasons why we should not.
Pros of MVVM:
- Removes boilerplate code in the presenter — e.g. simple view state changes can be set in xml.
- Lessens the use of findViewById, onClick forwarding, etc. resulting in even cleaner Fragment code.
Cons of MVVM:
- MVVM is more complex than MVP — e.g. the use of ObservableFields, BaseObservable and BindingAdapters will be more difficult to grasp for junior developers. In MVP you’re basically only moving familiar code around to provide more structure to app architecture.
- The code can become messy. Parts of the code might end up in XML, thus complicating testing, development and debugging. You might end up with a forest of (global) BindingAdapters and incomprehensible methods for bound animations, etc.
- Compared to MVVM, more classes are easily testable in most MVP variants because these have no (or less) Android specific dependencies.
What is a Parcelable? When would you use it?
Parcelables are used to pass around POJO’s across process boundaries. This can be done between activities, services, fragments, etc. Parcelables can also be contained in savedInstanceState Bundles, e.g. to restore an object on orientation change. We prefer Parcelables over Serializables because of performance reasons; Parcelables do not use reflection.
One can use the Parceler Library to write a single annotation per Parcelable, evading boiler plate code.
Explain Gerrit. Do you think we should start using it?
Gerrit is a code review tool. Each push needs to be reviewed before it can be pushed onto the “main” branch. Main pros of extensive code reviewing:
- Sharing of knowledge between developers.
- Improved software quality.
For a small development team this is quite a heavyweight solution. In my opinion: some, but not all pushes need reviewing. Because we are a small team of developers, I do not think we need to use this.
10. Retained Memory and GC
Open our latest app and change your device’s orientation a few times. Now find out how many Fragment and Activity objects there are in memory.
After orientation change, selecting Dump Java Heap in the Memory Monitor in Android Studio yields multiple Activity and Fragment objects for the app. However, only one of them occupies actual (dominating) memory.
If I now select Initiate GC to run garbage collection, some of the dead objects are removed. On the second garbage collection all dead objects are removed. LeakCanary does not report any problems.
11. Google App Indexing
Explain the concept. Are there any real live examples where it greatly improved app installs? And app user retainment?
“You can enable Google to crawl through your app content and present your Android app as a destination to users through Google Search results, when that content corresponds to a web page that you own.” — Google docs
Basically it boils down to this:
So for every app page reference you will also need a web page. I found mixed results on if it is worth the effort, see this case study. Also, it seems that Google prefers Accelerated Mobile Pages (AMP) over app indexes. Furthermore, I have a feeling Instant Apps might replace the app indexing eventually.
There is nothing negative about app indexing if your company has to resources to implement it but the effort might not match the gain.
12. Context vs ApplicationContext
What is the difference between getContext() and getApplicationContext()? When would you use the second? And when not?
The Application Context has the same (long running) lifecycle as the whole application process. getContext() will return the (shorter lived) context of the current activity.
Usage of the normal getContext() can cause temporary memory leaks when used in async methods or tasks. When you change orientation, a new activity is created but the old context (activity) can only be garbage collected after the async method or task finished. Therefore, use the Application Context when you wish to outlive the Activity in long running operations. For example, the Google Dev Guide states the use of the application context explicitly on toast messages, probably because they may outlive the activity. More info on Context capabilities: Context, What Context?.
In general, it is best to use getApplicationContext() anywhere you can. Only starting an Activity, opening a dialog and inflating layouts cannot be done with the Application Context.
13. Lifecycle Detail
Why is cleaning up your app (releasing listeners, saving data, etc.) in onDestroy() not a good idea?
onDestroy() is not guaranteed to be called, see the documentation on onDestroy. Therefore, it is good practice to do most of the cleanup in onStop(). Also, because your app will use less memory when in the background the chances are smaller that the OS will kill the app process. Saving data in lifecycle methods is never a good idea. It might slow down orientation change, closing of the app, etc.
The second half of the assignments will follow in the next article, touching Reflection, Generic Types, the mysteries surrounding the Support Library, Java 8 Streams, Automated Testing and more. Part two can be found here.