Without proper performance monitoring, your application may unnecessarily tie up valuable resources, potentially causing revenue losses that could easily be avoided. While there are many tools and platforms available for benchmarking managed applications, mobile applications are often overlooked.

In this tutorial, we’ll cover the basics of analyzing Android apps. We’ll cover what to look for when profiling Android applications, how to get started with popular tools, and how to reduce resource overuse. Let’s get started!

What is Android Profiling?

Profiling is a software development practice that helps identify performance and resource management bottlenecks in applications.

Android apps are meant to run on Android devices, which often have limited hardware resources. Therefore, you must optimize your application’s resource consumption to provide the best possible experience for your users. Without Android Profiling, performance optimization would be nearly impossible.

What should Android Profiling focus on?

There are several areas you can focus on when profiling your Android app, such as memory. As one of the most critical but limited resources on mobile devices, improper memory management can lead to application non-response errors (ANR) and application crashes.

Processing is what controls your users’ experience as they browse your application. Improper management can cause user interfaces to lag, applications to slow down, and in the worst case, completely freeze.

Most Android apps rely on remote servers to provide content and information. Poor network management can add unnecessary delays to content load times and create a bad experience for your users.

Finally, since all mobile devices run on some form of battery, you need to optimize your apps to consume as little battery as possible. Applications that consume a lot of batteries are usually quickly uninstalled by users.

How to analyze an Android app

There are several ways you can analyze An Android app, but in this section, we’ll cover three.

Profiling on devices through developer tools

You can use the developer tools available on every Android phone to instantly analyze GPU performance. Here’s what you need to do first.

  1. Enable the developer option on your phone
  2. Go to Settings > Developer Options
  3. In the “Monitor” section, select the “Profile GPU Rendering” option
  4. In the dialog box that pops up, select the “On Screen As Bars” option
  5. Open the configuration file for the application you want

You’ll notice a bar chart at the bottom of the screen like the one below.

Image source. Android developer

Each bar in this diagram represents a frame of your application’s user interface. The height of the bar indicates how long it takes for the device to render the frame on the screen. The diagram also contains information such as the time spent by each component in the rendering life cycle, represented by bars of different colors. You can learn more on the Android developer’s website.

Android Studio

Android Studio is the de facto IDE for Android application development, so it has a lot of analytics capabilities. With Android Studio, you can analyze almost anything from memory to batteries. Each metric has a separate profiling section and provides a range of tuning and customization capabilities. We’ll cover Android Studio in more detail in a later section.

Dalvik Debug Monitor Server (DDMS)

If you don’t use Android Studio, or if you’re not happy with the profiling features Android offers on your device, there’s another option. The Android SDK includes a standalone Java application that you can use to monitor the performance of your Android applications in real time.

The profiling tool, called Dalvik Debug Monitor Server, can be launched directly from the command line. DDMS acts as a bridge between your application and your command line, directly connecting to the virtual machine in your phone. The DDMS runs the application, directing the output of the application’s debugger directly to your command line.

DDMS is a very advanced tool, however, it should be noted that this tool was deprecated in Android Studio V3.0. The recommended alternative to DDMS is the new Android Profiler, which we’ll discuss later. Either way, DDMS can come in handy if you’re using earlier versions of Android Studio, or if you’re looking for a way to manually debug Your Android applications.

You can do many things with DDMS, including screen capture, port forwarding, call and SMS spoofing, location data spoofing, and access Logcat, processes, and other application information.

Start using basic profiling

Android Studio is a very detailed Android development and debugging tool. In this section, we’ll provide basic insights on how you can use the profiling tools provided by Android Studio to profile various aspects of your Android application.

Android profiler

The Android Profiler is a set of tools provided by Android Studio for profiling Android applications. You can access it by going to View > Tools Windows > Profiler on the menu bar. Alternatively, you can click on the Profile icon in the toolbar.

When you open the Android Profiler, it looks like the following code.

There is a shared timeline, with simultaneous profiling of your application’s CPU, memory, network, and energy. To begin a detailed analysis of each resource, you can click on each individual timeline.

