Android

Android is an operating system developed by Google. Native android applications are developed in Kotlin mostly using Android Studio.

➑️ Google recommends using Kotlin over Java since 2019.

Android projects are managed by Gradle. It's used to:

  • 🌍 manage dependencies (import a library...)
  • βš’οΈ compile the code
  • πŸ—ƒοΈ generate the APK (Android Package) installed on devices

You will have to edit the second build.gradle (Module: XXX).

gradle


Android Framework

Application

An Android application corresponds to an instance of the Application class. The default implementation is usually enough.

Activities and fragments

Applications are made of activities. An activity is most of the time a screen of an application (ex: the login screen).

In practice, an activity typically corresponds to a specific task or user interaction (ex: we may group the register and login screen in one activity).

Activities are associated with one "screen". When we choose to display multiple screens during one activity, we can use fragments.

A fragment is a modular reusable UI component ("screens", menubar...).

πŸ‘‰ You can create apps without any fragments. Using fragments is a choice based on stuff like data handling, code reuse, modularity...

➑️ See also: Single Activity Pattern.

Activity/Fragment lifecycle

Both activities and fragments have a lifecycle which we need to be familiar with in order to know where we will write our code.

For instance, code to handle an event, such as a click on a button, would be in onCreate() (activity) or onViewCreated() (fragment).

View and ViewGroups

A view is a visual element such as a Button. They are grouped in containers called ViewGroups to apply styles on multiple views or create responsive screens (i.g., adapting to the size of the screen).

AndroidManifest.xml

The Android Manifest defines things like:

  • 🏠 the first activity executed when starting the application
  • πŸ” the permissions required by the application
  • πŸ—ƒοΈ the activities, services, and other components...
  • ...

Model View ViewModel (MVVM)

Model View ViewModel, or MVVM for short, is a popular architectural design pattern in which

  • πŸ“¦ The model is the classes used to store Data
    • data class XXX(...)
    • They are usually stored in .data
    • Classes interacting with the API/Room/the FileSystem...

  • πŸ–ΌοΈ The view is what is displayed to the user
    • Most XML displayed (res/layout, res/menu...)
    • They are usually stored in .ui.viewname
  • πŸ’ The ViewModel (ViewModel + LiveData)
    • They are usually stored in .ui.viewname with their View
    • See also: DataBinding

The "main" component is the ViewModel. This is a component that will link the View with the model. When the model is updated, it will update the View. When the View is updated, it will update the model.


  • βž• Controllers (Activities, Fragments)

There are still controllers like in MVC. They are responsible for rendering the view, and listening to events.


Android Application

When an Android application is started, it instantiates the Application class which loads the main activity.

πŸ‘‰ The default implementation is enough most of the time.

Use case: run code only once

Code loaded in an Activity will be run when we navigate between activities, when we rotate the screen... The only way to run code once is inside Application#onCreate.

Ex: the code to create notification channels (see notifications).

Use case: Listen for application-level changes

Ex: app foreground/background
-class MainApplication : Application() {
+class MainApplication : Application(), DefaultLifecycleObserver {

    override fun onCreate() {
        super<Application>.onCreate()
+        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

+    override fun onStart(owner: LifecycleOwner) {
+        // App in the foreground
+    }

+    override fun onStop(owner: LifecycleOwner) {
+        //App in the background
+    }
}
class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
    }
}

In your AndroidManifest.xml, look for the tag "application", and add an attribute "android:name" pointing to your newly created file.

<application
        ...
        android:name=".MainApplication"
        />

Activities

Activities are screens of an application, while in fact, one activity can serve multiple screens using fragments according to the app design.

  • πŸ‘‰ Activities must be declared in AndroidManifest.xml
  • πŸ‘‰ The first screen is usually called "MainActivity"
  • πŸ‘‰ An activity is associated with one layout (ex: R.layout.activity_main)

A short version to declare an activity:

class MainActivity : AppCompatActivity(R.layout.activity_main) {
}

The extended and most commonly used version is:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Load the associated layout
        setContentView(R.layout.activity_main)
    }
}

Activity lifecycle

Android activities' lifecycle is a bit complex. To summarize,

  • πŸ‘‰ onCreate is where you will configure the view
  • πŸ‘‰ Before presenting the activity, onStart is called. If the user press "home"/the activity isn't visible anymore, onStop is called.
  • πŸ‘‰ Before the user can interact with the activity, onResume is called. If the user isn't able to interact with the activity anymore, onPause is called. The activity is still visible.

As for onDestroy, it is called

  • when the user closes the app
  • when the system terminates the app (to free up memory...)
  • when it's easier to kill and recreate the app
    • πŸ”€ the language changed
    • ⚠️ the rotation changed (don't forget to enable rotation on the phone, especially on emulated devices, as it's disabled by default)

➑️ When needed, you can override these methods.

android_application_lifecycle

Note: onPause must be lightweight, otherwise it will delay the other application from showing up in the front screen (ex: a call).

Note (2): A bundle is a small, in-memory, dictionary. It's passed to onCreate, if the app was recreated. See onRestoreInstanceState and onSaveInstanceState too, if you want to use it to store/load data.


Fragments

Fragments are in many ways similar to activities. They are loaded by an activity, but they have their own lifecycle.

  • πŸ‘‰ Activity#onCreated was split into 3 methods
  • πŸ‘‰ New methods and attributes are present to access some attributes/methods that were available in an activity
    • Use requireActivity() to get the parent Activity
    • Use activity to get the parent Activity (@Nullable)
    • ...

Create a fragment

  • File > New > fragment > [select a template]
BlankFragment template
class BlankFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Load the associated View
        return inflater.inflate(R.layout.fragment_blank, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // See View to configure the view (listeners...)
        // use "view.xxx()" instead of "xxx()"
        // ex: view.findViewById<XXX>(...)
    }
}

Load a fragment in an activity

➑️ See also: Navigation Component.

The Fragment lifecycle is as follows.

Fragment lifecycle


πŸ› Debugging πŸ›

Use the Logger instead of print/println to keep track of what your application is doing (=logs).

You must give a tag to your log. You will be able to filter messages by tag in the Logcat tab.

// ❌ improper, but faster
Log.v("SOME_TAG_NAME", "message")

There are 5 levels of logs. You can increase/decrease the level of logs inside Logcat or by editing the log settings.

  • Log.v: verbose
  • Log.d: debug
  • Log.i: info
  • Log.w: warn
  • Log.e: error

If the level of logging is debug, then all below are included, meaning that only verbose logs won't be shown/logged.

// βœ… proper logging
class XXX : YYY() {
    companion object {
        private const val TAG = "SOME_TAG_NAME"
    }
    
    fun xxx() {
        Log.v(TAG, "verbose message")
    }
}

OR, with the TAG outside, and not inside a companion object

// βœ… proper logging
private const val TAG = "SOME_TAG_NAME"

class XXX : YYY() {    
    ...
}

πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.

// Activity.title: set the title shown in task list
//  and inside the default menubar
title = "Some title"

Topics

  • services (independent processes)
  • content providers (manage app data, enable data sharing),contentResolver, _deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
  • context (app env info, access to resources)
  • broadcast receivers (listen for events)
  • AndroidManifest.xml (metadata, perms, components)
  • src/resources directory

Features

  • NFC
  • Talkback
  • leanback/touchscreen