Meet the Logcat

Logcat is an important part of everyday development. Its purpose is to display all logs coming from your device. It displays logs from an emulator or a live device connected. Android has several levels of log messages:

You can filter log messages by these log levels (for example, when you need to see only errors--application crash stacktrace) or log tag (we will explain this later); or by keyword, regex, or the application package. Before we apply any filter, we will configure Android Studio so the log messages are colored differently.

Choose Android Studio | Preferences. In the search field, enter Logcat. Logcat coloring preferences appears, as shown in the following screenshot:

To edit coloring, you must save a copy of the current coloring theme. Choose your theme from the drop-down list and choose Save As. Choose some proper name and confirm:

Choose Assert from the list and uncheck Use inherited attributes to override the color. Make sure that the Foreground option is checked and click on Color located to the right of checkbox to choose a new color for the log text. We will pick some tone of pink, as shown in the following screenshot:

For the Assert level, you can enter the hex code manually: FF6B68. For maximal readability, we recommend the following colors:

To apply changes, click on Apply and then OK.

Open Android Monitor (View | Tool Windows | Android Monitor) and take a look at the messages printed in the Logcat pane. They are colored with different tones for each log level, as shown here:

Now we will define our own log messages, which is also a good opportunity to work with the Android lifecycle. We will put proper log messages for each lifecycle event for the Application class and the screen (activity) we created.

Open your main Application class, Journaler.kt. Extend the code like the following:

    class Journaler : Application() { 

companion object { val tag = "Journaler" var ctx: Context? = null } override fun onCreate() { super.onCreate() ctx = applicationContext Log.v(tag, "[ ON CREATE ]") } override fun onLowMemory() { super.onLowMemory() Log.w(tag, "[ ON LOW MEMORY ]") } override fun onTrimMemory(level: Int) { super.onTrimMemory(level) Log.d(tag, "[ ON TRIM MEMORY ]: $level") } }

Here, we introduced some important changes. We overrode the main lifecycle event for the onCreate()application. We also overrode two additional methods: onLowMemory(), triggered in critical memory situations (actively running processes should trim their memory usage), and onTrimMemory(), when the memory is trimmed.

To log events in our application, we use Log class with static methods, each exposed for a proper log level. Based on this, we have the following methods exposed:

        v(String tag, String msg) 
        v(String tag, String msg, Throwable tr) 
        d(String tag, String msg) 
        d(String tag, String msg, Throwable tr) 
        i(String tag, String msg) 
        i(String tag, String msg, Throwable tr) 
        w(String tag, String msg) 
        w(String tag, String msg, Throwable tr) 
        e(String tag, String msg) 
        e(String tag, String msg, Throwable tr) 

Methods accept the following parameters:

Beside these log methods, there are some additional methods that you can use:

Wtf stands for What a Terrible Failure! Wtf is used to report an exception that should never happen!

We will play some more with the Log class. Open the only screen you have created so far and update the MainActivity class with the following changes:

    class MainActivity : AppCompatActivity() { 
      private val tag = Journaler.tag 
override fun onCreate( savedInstanceState: Bundle?, persistentState: PersistableBundle? ) { super.onCreate(savedInstanceState, persistentState) setContentView(R.layout.activity_main) Log.v(tag, "[ ON CREATE ]") }
override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) Log.v(tag, "[ ON POST CREATE ]") }
override fun onRestart() { super.onRestart() Log.v(tag, "[ ON RESTART ]") } override fun onStart() { super.onStart() Log.v(tag, "[ ON START ]") } override fun onResume() { super.onResume() Log.v(tag, "[ ON RESUME ]") } override fun onPostResume() { super.onPostResume() Log.v(tag, "[ ON POST RESUME ]") }
override fun onPause() { super.onPause() Log.v(tag, "[ ON PAUSE ]") } override fun onStop() { super.onStop() Log.v(tag, "[ ON STOP ]") } override fun onDestroy() { super.onDestroy() Log.v(tag, "[ ON DESTROY ]") } }

We overrode all important lifecycle methods by the order they execute during the activity's lifecycle. For each event, we print the proper log message. Let's explain the purpose of the lifecycle and each important event.

Here, you can see the official diagram from the Android developers' website, explaining the activity lifecycle:

You can find the image at https://developer.android.com/images/activity_lifecycle.png:

Now, when we wrote some code using the Android lifecycle and put proper log messages, we will execute two use cases and look at the logs printed out by Logcat.