An important issue when building an app is how to monetize it. With so many apps out there, most people will first try a free app whenever possible. The Android SDK provides developers with the ability to include advertising inside their apps and leverage Google’s advertising resources to upload ads at runtime.
We want to build a stopwatch app that enables users to start the clock, stop it, reset it, restart it, etc. In Version 0, we only build the GUI, reserving a space for an ad banner at the bottom. FIGURE 15.1 shows the version 3 of the app running inside the emulator. The screen is divided into three parts:
▸ At the top, we have the clock.
▸ In the middle, we have a start/stop toggle button and a reset button.
▸ At the bottom, we have the ad banner.
For the two round buttons, we use three drawables, shown in EXAMPLES 15.1, 15.2, and 15.3. We place them in the drawable directory. For the start/stop button, we will toggle the background of the button between start_button.xml and stop_button.xml. The start button has a green circle outline and the stop button has a red circle outline (line 6 of Examples 15.1 and 15.2). The reset button has a gray circle outline (line 6 of Example 15.3).
To display the stopwatch, we use a Chronometer
. The Chronometer
class inherits from TextView
and encapsulates the functionality of a running clock. We want to style the Chronometer
so we code the textViewStyle
style at lines 11–15 in the styles.xml file, shown in EXAMPLE 15.4.
EXAMPLE 15.5 shows the activity_main.xml file, organizing the GUI. A vertical (line 10) LinearLayout
(line 2) organizes the screen into three parts:
▸ A Chronometer
(lines 13–18).
▸ A horizontal (line 21) LinearLayout
(lines 20–59) contains the start/stop and reset buttons.
▸ Another LinearLayout
(lines 61–67), a placeholder for the ad banner (we later change this LinearLayout
to a fragment as recommended by Google).
We assign 4/9 (weight 4 – line 14) of the screen to the Chronometer
element, 4/9 to the buttons (weight 4 – line 22), and 1/9 (weight 1 – line 63) to the LinearLayout
at the bottom. We give an id to the Chronometer
at line 17 because we need to access it from the MainActivity
class. We style it at line 18.
The horizontal LinearLayout
in the middle of the screen contains two LinearLayouts
(lines 27–41 and 43–57), each containing a button (lines 33–40 and 49–56). Each button has a diameter of size 150 pixels (lines 35–36 and 51–52) and a text size of 36. Although it is generally not good practice to hard code dimension values, we do so here to keep the example simple. Furthermore, these dimensions are reasonably small and are expected to work on any device. At lines 39 and 55, we set the background of each button to its corresponding drawable resource. Clicking on the Start/Stop button will trigger a call to the startStop
method (line 40) and clicking on the Reset button will trigger a call to the reset
method (line 56).
In this version, we color the bottom LinearLayout
in light gray (line 66) so that we can visualize where the ad banner will go.
We add do-nothing startStop
and reset
methods to the MainActivity
class (EXAMPLE 15.6) so that the app does not crash when the user clicks on either button.
FIGURE 15.2 shows a preview of the Stopwatch, Version 0, in the environment.
In Version 1, we will code the startStop
and reset
methods to give the app its functionality. In order to do this, we use the functionality of the Chronometer
class, which represents the Model for this app: the start
, stop
, and setBase
methods of the Chronometer
class, shown in TABLE 15.1, enable us to start, stop, and reset the Chronometer
.
The parameter of setBase
, base
, is typically set using the elapsedRealtime
method of the SystemClock
class. Its API is:
public static long elapsedRealtime( )
The elapsedRealTime
method returns, in milliseconds, the amount of time since the last boot, including sleep time.
TABLE 15.1 Selected methods from the Chronometer
class
Method | Description |
---|---|
void start( ) | Start counting ( or restart counting ). |
void stop( ) | Stop counting. |
void setBase( long base ) | Set the time of reference for the count. |
EXAMPLE 15.7 shows the updated MainActivity
class. Since the Start/Stop button toggles between two states, we keep track of that state with a boolean
instance variable, started
(line 12). Since we need access to the Chronometer
in both methods, we add an instance variable for it, chrono
(line 11). Inside the onCreate
method, we instantiate it at line 18 using the findViewbyId
method.
The startStop
method is coded at lines 21–34. If the Chronometer
has already started (line 23), then the Start/Stop button is in its “started” state and we do the following:
▸ Stop the Chronometer
: we do this by calling stop
with chrono
at line 24.
▸ Turn started
to false
(line 25) to specify that the button is now in a “stopped” state.
▸ Change the text of the button to START (line 26).
▸ Switch the background of the button to the drawable defined in start_button.xml (line 27).
Otherwise (line 28), the Chronometer
has not started yet or was stopped, the button is in its “start” state and we do the following:
▸ Start the Chronometer
: we do this by calling start
with chrono
at line 29.
▸ Turn started
to true
(line 30) to specify that the button is now in a “started” state.
▸ Change the text of the button to STOP (line 31).
▸ Switch the background of the button to the drawable defined in stop_button.xml (line 32).
The reset
method (lines 36–39) resets chrono
to 00:00
by calling the setBase
method, passing the current time in milliseconds. Thus, when we call reset at time t
, 00:00
is considered to be time t
.
FIGURE 15.3 shows the Stopwatch, Version 1, running inside the emulator. The clock is running and the Start/Stop button has a red circle outline and says STOP.
There is one issue with Version 1: if we stop the clock and restart it later, it does not restart where we stopped it. In fact, when we stop the Chronometer
, it keeps running in the background. In Version 2, we fix that problem so that when we stop the Chronometer
at time t
, it restarts at time t
.
To implement this, when we start or restart the Chronometer
, we need to subtract the time elapsed since we stopped the Chronometer
from the value returned by the elapsedRealtime
method. Rather than doing this inside the MainActivity
class, the Controller, we create a utility class, ClockUtility
, shown in EXAMPLE 15.8. The Model for this app is now comprised of the Chronometer
and ClockUtility
classes. Furthermore, the functionality of the ClockUtility
class is reusable in other apps.
The ClockUtility
contains one static
method, milliseconds
: it converts a String
that is formatted like a String
displayed inside a Chronometer
to its equivalent number of milliseconds. The format of the String clock
(line 10) is expected to be hh:mm:ss
or mm:ss
where hh
represents the number of hours (between 00 and 23), mm
the number of minutes (between 00 and 59), and ss
the number of seconds (between 00 and 59). In order to convert the String
clock
to a number of milliseconds, we use the parseInt
method of the Integer
class, which throws
a NumberFormatException
. Although it is not mandatory because NumberFormatException
is unchecked, we prefer to use try
and catch
blocks (lines 15–26).
We convert clock
to an array at line 12. If there are three elements in that array (line 16), the number of milliseconds is equal to hh * 60 * 60 * 1000 + mm * 60 * 1000 + ss * 1000
(lines 17–19). If there are two elements in that array (line 20), the number of milliseconds is equal to mm * 60 * 1000 + ss * 1000
(lines 21–22). If clock
does not have the proper formatting, the method returns 0
.
EXAMPLE 15.9 shows the updated MainActivity
class. Inside the startStop
method, if we restart the Chronometer
(lines 29–33), we first reset it to where it had previously stopped at line 29 by calling the resetChrono
method. Inside resetChrono
(lines 42–46), we convert the current value of chrono
to a number of milliseconds at lines 43–44. At line 45, we reset its base (i.e., its starting value) to its value when it stopped. TABLE 15.2 shows possible values returned by the elapsedRealtime
and milliseconds
methods when the user clicks on START, then STOP when chrono
shows 10:00
, then START again.
TABLE 15.2 Values returned by elapsedRealtime
and milliseconds
assuming a START, STOP, START sequence
Action | Chrono |
Time | Value Returned by elapsedRealtime |
Value Returned by milliseconds |
---|---|---|---|---|
Start | 00:00 | t1 | 695064 | 0 |
Stop | 00:10 | t2 | 705777 | 10000 |
Start | 00:10 | t3 | 859134 | 10000 |
Now, when we run the app, we can stop the Chronometer
and it will restart where it was stopped. For example, if the Chronometer
says 00:15
when we stop it and we wait 10 seconds before clicking on the Start/Stop button, the Chronometer
will restart at 00:15
, not 00:25
.
In Version 3, we place an ad at the bottom of the screen. The com.google.android.gms.ads
package provides a set of classes to display an ad and manage the ad. TABLE 15.3 shows some of these classes.
TABLE 15.3 Selected classes from the com.google.android.gms.ads
package
Class | Description |
---|---|
AdView | A subclass of View to display an ad banner. |
AdSize | Encapsulates the size of a banner ad. |
AdRequest | Encapsulates a set of marketing characteristics such as location, birthday, keywords, etc., so that the ad can target demographics related to the app. |
However, the com.google.android.gms.ad
s package is not part of the standard Android SDK, but it is part of Google Play services. Thus, in order to use it, we edit the build.gradle file as shown in EXAMPLE 15.10. Since we edited the build.gradle file, we need to sync the project.
We edit the activity_main.xml file and give an id to the last LinearLayout
, where we place the ad, as shown at line 60 in EXAMPLE 15.11. Since we do not fully control the size of the ad that will be served, we want the width of the screen to be maximal. Thus, we delete lines 7 and 8 of Example 15.5 so that we do not have any left and right padding for the overall LinearLayout
.
The AdSize
class encapsulates the size of a banner ad. It provides constants to match various industry standard sizes, as well as constants to cause the width and height to be relative to the device’s width and height. TABLE 15.4 shows some of these constants. We can use the FULL_WIDTH
and AUTO_HEIGHT
constants to create an AdSize
object using an AdSize
constructor, whereas we can use the other constants, such as SMART_BANNER
, to specify a premade AdSize
object.
The AdView
class includes methods to create and manage a banner ad. Its direct superclass is ViewGroup
, itself a subclass of View
. Thus, AdView
inherits from View
. TABLE 15.5 shows some methods of the AdView
class.
We must set the size and ad unit id of an AdView
before we can load an ad into the AdView
. Otherwise, an (unchecked) IllegalStateException
will be thrown when we try to load the ad. In order to obtain an ad unit id from Google, we must be a registered Android developer. Among other things, the developer uses the ad unit id to generate ad revenues. The ad unit id is a String
that we can obtain from AdMob, the platform that Google uses for managing ads, at the following URL:
TABLE 15.4 Selected constants of the AdSize
class
Constant | Data Type | Description |
---|---|---|
AUTO_HEIGHT | int | Causes the height of the ad to scale based on the height of the device. |
FULL_WIDTH | int | Causes the width of the ad to match the width of the device. |
BANNER | AdSize | Mobile Marketing Association ad size of 320 × 50 dip. |
SMART_BANNER | AdSize | Dynamically sized to full width and auto height. |
TABLE 15.5 Selected methods of the AdView
class
Method | Description |
---|---|
public AdView( Context context ) | Constructs an AdView. |
public void setAdSize( AdSize adSize ) | Sets the size of the banner ad. The argument can be one of the constants of the AdSize class. |
public void setAdUnitId( String adUnitId ) | Sets the ad unit id. |
public void loadAd( AdRequest request ) | Loads the ad on a background thread. |
https://support.google.com/admob/v2/answer/3052638
AdMob ad unit ids have the following format:
ca-app-pub-XXXXXXXXXXXXXXXX/NNNNNNNNNN.
For developers who are not registered and want to test an app containing an AdView
, Google provides a test ad unit id. We include it in the strings.xml file, shown in EXAMPLE 15.12 (lines 3–4). We actually do not use that String
in Version 3, but we do use it in Versions 4 and 5.
The following shows a code sequence creating an AdView
, setting its size and setting its ad unit id, assuming we are inside an Activity
class:
// Create a banner ad; assume this is an Activity reference AdView adView = new AdView( this ); // Set ad size adView.setAdSize( AdSize.SMART_BANNER ); // Set the ad unit id; use the default String from Google String adUnitId = "ca-app-pub-3940256099942544/6300978111"; adView.setAdUnitId( adUnitId );
TABLE 15.6 Selected methods of the AdRequest.Builder
class
Method | Description |
---|---|
public AdRequest.Builder( ) | Default constructor. |
public AdRequest.Builder addKeyword( String keyword ) | Adds a keyword for targeting purposes and can be called several times to add several keywords. |
public AdRequest.Builder setGender( int gender ) | Sets the user’s gender for targeting purposes. |
public AdRequest.Builder setBirthday( Date birthday ) | Sets the user’s birthday for targeting purposes. |
public AdRequest.Builder setLocation( Location location ) | Sets the user’s location for targeting purposes. |
public AdRequest.Builder addTestDevice( String deviceId ) | Sets up a device to receive test ads rather than live ads. Use the constant DEVICE_ID_EMULATOR from the AdRequest class to use the emulator. |
public AdRequest build( ) | Constructs and returns an AdRequest with the attributes specified by this AdRequest.Builder. |
Once we have created an AdView
, set its size and ad unit id, we need to create an ad request and load it into the AdView
. The AdRequest
class encapsulates the concept of an ad request. It includes a static
inner class, Builder
, that we can use to set the characteristics of an AdRequest
. TABLE 15.6 shows some methods of the AdRequest.Builder
class. The addKeyword
allows us to add keywords, one per method call, that relate to the app, so that the ad can be better targeted at the typical app user. The setGender
method allows us to target the ad at women, men, or both. The AdRequest
class provides three int
constants, GENDER_FEMALE
, GENDER_MALE
, and GENDER_UNKNOWN
, which we can use as arguments of that method. We can use the setLocation
method to target the ad based on a location. The app can access the GPS, retrieve the location of the user dynamically, and include it in the ad request so that Google services can use the location to better choose and target the ad. All these methods return the AdRequest.Builder
reference that called them so that method calls can be chained. Once all the characteristics of the ad request are set, we can use the build
method to create an AdRequest
object.
The following shows a code sequence creating an AdRequest
, and loading it onto an AdView
:
// Create the AdRequest AdRequest adRequest = adRequestBuilder.build( ); // load the ad adView.loadAd( adRequest );
If we want to use the emulator to test our app, we add this line before calling build
to create the AdRequest
:
adRequestBuilder.addTestDevice( AdRequest.DEVICE_ID_EMULATOR );
If we are a registered developer and we have obtained a valid ad unit id from Google, we can add this line before calling build
to create the AdRequest
for testing purposes. In the final version of the app, before submitting our app to Google Play, we should either delete that line or comment it out.
We can obtain the device id, a 32-digit hexadecimal string, for a device we use to test our app. We can obtain it by looking at the Logcat output when running the app on a connected device. For the author’s device, here is the Logcat output, also shown in FIGURE 15.4 (if you cannot find it in Logcat, screen the messages using the Ads tag as shown):
Use AdRequest.Builder.addTestDevice( "DE4?????????????????7A" ) to get test ads on this device.
Thus, for the author’s device, we can include the following code to test the app on the tablet (for security and privacy reasons, the device id is partially hidden on Figure 15.4 and has been filled with ? characters below).
String deviceId = "DE4?????????????????7A" adRequestBuilder.addTestDevice( deviceId );
There are three things to add to the AndroidManifest.xml file, shown in EXAMPLE 15.14. A uses-permission
element to access the Internet (lines 5–6), a required meta-data
element in order to use Google services (lines 15–17), and another activity
element as shown at lines 29–32. Note that lines 30 and 31 should be in a single line in the AndroidManifest.xml file.
Figure 15.1, at the beginning of this chapter, shows the Stopwatch app, Version 3, running, including the banner ad at the bottom of the screen. If we are a registered developer and are using our own app unit id, we should not click on the live ad for testing purposes: it is against Google policy to do so. If we want to test the functionality of the banner ad, then we should use test ads, which we can get as follows:
▸ Use the Google provided test ad unit id, or
▸ Request test ads by using AdRequest.Builder.addTestDevice
.
In both cases, we can run on the emulator or a device.
Google recommends using a fragment in which to place the AdView
. A benefit is that a fragment’s XML layout file is reusable in other apps. In Version 4, we place the AdView
inside a fragment instead of a LinearLayout
. Going from Version 3 to 4 involves the following steps:
▸ Create an XML layout file for the fragment.
▸ Change the last LinearLayout
in the activity_main.xml file to a fragment.
▸ Code the fragment class.
▸ Update the MainActivity
class.
EXAMPLE 15.15 shows the XML layout file for our fragment. We place the AdView
element (lines 7–15) inside a RelativeLayout
element (line 2). We give the AdView
an id at line 8 so we can retrieve it inside the fragment class using the findViewById
method. We specify the size and the ad unit id of the AdView
at lines 13–14. At lines 11–12, we center the AdView
horizontally and vertically within the RelativeLayout
.
EXAMPLE 15.16 shows the updated activity_main.xml file: we have replaced the last LinearLayout
element with a fragment
element (lines 59–64). We give it an id at line 60 and specify that it is an instance of the AdFragment
class at line 61. Although we are not using its id in this app, the app will crash at runtime if we do not give a fragment either an id or a tag.
EXAMPLE 15.17 shows our fragment class, which we name AdFragment
. Inside the onCreateView
method, we inflate the fragment_ad.xml file at line 15. Inside the onActivityCreated
method, we retrieve the AdView
specified in the fragment using its id (lines 21–22), we build an AdRequest
(lines 23–31), and load it into the AdView
(line 32–33).
Since all banner ad–related code is in the AdFragment
class, the MainActivity
class is the same as the one in Version 2.
The AdView
class includes life-cycle methods, shown in TABLE 15.7, so that we can avoid unnecessary processing when the app goes to the background or is exited. Inside the fragment class, the onPause
, onResume
, and onDestroy
life-cycle methods are automatically called as the parent’s activity goes into the background, the foreground, or is exited. Thus, we can call the AdView
life-cycle methods from the fragment’s life-cycle methods.
EXAMPLE 15.18 shows the updated AdFragment
class of our app, Version 5. All the other classes and files remain identical. Because we need to access the AdView
from onPause
(lines 38–42), onResume
(lines 44–48), and onDestroy
(lines 50–54), we make the AdView
an instance variable (line 12) so that we can have a direct reference to it. Inside the three methods, we pause, resume, or destroy the AdView
and call the super
method. Note that inside onPause
and onDestroy
, we first pause
or destroy
the AdView
before calling the super
method: we want to pause the processing of the AdView
or destroy the AdView
inside the fragment before pausing or destroying the fragment itself. Inside onResume
, we call the super
method before calling resume
with the instance variable adView
: we want to resume the processing of the fragment before resuming the processing of the AdView
, which is inside the fragment.
TABLE 15.7 The life-cycle methods of the AdView
class
Method | Description |
---|---|
public void pause( ) | Pauses any extra processing associated with this AdView. |
public void resume( ) | Resumes processing associated with this AdView following a call to pause. |
public void destroy( ) | Destroys this AdView. 1package com.jblearning.stopwatchv5; |
Note that the AdFragment
is not reusable as is because the gender and the keywords of this ad request are specific to this app. We could make the ad request builder an instance variable and provide methods inside AdFragment
to set these parameters. This is left as an exercise.
The Chronometer
class encapsulates a stopwatch.
elapsedRealtime
, a static
method of the SystemClock
class, returns, in milliseconds, the amount of time since the last boot, including sleep time.
The com.google.android.gms.ads
package provides a set of classes to display an ad and manage the ad.
The com.google.android.gms.ads
package is part of Google Play services. In order to use it, we need to edit the build.gradle file accordingly.
The AdView
class, which inherits from View
, encapsulates a View
that can display an ad banner.
The AdSize
class encapsulates the size of a banner ad. It includes constants for various industry standard banner sizes.
The AdRequest.Builder
class provides methods to define data, such as gender, location, and keywords, that enable an ad to target a certain type of demographic.
The build
method of the AdRequest.Builder
class returns an AdRequest
reference.
We can use the following ad unit id for testing purposes: ca-app-pub-3940256099942544/6300978111
We must set the size and ad unit id of an AdView
before the AdView
calls the loadAd
method to load an ad.
We can use a fragment for the banner ad, per Google’s recommendations.
Including a Google ad requires three additions to the AndroidManifest.xml file:
The INTERNET
permission is required since the Internet is used in any app that displays a Google ad.
A meta-data
element showing use of Google play services is also required.
An additional activity
element for AdActivity
is required.
The com.google.android.gms.ads package is part of Google Play services
True
False
What class is not in the com.google.android.gms.ads package?
AdView
AdBuilder
AdSize
AdRequest
The Builder class is an inner static class of which class?
AdView
AdRequest
AdSize
AdListener
What method can we use to be sure that we only receive test ads and not live ads during testing?
addTest
addDevice
addTestDevice
addEmulator
If we are not an Android developer and therefore cannot obtain an ad unit id from Google, we cannot test an app that includes banner ads.
True
False: we could use a default ad unit id provided by Google
What method is not one of the methods that can specify the target demographics of an ad request?
setLocation
setBirthday
setGender
setKeyword
What Exception is thrown by the loadAd method when the size or the ad unit id of the AdView has not been set?
IOException
IllegalStateException
LoadException
AdViewException
What element is not one that we need to include in the AndroidManifest.xml file for an app that uses banner ads?
activity
uses-permission
meta-data
uses-storage
Inside an Activity class, create an AdView and set its size so that it meets the IAB leaderboard ad size (you need to look at the AdSize class for that).
Inside an Activity class, create an AdView and set its ad unit id to the default String provided by Google for testing purposes.
Create a simple AdRequest so that we use test ads and not live ads.
Create an AdRequest for men, using the two keywords game
and video
.
Create an AdRequest for women, using a birthday of 1/1/2000.
Create an AdRequest, considering that we will test the app in the emulator, and we only want test ads, not live ads.
Assuming the AdView myAdView has been created, its size and ad unit id have been set, and the AdRequest myRequest has been built, load the ad into myAdView.
Inside the AndroidManifest.xml, code the permission-related element for an app that includes banner ads.
Inside the AndroidManifest.xml, code the extra activity element for an app that includes banner ads.
Inside the AndroidManifest.xml, code the metadata-related element for an app that includes banner ads.
Make a simple flashlight app with a banner ad at the bottom. The flashlight is a yellow View taking the whole screen except the banner ad.
Make a flashlight app with a banner ad at the top. The flashlight is a yellow View taking the whole screen except the banner ad and a SeekBar. The yellow View is dimmable, which you should implement with the SeekBar. Include a Model.
Make an app of your choice (it should have some functionality) with a banner ad whose display is triggered by an event, for example the user clicking on a button.
Make an app of your choice (it should have some functionality) with a banner ad that shows 50% of the time the user runs the app.
Make an app of your choice (it should have some functionality) with a banner ad. The app should include some form of user input (either an EditText, or some list to pick from). You need to use the user input as a keyword for the ad request.
Modify the Stopwatch app, Version 5, making the AdFragment class fully reusable (i.e., it should not set specific gender and keywords). Limit the customization of the ad to gender and keywords.
44.200.94.150