Note that to access these timelines, you need to connect the Android Profiler to a running session. To do this, you need to connect a physical or virtual Android device to your system, enable debugging, and then launch an application. Android Studio recognizes running applications and generates a real-time timeline.

Memory profiling

The Memory profiler is one of the most frequently used profiling tools in Android Studio. It is important to observe how your application uses the available memory to prevent memory leaks and bloat.

You can also use the memory profiler to look for memory allocation patterns that might indicate performance problems with your application. In addition, you can dump your application’s heap to see which objects are taking up memory on your device. A collection of related heap dumps can help you find memory leaks.

Recording memory allocation activities in various types of user interactions can help you understand where your application is allocating too many objects at once, and whether you are forgetting to free up memory, resulting in memory bloat.

The memory profiling section looks like the picture below.

The tool provides you with a timeline that shows various properties such as.

  • The memory being used by each category is represented by color, that is, Java, local, graphics, and so on.
  • The number of allocated objects represented by numbers on the Y-axis
  • Garbage collection events represented by a trash can icon

When you get a high-level overview of the memory allocation your application does, you can also use the three options in the middle pane to identify individual memory-related activities.

Heap dumps show which objects were created and occupied memory at the time the heap dump was recorded. You can learn about the types of objects allocated in memory, how many of them there are, how much memory they’re using, and much more.

A sample heap dump would look like this.

If you choose to record Java or Kotlin object assignments for further analysis, the tool will display the recorded data, as shown below.

Using the search tool, you can search through this list to determine if a class is assigned, which is useful when debugging the behavior of a particular code.

When you search for the name of your application, it looks like this.

Android Studio gives you these options to profile your application’s memory usage. However, to make the best use of these tools, you need to develop a profiling strategy.

I recommend recording and comparing several heap dumps at regular intervals to understand where your application is leaking memory. In addition, you should keep track of object allocations during heavy and light use of your application to see if this number is unreasonably high, which could indicate a memory management problem in your code.

CPU profiling

Keeping track of your Android app’s CPU activity can help you see if your app is managing its workload well. The CPU profiling tool lists your application’s active threads and plots their activity over time. Here is an example of the results displayed by the CPU profiler tool.

The green bar represents the CPU activity of a thread. The bar changes to yellow if the thread stops the flow of the application to accept input, or gray if the thread is asleep.

You can use this data to identify whether a thread is using more CPU time than it needs. You can also visually see the on-screen rendering time for each frame, which will indicate activities that need to be redone to improve performance.

Network analysis

Network profiler tools come in handy when your application processes a large number of network interactions. You may need to determine which request failed or which endpoint took more time than usual to service your request.

With a network analyzer, you can record the order in which network requests are sent and received, the data exchanged, and the network speed at which interactions occur.

In the example below, an image file is downloaded from Unsplash when the login activity begins.

The blue line indicates the download speed, and the orange line indicates the upload speed. If you use HttpURLConnection or okHTTP Libraries to send and receive requests, you can also view the details of individual requests on this timeline, which is useful when debugging network responses.

The battery analysis

Android Profiler also comes with a built-in battery usage analysis tool called Energy Profiler, which gives you a visual view of how your app is affecting your device’s battery usage over time. You can try performing heavy tasks in your application to check if it has a significant impact on the device’s battery consumption.

In the example below, the application holds a wakeup lock for the first five seconds of run time. You can see that the battery usage is high during this time, even though there is no actual heavy processing. In this way, the energy analyzer helps identify excessive energy use in Android applications.

Best practices for Android resource management

While we can use profiling to identify problems with our Android app, it’s always better to minimize or avoid them in the first place. In this section, we will identify some best practices to help you properly manage your application’s resource usage.

Tip 1: Lighten the load on the UI thread by delegating to the background thread

The Android runtime supports multithreaded programming. According to its architecture, the UI of an Android application is rendered on the main thread, which is why it is called a UI thread.

