© Ted Hagos, Mario Zechner, J.F. DiMarzio and Robert Green 2020
T. Hagos et al.Beginning Android Games Developmenthttps://doi.org/10.1007/978-1-4842-6121-7_8

8. Testing and Debugging

Ted Hagos1 , Mario Zechner2, J. F. DiMarzio3 and Robert Green4
(1)
Makati, Philippines
(2)
Graz, Steiermark, Austria
(3)
Kissimmee, FL, USA
(4)
Portland, OR, USA
 
What we’ll cover:
  • Types of game testing

  • Unit testing

  • Debugging

  • Android Profiler

We’ve gone through the programming phase of our project; next, we go through testing and debugging. It’s in this stage that we must find all errors and inconsistencies in the code. A polished game doesn’t have rough edges; we need to test it, debug it, and make sure it doesn’t hog computing resources.

Types of Game Testing

Functional testing. A game is basically an app. Functional testing is a standard way of testing an app. It’s called functional because we’re testing the app’s features (also known as functions) as they are specified in the requirement specification—the requirement specification is something you (or the game designer) would have written during the planning stages of the game. This would have been written in a document (usually called functional requirements specification). Examples of what you might find in a functional specification are “user must log in to the game server before entering the game” and “user may be able to select or go back to levels which have been completed; user cannot select a level which has not been completed.” The testers, usually called QA or QC (short for quality assurance and quality control, respectively), are the ones who carry out these tests. They will create test assets, craft a test strategy, execute them, and eventually report on the results of the executions. Failing tests are usually assigned back to the developer (you) to fix and resubmit. What I’m describing here is a typical practice for a development team that has a separate or dedicated testing team; if you’re a one-person team, the QA will most likely be you as well. Testing is an entirely different skill; I strongly encourage you to enlist the help of other people, preferably those who have experience in testing.

Performance testing . You could probably guess what this type of testing does just from its name. It pushes the game to its limit and sees it performs under stress. What you want to see here is how the game responds when subjected to above-normal conditions. Soak testing or endurance testing is a kind of performance testing; usually, you leave the game running for a long long time and in various modes of operation, for example, leave the game for a really long time while it’s paused or at the title screen. What you’re trying to find here is how the game responds to these conditions and how it utilizes system resources like the memory, CPU, network bandwidth, and so on; you will use tools like the Android Profiler to carry out these measurements.

Another form of performance testing is volume testing ; if your game uses a database, you might want to find out how the game will respond when data is loaded to the database. What you’re checking is how the system responds under various loads of data.

Spike testing or scalability testing is also another kind of performance test. If your game depends on a central server, this test will usually raise the number of users (device endpoints) connected to the central server. You’d want to observe how a spike in number of users affects player experience; is the game still responsive, was there an effect on frames per second, are there lags, and so on?

Compatibility testing is where you check how the game behaves on different devices and configurations of hardware/software. This is where AVDs (Android Virtual Devices) will come in handy; because AVDs are simply software emulators, you don’t have to buy different devices. Use the AVDs whenever you can. There will be some games that will be difficult to test reliably on emulators; when you’re in that situation, you really have to fork over money for testing devices.

Compliance or conformance testing . This is where you check the game against Google Play guidelines on apps or games; make sure you read Google Play’s Developer Policy Center at https://bit.ly/developerpolicycenter. Make sure you are also acquainted with PEGI (Pan European Game Information) and ESRB (Entertainment Software Rating Board). If the game has objectionable content that’s not aligned with a specific rating, they need to be identified and reported. Violations could be a cause for rejection, which may result in costly rework and resubmission.

Localization testing is important especially if the game is intended for global markets. Game titles, contents, and texts need to be translated and tested in the supported languages.

Recovery testing . This is taking edge case testing to another level. Here, the app is forced to fail, and you’re observing how the application behaves as it fails and how it comes back after it fails. It should give you insight whether you’ve written enough try-catch-finally blocks or not. Apps should fail gracefully, not abruptly. Whenever possible, runtime errors should be guarded by try-catch blocks; and when the exception happens, try to write a log and save the state of the game.

