How to build native Android apps on Codemagic
Written by Sneh Pandya
Human brains are naturally wired to process visual information faster and more precisely than text-based information. In the previous article, we talked about the app size and Gradle/YAML optimizations for Android apps. In this article, we will see how to improve performance and optimize your apps for animations and media playback.
Animations improve the visual appearance and experience for your end-users. Animations become useful when you want to display UI changes, transitions, or a specific state of your app.
Types of Animations on Android
The Transitions API framework allows layout changes within an activity. Introduced in Android 4.0, they animate transitions of a view, an Activity, or a Fragment.
This is the most popular type of animation, introduced in Android 3.0. It is used to change the properties of an object. Property Animations can change the state as well as behavior.
These are used to perform simple animations, like changing the size, position, and rotation of the view. They are easy to build but are slower and less flexible than Property Animations. The problem with View Animations is that though the View state changes, its property still remains in the original position.
Best Practices for Animations on Android
1. Avoid the use of multiple independent animations
We can perform multiple animations on a single view using the animator set, but this comes with performance overhead, including the processing overhead of setting up the
AnimatorSet and running two
Animators in parallel. A better approach is to use
2. Make use of new Motion System Guidelines
The container transform pattern is used when the transition includes a persistent container (buttons, lists, or cards) that transitions into a detailed view. Similarly, fade through, fade, shared axis, and other patterns that are included in the new Motion System Guidelines will make your life easier!
3. Retain item position for SharedElementTransition
When working with
RecyclerView, it’s not enough to simply add a listener for images. An item position of
RecyclerView where the transition starts from is also needed. When the user goes back to the previous screen, compare the loaded image position with the retained position at the listener, and start the transition only when they are matched.
4. Understand image loading properly
When working with SharedElementTransitions, Glide tries to optimize image loading and resizes and trims images to match the target view. This introduces a glitch while the transition is in progress. To remove this glitch, apply
dontTransform() like this:
5. Use fragments appropriately
When using SharedElementTransition, always specify a parent fragment that will call the transition methods for all the rest of the fragments. If you directly use the fragment specified in the container and specify
startPostponedEnterTransition(), it may not work.
6. Design your own animations
7. Strike a balance between text and animations
Do not go overkill on your app by adding too many animations or a particular screen. This distracts users and might not be suitable for a seamless user flow. Strike a balance between hints, animation, and text wherever needed. Users need to understand which actions they can perform by looking at the animations.
8. Create a design system
It is always a best practice to define the visual hierarchy for your entire app. Users will have an unpleasant experience if every other screen has different animations or transitions. Ideally, the transitions should be kept the same throughout the app for the same components, i.e., all the activity transitions should provide the same transition experience throughout the app.
9. Use the latest image formats
Using the latest image formats, like WebP, for static images and drawables converted from SVG for vector animations, etc., will save a lot of space and resources, as they are smaller in size, adaptable, backward compatible, and optimized.
10. Test your apps thoroughly
Android devices vary a lot in screen size, resolution, screen ratio, pixel density, frame rate, and so on. It is important to test your animations, transitions, and other visual components across devices and API versions supported by your app to identify pitfalls, glitches, ANRs, and any other unpleasant experiences before you publish your app for your end-users with a bunch of animations and transitions. Remember, seamless experience becomes crucial when working with visual animations and transitions.
11. Learn from the best
The Android Framework Team makes consistent efforts to update the developer documentation and samples on their GitHub repository, which you can take a look at and learn from. They implement the projects with best practices, specifying methods and implementations that make the animations robust and beautiful at the same time. You can explore the repository here, which includes multiple samples for all types of animations.
Providing a seamless media playback experience for your users is crucial. Media playback on Android consumes system resources, such as memory, audio/media controls, codecs, and more. This can degrade the performance of your app and possibly the entire device if not handled correctly. Below are the performance strategies and steps that will help you build robust and more beautiful experiences for your users.
The 4-Step Process to Show Media Control Notifications
- Create a media session for your service.
val mediaSession = MediaSessionCompat(this, “MyAppMediaPlayerService”);
- Declare a media style for your media notification.
val mediaStyle = Notification.MediaStyle().setMediaSession(mediaSession.sessionToken)
- Build a notification and display your media player controls.
val notification = Notification.Builder(this@MyAppMediaPlayerService, YOUR_CHANNEL_ID).setStyle(mediaStyle).build()
- Add user actions for your media player notification.
val pauseAction: Notification.Action = Notification.Action.Builder(pauseIcon, “Pause”, pauseIntent).Build() notification.addAction(pauseAction)
Manage Audio Focus Automatically
More than one Android app can play audio to the same output stream simultaneously. In this case, the Android framework mixes everything together—giving users an unpleasant experience.
When your app needs to play audio, it should request audio focus. However, after the app acquires audio focus, it may not be able to keep the audio focus until it is done playing the audio. Another app can request focus, which preempts your hold on the audio focus. If that happens, your app should pause playing or lower its volume to let users hear the new audio source more easily.
Making use of this new feature is very simple with
SimpleExoPlayer and can be done in just three steps!
- First, create an instance of
AudioAttributesfor your use case. For example, if you’re playing a movie:
// class com.google.android.exoplayer2.audio.AudioAttributes AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(C.USAGE_MEDIA) .setContentType(C.CONTENT_TYPE_MOVIE) .build();
setAudioAttributeswith the second parameter set to
simpleExoPlayer.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ true);
SimpleExoPlayerwill automatically manage audio focus for you. Your app does not need to include any code for requesting or responding to audio focus changes!
If your app is playing a podcast or an audiobook, it would be better to pause it than to continue playing it more softly, which could be distracting. For that,
SimpleExoPlayer will pause while the notification is playing and will automatically resume afterwards.
Best Practices for Media Playback on Android
Here’s a summary of key learnings and best practices for building products and apps that focus heavily on media streaming and media playback:
1. Keep screen on
It might sound obvious that notifying the Android system about media playback, especially video playback, is a vital step. When the video playback begins, specify
setKeepScreenOn(true), and specify
setKeepScreenOn(false) either when the user leaves the screen or when the playback has completed.
2. Add picture-in-picture mode
Android Oreo (8.0) and above support picture-in-picture mode, where two apps can run side by side or one over the other when video playback is in progress. It lets the user watch a video in a small window pinned to a corner of the screen while navigating between apps or browsing content on the main screen.
3. Sense and switch orientation
If your app allows the user to switch between landscape and portrait modes, make sure you use sensor orientation instead of the hardcoded regular orientation. For example, in
SCREEN_ORIENTATION_SENSOR_LANDSCAPE mode, the screen will rotate to adjust the orientation according to how the users are holding the device. This is extremely useful and seamless for users watching videos or charging their device or who just prefer to hold the phone in a certain manner.
4. Pause on headphones removal
When headphones are unplugged, the Android framework publishes an
ACTION_AUDIO_BECOMING_NOISY broadcast. Make your app listen to this broadcast when your audio/video playback is ongoing, and take appropriate action, like pausing the playback, so as not to surprise the user with audio from the speaker.
5. Support device, peripheral, or system volume keys
Users are habituated to using the volume keys provided by the device manufacturer and Android framework to control the volume of audio/video playback. Don’t swim the wave and create your own volume controls. Most headphones/earphones have volume and media control keys. Users naturally interact with these keys, and they expect the keys and peripherals to work seamlessly with your app. Create a
MediaSession to export your media information and media state, like play, pause, skip, volume controls, etc., which then can be consumed by not only headphones but also devices like Google Assistant, Google Home, Android Auto, Android Wear, external Bluetooth speakers, etc.
6. Support adaptive streaming and playback
If you are providing functionalities like media streaming over the network and/or offline downloads, the best thing you can ever do is provide an adaptive stream of your audio/video, with which your end-users can switch the quality or resolution of playback. Ideally, low-, medium-, and high- or HD-quality options for playback or offline downloads are a good feature, especially for mobile devices. Remember, not everyone has stable or high-speed connectivity. Your user retention will grow, and your users will thank you for this!
Building media-rich experiences has become one of the core elements of mobile app development. Animation and media are both powerful and provide excellent user experiences when implemented properly. The information in this article should help you build such experiences for your end-users, ultimately growing your retention and ratings.
More articles about Android app development:
- Android app performance: Gradle and YAML (part 1)
- Android app performance: Modular Architectures and Reusable Code (part 3)
- How to set up an Android project on Codemagic CI/CD?
- Designing complex UI using Android ConstraintLayout
- Introduction to the core concepts of Android Navigation
Sneh is a Senior Product Manager based in Baroda. He is a community organizer at Google Developers Group and co-host of NinjaTalks podcast. His passion for building meaningful products inspires him to write blogs, speak at conferences and mentor different talents. You can reach out to him over Twitter (@SnehPandya18) or via email (email@example.com).