If you try to perform resource-intensive activities on the UI thread, such as downloading files or processing images, it reduces the processor time available for UI rendering activities, making your application’s UI lag and slow.

To avoid this, you should always allocate a worker thread for heavy work that can run safely in the background to mitigate any lag or deceleration of the UI thread. The Android runtime provides several native libraries that you should consider using in your application whenever applicable.

Tip 2: Avoid nesting layouts two or three levels deep

Android UI is bloated, or rendered in a hierarchy of views and ViewGroups. Views are the visual elements you see on the screen, such as buttons, switches, etc., and viewGroups are containers that hold and arrange views.

As you might guess, all views and Viewgroups consume runtime memory and must be processed to render on screen. In addition, processing that runs on one View or ViewGroup object runs on all of its children. If your application’s user interface is deeply nested, this puts an amazing amount of work on the device, slows down your user interface, and affects the user.

To avoid this, try to design your user interface with the simplest possible hierarchy. Avoid using too many LinearLayouts, which limits your freedom to arrange views inside. Instead, I prefer ConstraintLayout, which helps you create complex UI arrangements without deep nesting.

Tip #3: Reuse AS many UI elements as possible

Many UI elements, such as navigation bars and sidebars, are reused throughout the application. Many novice developers ignore this and recreate these components where needed. For example, let’s assume that the code below is our Title bar.

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:src="@drawable/app_logo" />
</FrameLayout>

Copy the code

Although you can include the Title bar directly in your activities, as shown in the code snippet below, doing so is not the best choice for resource management.

<! -- MainActivity.xml --> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <! -- Title bar here --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/app_logo" /> </FrameLayout> <! -- Rest of the activity.. --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" android:padding="10dp" /> ... </LinearLayout>Copy the code

Instead, you should create a separate XML file for the Title bar’s user interface and incorporate it into your code where necessary.

<! -- MainActivity.xml --> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <! -- Title bar here --> <include layout="@layout/title_bar" /> <! -- Rest of the activity.. --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" android:padding="10dp" /> ... </LinearLayout>Copy the code

While this greatly increases the readability of the code, you can take the layout to the next level by using the Merge tag to reduce unnecessary parent containers. To better understand this, let’s take an example of a layout that contains two TextViews.

<! -- @layout/banner.xml --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/world" /> </ LinearLayout>Copy the code

If you were to include it in another layout, you would always have an unnecessary LinearLayout, wrapped around TextViews. Since XML layout files always require a root parent ViewGroup, you can’t get rid of it, unnecessarily adding nesting to the UI layout. To solve this problem, you can use the merge tag to get rid of the parent LinearLayout in your banner.xml file.

<! -- @layout/banner.xml --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/world" /> </merge>Copy the code

Now, when you include this layout in your main layout, the merge element is ignored and the two TextViews are placed directly in place of the include tag, flattening your UI layout and improving the performance of your UI.

Tip #4: Make good use of context to reduce unnecessary memory leaks

Android resources are aggregated and accessed through an interface called Context. Each activity has its own Context that allows access to specific resources within the activity’s lifecycle. In addition to this, Android apps also have their own Context, which is related to the application lifecycle and is more global in nature.

These contexts are used to access Android resources such as SharedPreferences, databases on the device, and so on. However, to avoid resource leaks, you must remember to use the appropriate Context whenever you create a resource access object in memory.

For example, if you initialize a database access object with an active Context, the scope of the object will be limited to that activity. If you try to use it outside of an activity, you will have to keep the activity’s Context in memory unnecessarily. Instead, you should consider using the application Context to initialize global resource objects.

conclusion

Developing Android apps requires a perfect balance of innovation and optimization. As with any type of development, you need to make sure you’re not wasting resources by writing bad code. Android profiling can help you identify and resolve this situation.

In this guide, we talk in detail about Android Profiling, discussing various areas where you can monitor the performance of your Android applications. We also looked at some of the most popular ways to start dissecting your app, and some best practices to keep in mind when developing your next Android app.

I hope this guide has helped you get into android Profiling and take your Android app development skills to the next level. Happy coding