Penetration or security testing . This kind of testing tries to discover the weaknesses of the game. It simulates the activities that a would-be attacker will do in order to circumvent all the security features of the game; for example, if the game uses a database to store data, especially user data, a pen tester (a professional who practices penetration testing) might play through the game while Wireshark is running—Wireshark is a tool that inspects packets; it’s a network protocol analyzer. If you stored passwords in clear text, it will show up in these tests.

Sound testing . Check if there are any errors loading the files; also, listen to the sound files if there’s a cracking sound or others.

Developer testing . This is the kind of testing you (the programmer) do as you add layers and layers of code to the game. This involves writing test code (in Java as well) to test your actual program. This is known as unit testing. Android developers usually perform JVM testing and instrumented testing; we’ll discuss these some more in the following sections.

Unit Testing

Unit testing is actually functional testing that a developer performs, not the QA or QC. A unit test is simple; it’s a particular thing that a method might do or produce. An application typically has many unit tests because each test is a very narrowly defined set of behavior. So, you’ll need lots of tests to cover the whole functionality. Android developers usually use JUnit to write unit tests.

JUnit is a regression testing framework written by Kent Beck and Erich Gamma; you might remember them as the one who created extreme programming and the other one from Gang of Four (GoF, Design Patterns), respectively, among other things.

Java developers have long used JUnit for unit testing. Android Studio comes with JUnit and is very well integrated in it. We don’t have to do much by way of setup. We only need to write our tests.

JVM Test vs. Instrumented Test

If you look at any Android application, you’ll see that it has two parts: a Java-based behavior and an Android-based behavior.

The Java part is where we code business logic, calculations, and data transformations. The Android part is where we actually interact with the Android platform. This is where we get input from users or show results to them. It makes perfect sense if we can test the Java-based behavior separate from the Android part because it’s much quicker to execute. Fortunately, this is already the way it’s done in Android Studio. When you create a project, Android Studio creates two separate folders—one for the JVM tests and another for the instrumented tests. Figure 8-1 shows the two test folders in Android view, and Figure 8-2 shows the same two folders in Project view.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig1_HTML.jpg
Figure 8-1

JVM test and instrumented test in Android view

../images/340874_4_En_8_Chapter/340874_4_En_8_Fig2_HTML.jpg
Figure 8-2

JVM test and instrumented test in Project view

As you can see from either Figure 8-1 or 8-2, Android Studio went the extra mile to generate sample test files for both the JVM and the instrumented test. The example files are there to serve as just quick references; it shows us what unit tests might look like.

A Simple Demo

To dive into this, create a project with an empty Activity. Create a class, then name it Factorial.java, and fill it up with the code shown in Listing 8-1.
public class Factorial {
  public static double factorial(int arg) {
    if (arg == 0) {
      return 1.0;
    }
    else {
      return arg + factorial(arg - 1);
    }
  }
}
Listing 8-1

Factorial.java

Make sure that Factorial.java is open in the main editor, as shown in Figure 8-3; then, from the main menu bar, go to NavigateTest. Similarly, you can also create a test using the keyboard shortcut (Shift+Command+T on macOS and Ctrl+Shift+T for Linux and Windows).
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig3_HTML.jpg
Figure 8-3

Create a test for Factorial.java

Right after you click “Test,” a pop-up dialog (Figure 8-4) will prompt you to click another link—click “Create New Test” as shown in Figure 8-4.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig4_HTML.jpg
Figure 8-4

Create New Test pop-up

Right after creating a new test, you’ll see another pop-up dialog, shown in Figure 8-5, which I’ve annotated. Please follow the annotations and instructions in Figure 8-5.

