--- _db_id: 480 available_flavours: - kotlin content_type: project prerequisites: hard: - topics/kotlin/null-safety - topics/kotlin/annotations - topics/kotlin/constructor-default-values - topics/kotlin/returns-and-jumps soft: [] ready: true submission_type: repo title: Lifecycles and logging. --- ## App overview In this project, you work with a starter app called DessertClicker. In this app, each time the user taps a dessert on the screen, the app "purchases" the dessert for the user. The app updates values in the layout for the number of desserts that were purchased, and for the total amount the user spent. ![](8216c20f5571fc04.png) This app contains several bugs related to the Android lifecycle: For example, in certain circumstances, the app resets the dessert values to 0, and the app continues using system resources even when the app is in the background. Understanding the Android lifecycle will help you understand why these problems happen, and how to fix them. ### Task: Explore the lifecycle methods and add basic logging Every activity and every fragment has what is known as a lifecycle. This is an allusion to animal lifecycles, like the lifecycle of this butterfly—the different states of the butterfly show its growth from birth to fully formed adulthood to death. ![](c685f48ff799f0c9.png) Similarly, the activity lifecycle is made up of the different states that an activity can go through, from when the activity is first initialized to when it is finally destroyed and its memory reclaimed by the system. As the user starts your app, navigates between activities, navigates inside and outside of your app, and leaves your app, the activity changes state. The diagram below shows all the activity lifecycle states. As their names indicate, these states represent the status of the activity. ![](c803811f4cb4034b.png) Often, you want to change some behavior, or run some code when the activity lifecycle state changes. Therefore the `Activity` class itself, and any subclasses of `Activity` such as `AppCompatActivity`, implement a set of lifecycle callback methods. Android invokes these callbacks when the activity moves from one state to another, and you can override those methods in your own activities to perform tasks in response to those lifecycle state changes. The ![](f6b25a71cec4e401.png) A fragment also has a lifecycle. A fragment's lifecycle is similar to an activity's lifecycle, so a lot of what you learn applies to both. In this project, you focus on the activity lifecycle because it's a fundamental part of Android and the easiest to observe in a simple app. Here is the corresponding diagram for the fragment lifecycle: ![](dfde69e6a42d54b3.png) It's important to know when these callbacks are invoked and what to do in each callback method. But both of these diagrams are complex and can be confusing. In this project, instead of just reading what each state and callback means, you're going to do some detective work and build your understanding of what's going on. ### Step 1: Examine the onCreate() method and add logging To figure out what's going on with the Android lifecycle, it's helpful to know when the various lifecycle methods are called. This will help you hunt down where things are going wrong in DessertClicker. A simple way to do that is to use the Android logging API. Logging enables you to write short messages to a console while the app runs, and you can use it to show you when different callbacks are triggered. 1 - Download the DessertClicker starter app and open it in Android Studio. 2 - Compile and run the app, and tap several times on the picture of the dessert. Note how the value for Desserts Sold and the total dollar amount changes. 3 - Open `MainActivity.kt` and examine the `onCreate()` method for this activity ``` override fun onCreate(savedInstanceState: Bundle?) { ... } ``` In the activity lifecycle diagram, you may have recognized the `onCreate()` method, because you've used this callback before. It's the one method every activity must implement. The `onCreate()` method is where you should do any one-time initializations for your activity. For example, in `onCreate()` you inflate the layout, define click listeners, or set up data binding. ![](9be2255ff49e0af8.png) The `onCreate()` lifecycle method is called once, just after the activity is initialized (when the new Activity object is created in memory). After `onCreate()` executes, the activity is considered created. ``` Note: The `onCreate()` method is an override, so within it, you must immediately call `super.onCreate()`. The same is true for other lifecycle methods. ``` In the `onCreate()` method, just after the call to `super.onCreate()`, add the following line. Import the Log class if necessary. (Press Alt+Enter, or Option+Enter on a Mac, and select Import.) `Log.i("MainActivity", "onCreate Called")` The Log class writes messages to the Logcat. There are three parts to this command: - The severity of the log message, that is, how important the message is. In this case, the `Log.i()` method writes an informational message. Other methods in the Log class include `Log.e()` for errors, or `Log.w()` for warnings. - The log tag, in this case `"MainActivity"`. The tag is a string that lets you more easily find your log messages in the Logcat. The tag is typically the name of the class. - The actual log message, a short string, which in this case is `"onCreate called"`. 5 - Compile and run the DessertClicker app. You don't see any behavior differences in the app when you tap the dessert. In Android Studio, at the bottom of the screen, click the Logcat tab. ![](ff9c50376701877f.png) The Logcat is the console for logging messages. Messages from Android about your app appear here, including the messages you explicitly send to the log with the Log.i() method or other Log class methods. 6 - In the Logcat pane, type `I/MainActivity` into the search field. ![](f5c091e2b480edf8.png) The Logcat can contain many messages, most of which aren't useful to you. You can filter the Logcat entries in many ways, but searching is the easiest. Because you used `MainActivity` as the log tag in your code, you can use that tag to filter the log. Adding `I/` at the start means that this is an informational message, created by `Log.i()`. Your log message includes the date and time, the name of the package `(com.example.android.dessertclicker)`, your log tag (with `I/` at the start), and the actual message. Because this message appears in the log, you know that `onCreate()` has been executed. ### Step 2: Implement the onStart() method The `onStart()` lifecycle method is called just after `onCreate()`. After `onStart()` runs, your activity is visible on the screen. Unlike onCreate(), which is called only once to initialize your activity, `onStart()` can be called many times in the lifecycle of your activity. ![](385df4ce82ae2de9.png) Note that `onStart()` is paired with a corresponding `onStop()` lifecycle method. If the user starts your app and then returns to the device's home screen, the activity is stopped and is no longer visible on screen. In Android Studio, with `MainActivity.kt` open, select Code > Override Methods or press `Control+o`. A dialog appears with huge list of all the methods you can override in this class. ![](e1f2460242b2ae.png) 2 - Start entering onStart to search for the right method. To scroll to the next matching item, use the down arrow. Choose onStart() from the list, and click OK to insert the boilerplate override code. The code looks like this: ``` override fun onStart() { super.onStart() ``` ``` Tip: Android Studio inserts your overridden method code in the next available appropriate place in the class. If you'd like to put your lifecycle overrides in a specific place (like at the end of the class), set the insertion point before you use Override Methods. ``` 3 - Inside the `onStart()` method, add a log message: ``` override fun onStart() { super.onStart() Log.i("MainActivity", "onStart Called") } ``` 4 - Compile and run the DessertClicker app, and open the Logcat pane. Type `I/MainActivity` into the search field to filter the log. Notice that both the `onCreate()` and `onStart()` methods were called one after the other, and that your activity is visible on screen. 5 - Press the Home button on the device, and then use the recents screen to return to the activity. Notice that the activity resumes where it left off, with all the same values, and that `onStart()` is logged a second time to Logcat. Notice also that the `onCreate()` method is usually not called again. ``` Note: As you experiment with your device and observe the lifecycle callbacks, you might notice unusual behavior when you rotate your device. You'll learn about that behavior in the next project. ``` ### Task: Use Timber for logging In this task, you modify your app to use a popular logging library called `Timber`. `Timber` has several advantages over the built-in Android `Log` class. In particular, the `Timber` library: - Generates the log tag for you based on the class name. - Helps you avoid showing logs in a release version of your Android app. - Allows for integration with crash-reporting libraries. You'll see the first benefit immediately; the others you'll appreciate as you make and ship bigger apps. ### Step 1: Add Timber to Gradle 1 - Visit this link to the Timber project on GitHub, and copy the line of code under the Download heading that starts with the word implementation. The line of code will look something like this, although the version number may be different. `implementation 'com.jakewharton.timber:timber:4.7.1'` 2 - In Android Studio, in the Project: Android view, expand Gradle Scripts and open the build.gradle (Module: app) file. 3 - Inside the dependencies section, paste the line of code that you copied. ``` dependencies { ... implementation 'com.jakewharton.timber:timber:4.7.1' } ``` 4 - Click the Sync Now link in the top right of Android Studio to rebuild Gradle. The build should execute without errors. ### Step 2: Create an Application class and initialize Timber In this step, you create an `Application` class. `Application` is a base class that contains global application state for your entire app. It's also the main object that the operating system uses to interact with your app. There is a default Application class that Android uses if you don't specify one, so there's always an Application object created for your app, without you needing to do anything special to create it. Timber uses the `Application` class because the whole app will be using this logging library, and the library needs to be initialized once, before everything else is set up. In cases like this, you can subclass the Application class and override the defaults with your own custom implementation. ``` Warning: It might be tempting to add your own code to the Application class, because the class is created before all of your activities and can hold global state. But just as it's error-prone to make readable and writable static variables that are globally available, it's easy to abuse the Application class. Avoid putting any activity code in the Application class unless the code is really needed. ``` After you create your Application class, you need to specify the class in the Android manifest. 1 - In the `dessertclicker` package, create a new Kotlin class called `ClickerApplication`. To do this, expand app > java and right-click on `com.example.android.dessertclicker`. Select New > Kotlin File/Class. 2 - Name the class ClickerApplication and set the Kind to Class. Click OK. Android Studio creates a new `ClickerApplication` class, and opens it in the code editor. The code looks like this: ``` package com.example.android.dessertclicker class ClickerApplication { } ``` 3 - Change the class definition to be a subclass of `Application` and import the `Application` class if necessary. `class ClickerApplication : Application() {` 4 - To override the `onCreate()` method, select Code > Override Methods or press `Control+o`. ``` class ClickerApplication : Application() { override fun onCreate() { super.onCreate() } } ``` 5 - Inside that `onCreate()` method, initialize the `Timber` library: ``` override fun onCreate() { super.onCreate() Timber.plant(Timber.DebugTree()) } ``` This line of code initializes the `Timber` library for your app so that you can use the library in your activities. 6 - Open `AndroidManifest.xml`. ``` At the top of the element, add a new attribute for the ClickerApplication class, so that Android knows to use your Application class instead of the default one.