You can choose which testing library you want to use. You can choose JUnit 3, 4, or 5. You can even choose Groovy JUnit, Spock, or TestNG. I used JUnit4 because it comes installed with Android Studio.

The convention for naming a test class is “name of the class to test” + “Test”. Android Studio populates this field using that convention.

Leave this blank; we don’t need to inherit from anything.

We don’t need setUp() and tearDown() routines for now, so leave these unchecked.

Let’s check the factorial() method because we want to generate a test for this.

../images/340874_4_En_8_Chapter/340874_4_En_8_Fig5_HTML.jpg
Figure 8-5

Create FactorialTest

When you click the OK button, Android Studio will ask where you want to save the test file. This is a JVM test, so we want to save it in the “test” folder (not in androidTest). See Figure 8-6. Click “OK.”
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig6_HTML.jpg
Figure 8-6

Choose Destination Directory

Android Studio will now create the test file for us. If you open FactorialTest.java, you’ll see the generated skeleton code—shown in Figure 8-7.

The file Factorial.java was created under the test folder.

A factorial() method was created, and it’s annotated as @Test. This is how JUnit will know that this method is a unit test. You can prepend your method names with “test”, for example, testFactorial(), but that is not necessary, the @Test annotation is enough.

This is where we put our assertions.

../images/340874_4_En_8_Chapter/340874_4_En_8_Fig7_HTML.jpg
Figure 8-7

FactorialTest.java in Project view and the main editor

See how simple that was? Creating a test case in Android Studio doesn’t really involve us that much in terms of setup and configuration. All we need to do now is write our test.

Implementing the Test

JUnit supplies several static methods that we can use in our test to make assertions about our code’s behavior. We use assertions to show an expected result which is our control data. It’s usually calculated independently and is known to be true or correct—that’s why you use it as a control data. When the expected data is returned from the assertion, the test passes; otherwise, the test fails. Table 8-1 shows the common assert methods you might need for your code.
Table 8-1

Common assert methods

Method

Description

assertEquals()

Returns true if two objects or primitives have the same value

assertNotEquals()

The reverse of assertEquals()

assertSame()

Returns true if two references point to the same object

assertNotSame()

Reverse of assertSame()

assertTrue()

Tests a Boolean expression

assertFalse()

Reverse of assertTrue()

assertNull()

Tests for a null object

assertNotNull()

Reverse of assertNull()

Now that we know a couple of assert methods, we’re ready to write some test. Listing 8-2 shows the code for FactorialTest.java.
import org.junit.Test;
import static org.junit.Assert.*;
public class FactorialTest {
  @Test
  public void factorial() {
    assertEquals(1.0, Factorial.factorial(1),0.0);
    assertEquals(120.0, Factorial.factorial(5), 0.0);
  }
}
Listing 8-2

FactorialTest.java

Our FactorialTest class has only one method because it’s for illustration purposes only. Real-world code would have many more methods than this, to be sure.

Notice that each test (method) is annotated by @Test. This is how JUnit knows that factorial() is a test case. Notice also that assertEquals() is a method of the Assert class, but we’re not writing the fully qualified name here because we’ve got a static import on Assert—it certainly makes life easier.

The assertEquals() method takes three parameters; they’re illustrated in Figure 8-8.

The Expected value is your control data; this is usually hardcoded in the test.

The Actual value is what your method returns. If the expected value is the same as the actual value, the assertEquals() passes—your code is behaving as expected.

Delta is intended to reflect how close the actual and expected values can be and still be considered equal. Some developers call this parameter the “fuzz” factor. When the difference between the expected and actual values is greater than the “fuzz factor,” then assertEquals() will fail. I used 0.0 here because I don’t want to tolerate any kind of deviation. You can use other values like 0.001, 0.002, and so on; it depends on your use case and how much fuzz your app is willing to tolerate.

../images/340874_4_En_8_Chapter/340874_4_En_8_Fig8_HTML.jpg
Figure 8-8

assertEquals method

Now, our code is complete. You can insert a couple more asserts in the code so you can get into the groove of things, if you prefer.

There are a couple of things I did not include in this sample code. I did not override the setUp() and tearDown() methods because I didn’t need it. You would normally use the setUp() method if you need to set up database connections, network connections, and so on. Use the tearDown() method to close whatever it is you opened in the setUp().

Now, we’re ready to run the test.

Running a Unit Test

You can run just one test or all the tests in the class. The little green arrows in the gutter of the main editor are clickable. When you click the little arrow beside the name of the class, that will run all the tests in the class. When you click the one beside the name of the test method, that will run only that test case. See Figure 8-9.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig9_HTML.jpg
Figure 8-9

FactorialTest.java in the main editor

Similarly, you can also run the test from the main menu bar; go to RunRun.

Figure 8-10 shows the result of the text execution.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig10_HTML.jpg
Figure 8-10

Result of running FactorialTest.java

Android Studio gives you plenty of cues so you can tell if your tests are passing or failing. Our first run tells us that there’s something wrong with Factorial.java; the assertEquals() has failed.

Tip

When a test fails, it’s best to use the debugger to investigate the code. FactorialTest.java is no different than any other class in our project; it’s just another Java file, we can definitely debug it. Put some breakpoints on strategic places of your test code, then instead of “running” it, run the “debugger” so you can walk through it.

Our test failed because the factorial of 1 isn’t 2, it’s 1. If you look closer at Factorial.java, you’ll notice that the factorial value isn’t calculated properly.

Edit the Factorial.java file, then change this line:
return arg + factorial(arg - 1);
to this line
return arg * factorial(arg - 1);
If we run the test again, we see successful results, as shown in Figure 8-11.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig11_HTML.jpg
Figure 8-11

Successful test

Instead of yellow exclamation marks, we now see green check marks. Instead of seeing “Test failed,” we now see “Test passed.” Now we know that our code works as expected.

Debugging

We’ve been writing code for a while now; I’m sure you’ve had some mishaps with your code by now and have seen the various ways that Android Studio prompted your attention about these errors.

Syntax Errors

One of the errors you’ll encounter with annoying frequency is syntax errors. They happen because you wrote something in the code that’s not supposed to be there; or you forgot to write something (like a semicolon). These errors can be as benign as forgetting a closing curly brace or can be as complex as passing the wrong type of argument to a method or a parameterized class when using generics. Fortunately, Android Studio is very competent in spotting these kinds of errors. It’s almost like the IDE is continuously reading the code and compiling it.

Syntax errors are simple enough to solve, and you’ve probably figured it out by now. Whenever you see red squiggly lines or red-colored text in the IDE (as shown in Figure 8-12), just hover the mouse on top of the red-colored text or red squiggly lines, and you should see Android Studio’s tips.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig12_HTML.jpg
Figure 8-12

Syntax errors shown in the editor

The tips typically tell you what’s wrong with the code. In Figure 8-12, the error is Cannot resolve symbol ‘Button’, which means you haven’t imported the Button class just yet. To resolve this, position the mouse cursor on the offending word (Button, in this case), then use the Quick Fix feature (Option+Enter in Mac, Alt+Enter in Windows). Quick Fix in action is shown in Figure 8-13.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig13_HTML.jpg
Figure 8-13

Quick Fix

Runtime Errors

Runtime errors happen when your code encounters a situation it doesn’t expect; and as its name implies, that errant condition is something that appears only when the program is running—it’s not something you or the compiler can see at the time of compilation. Your code will compile without problems, but it may stop running when something in the runtime environment doesn’t agree with what your code wants to do. There are many examples of these things; here are some of them:
  • The app gets something from the Internet, a picture or a file and so on, so it assumes that the Internet is available and there is a network connection. Always. Experience should tell you that isn’t always the case. Network connections go down sometimes, and if you don’t factor this in your code, it may crash.

  • The app needs to read from a file. Just like our first case earlier, your code assumes that the file will always be there. Sometimes, files get corrupted and may become unreadable. This should also be factored in the code.

  • The app performs Math calculations. It uses values that are inputted by users, and sometimes it also uses values that are derived from other calculations. If your code happens to perform a division and in one of those divisions, the divisor is zero, that will also cause a runtime problem.

For the most part, Java’s got your back when dealing with runtime errors. Exception handling isn’t optional in Java. Just make sure that you’re not skimping on your try-catch block; always put Exception handling code, and you should be fine.

Logic Errors

Logic errors are the hardest to find. As its name suggests, it’s an error on your logic. When your code is not doing what you thought it should be doing, that’s logic error. There are many ways to cope with it, but the most common methods are (1) using log statements and (2) using breakpoints and walking/stepping through the code.

Printing log messages is a simple way of marking the footprints of the program; you can do it with the simple System.out.println() statement, but I’d encourage you to use the Log class instead. Listing 8-3 shows a basic usage of the Log class.
public class MainActivity extends AppCompatActivity {
  final String TAG = getClass().getName();
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // ...
  }
  void doSomething() {
    Log.d(TAG, "Log message, doSomething");
  }
}
Listing 8-3

Basic use of the Log class

You can define the TAG variable anywhere in the class, but in Listing 8-3, I defined it as a class member; Log.d() prints a debug message. You can use the other methods of the Log class to print warnings, info, or errors. The other methods are shown here:
Log.v(TAG, message) // verbose
Log.d(TAG, message) // debug
Log.i(TAG, message) // info
Log.w(TAG, message) // warning
Log.e(TAG, message) // error

In each case, tag is a String literal or variable. You can use the tag for filtering the messages in the Logcat window. The message is also a String literal or variable which contains what you actually want to see in the log.

When you run your app, you can see the Log messages in the Logcat tool window. You can launch it either by clicking its tab in the menu strip at the bottom of the window (as shown in Figure 8-14) or from the main menu bar, ViewTool WindowsLogcat.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig14_HTML.jpg
Figure 8-14

Logcat tool window

Walking through the Code

Android Studio includes an interactive debugger which allows you to walk and step through your code as it runs. With the interactive debugger, we can inspect snapshots of the application—values of variables, running threads, and so on—at specific locations in the code and at specific points in time. These specific locations in the code are called breakpoints; you get to choose these breakpoints.

To set a breakpoint, choose a line that has an executable statement, then click its line number in the gutter. When you set a breakpoint, there will be a pink circle icon in the gutter, and the whole line is lit in pink—as shown in Figure 8-15.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig15_HTML.jpg
Figure 8-15

Debugger window

After the breakpoints are set, you have to run the app in debug mode. Stop the app if it is currently running, then from the main menu bar, click RunDebug ‘app’.

Note

Running the app in debug mode isn’t the only way to debug the app. You can also attach the debugger process in a currently running application. There are situations where this second technique is useful; for example, when the bug you are trying to solve occurs on very specific conditions, you may want to run the app for a while, and when you think you are close to the point of error, you can then attach the debugger.

Use the application as usual. When the execution comes to a line where you set a breakpoint, the line turns from pink to blue. This is how you know the code execution is at your breakpoint. At this point, the debugger window opens, the execution stops, and Android Studio gets into interactive debugging mode. While you are here, the state of the application is displayed in the Debug tool window. During this time, you can inspect values of variables and even see the threads running in the app.

You can even add variables or expressions in the Watch window by clicking the plus sign with the spectacles icon. There will be a text field where you can enter any valid expression. When you press Enter, Android Studio will evaluate the expression and show you the result. To remove a watch expression, select the expression and click the minus sign icon on the Watch window.

To resume the program execution, you can click the “Resume program” button at the top of the debugger toolbar—it’s the green arrow pointing to the right. Alternatively, you can also resume the program from the main menu bar, RunResume Program. If you want to halt the program before it finishes naturally, you can click the “Stop app” button on the debugger toolbar; it’s the red square icon. Alternatively, you can do this also from the main menu bar, RunStop app.

Profiler

The profiler gives us insights on how our app/game is using computing resources, like the CPU, memory, network bandwidth, and battery.

The Profiler is new in Android Studio 3. It replaces the Android monitor with its new unified and shared timeline view for the CPU, memory, network, and energy graphs. Figure 8-16 shows the Profiler.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig16_HTML.jpg
Figure 8-16

Profiler

You can get to the Profiler by going to the main menu bar, then selecting ViewTool WindowsProfiler.

It shows the process and device being profiled.

It shows you which sessions to view. You can also add new sessions from here by clicking the + button.

Use the zoom buttons to control how much of the timeline to view.

The new shared timeline view lets you see all the graphs for the CPU, memory, network, and energy usage. At the top, you will also see important app events, like user inputs or Activity state transitions.

As soon as you launch an application, either on an attached device or an emulator, you’ll see its graph on the Profiler.

Note

If you try to profile an APK with a version lower than API level 26, you will see some warnings because Android Studio needs to fully instrument your code. You will need to enable “Advance profiling”; but, if your APK is Oreo or higher, you won’t see any warnings.

If you click any of the charts, the Profiler window will take you to one of the detailed views. If you click the CPU, for example, you’ll see the detailed view for the CPU utilization.

CPU

Figure 8-17 shows the detailed view for the CPU utilization on the sample app I was running.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig17_HTML.jpg
Figure 8-17

CPU view

Aside from the live utilization graph, the CPU detailed view also shows a list of all the threads in the app and their states—you can see if the threads are waiting for I/O or when they are active.

You might have noticed the “Record” button in Figure 8-17; if you click that button, you can get a report on all the methods that were executed in a given period. Notice also the selected trace type in the drop-down (Sample Java Methods); this trace type has a smaller overhead but not as detailed nor as accurate as the instrumented type (Trace Java Methods), meaning the sampled type may miss the execution of a very short-lived method. You might think, “just always use the instrumented type then”—you have to remember, though, that while instrumented type can record every method call, on Android devices before version 8, there is a limit on how much data can be captured; so, if you use the instrumented trace, that limit will be reached quickly. You can change that limit by editing the configuration for the instrumented capture. On the trace type drop-down, choose “Edit Configurations” as shown in Figure 8-18.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig18_HTML.jpg
Figure 8-18

Edit Configurations

../images/340874_4_En_8_Chapter/340874_4_En_8_Fig19_HTML.jpg
Figure 8-19

CPU Recording Configurations

Figure 8-19 shows the “Sampling interval” and “File size limit” settings, which you can use to adjust how frequent the sampling will be and how big of a file size you’d like to allocate for the recording. Just to reiterate, the file size limitation is only present on Android devices that are running Android 8.0 or lower (< API level 26). If your device has a higher Android version, you’re not constrained by these limitations.

If you click record, Android Studio will begin capturing data. Click the “Stop” button when you’d like to stop recording, as shown in Figure 8-20.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig20_HTML.jpg
Figure 8-20

Recording session

When you hit stop, you can take a look at the individual threads, as shown in Figure 8-21.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig21_HTML.jpg
Figure 8-21

Inspecting the threads

Memory

The memory profiler shows, in real time, how much memory your app is consuming. Figure 8-22 shows a snapshot of the memory view as I captured the memory footprint of a test app. As you can see, not only does the graph show how much memory your app is gulping, it also shows the breakdown, for example, how much memory is used by the code, stack, graphics, Java, and so on.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig22_HTML.jpg
Figure 8-22

Memory view

You can force garbage collection (GC) in the memory view. See that garbage can icon at the top? Yup, if you click that, it’ll force a GC. The button to its right is also useful—the icon with a down-pointing arrow inside a box is a memory dump . If you click that, the Java heap will be dumped, and then you can inspect it, as shown in Figure 8-23.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig23_HTML.jpg
Figure 8-23

Java heap

The heap is a preserved amount of storage memory that the Android runtime allocates for our app. When we dumped the heap, it gave us a chance to examine instance properties of objects, as shown in Figure 8-24.
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig24_HTML.jpg
Figure 8-24

Instance view, Reference tab

The Reference tab can be very useful in finding memory leaks because it shows all the references pointing to the object you’re examining.

Another useful tool in the memory view is the Allocation tracker, shown in Figure 8-25.

Click anywhere in the timeline of the memory graph to view the allocation tracker. This will show you a list of all objects that were allocated and deallocated at that point in time.

This shows a list of all classes being used by the app at a point in time.

This shows the list of all those objects allocated and deallocated at a specific point in time.

The tracker even includes the call stack of the allocation.

../images/340874_4_En_8_Chapter/340874_4_En_8_Fig25_HTML.jpg
Figure 8-25

Allocation tracker

Network

Like the other views in the Profiler, the network view also shows real-time data. It lets you see and inspect data that is sent and received by your app; it also shows the total number of connections. Figure 8-26 shows a snapshot of the network profiler .
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig26_HTML.jpg
Figure 8-26

Network profiler

Every time your app makes a request to the network, it uses the WiFi radio to send and receive data—the radio isn’t the most energy efficient; it’s power-hungry, and if you don’t pay attention to how your app makes network requests, that’s a sure way of draining the device battery faster than usual.

When you use the network profiler, a good way to start is to look for short spikes of network activity. When you see sharp spikes that rise and fall abruptly and they’re scattered all over the timeline, that smells like you could use some optimization by batching your network requests so as to reduce the number of times that the WiFi radio needs to wake up and send or receive data.

Energy

By now you’re probably seeing a pattern on how the Profiler works. It shows you real-time data. In the case of the Energy profiler, it shows data on how much energy your app is guzzling—though it doesn’t really show the direct measure of energy consumption, the Energy profiler shows an estimation of the energy consumption of the CPU, the radio, and the GPS sensor. Figure 8-27 shows a snapshot of the Energy profiler .
../images/340874_4_En_8_Chapter/340874_4_En_8_Fig27_HTML.jpg
Figure 8-27

Energy profiler

You can also use the Energy profiler to find system events that affect energy consumption, for example, wake locks, jobs, and alarms.
  • A wake lock is a mechanism for keeping the CPU of the screen on when the device would otherwise go to sleep, for example, when an app plays a video, it may use a wake lock to keep the screen on even when there’s no user interaction—using a wake lock isn’t a problem, but forgetting to release one is; it keeps the CPU on longer than necessary which will surely drain the battery faster.

  • Alarms can be used to run background tasks that are outside your application’s context at specific intervals. When an alarm goes off, it can run some tasks; in case it runs an energy-intensive piece of code, you’ll definitely see it in the Energy profiler.

  • A job can perform actions when certain conditions are met, for example, when the network becomes available. You would usually create a job with JobBuilder and use JobScheduler to schedule the execution; when a job kicks in, you will be able to see them also in the Energy profiler.

That was a quick touch of the Android Studio Profiler; make sure you check out the official documentation at https://developer.android.com/studio/profile/android-profiler. Using the Profiler can give you insights about which part of your game code is hogging resources. Optimizing the use of resources can save battery; your users will thank you for it.

Key Takeaways

  • We’ve talked about various kinds of testing you can do for your games; you don’t have to do them all, but make sure you do the test that applies to your game.

  • Dev testing (unit testing) should be a core development task; try to get into the habit of writing your test cases together with your actual code.

  • Android Studio Profile can inspect your app’s behavior from under the hood. It can give you insights on how the app is consuming resources; use this tool when you’re doing performance testing.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.172.243