Android – Understanding Kotlin Style Guide

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Understanding Kotlin Style Guide).

In this article, We will learn about the Kotlin Style Guide. This article serves as the complete definition of Google’s Android coding standards for source code in the Kotlin Programming Language. A Kotlin source file is described as being in Google Android Style if and only if it adheres to the rules herein.

Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this article focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn’t clearly enforceable (whether by human or tool).

A famous quote about learning is :

” We now accept the fact that learning is a lifelong process of keeping abreast of change. And the most pressing task is to teach people how to learn. “

So Let’s begin.


1. Source Files

All source files must be encoded as UTF-8.


1.1. Naming

If a source file contains only a single top-level class, the file name should reflect the case-sensitive name plus the .kt extension. Otherwise, if a source file contains multiple top-level declarations, choose a name that describes the contents of the file, apply PascalCase, and append the .kt extension.

// MyClass.kt
class MyClass { }
// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …


1.2. Special Characters


1.2.1. Whitespace Characters

Aside from the line terminator sequence, the ASCII horizontal space character (0x20) is the only whitespace character that appears anywhere in a source file. This implies that:

  • All other whitespace characters in string and character literals are escaped.
  • Tab characters are not used for indentation.


1.2.2. Special Escape Sequences

For any character that has a special escape sequence (\b\n\r\t\'\"\\, and \$), that sequence is used rather than the corresponding Unicode (e.g., \u000a) escape.


1.2.3. Non-ASCII Characters

For the remaining non-ASCII characters, either the actual Unicode character (e.g., ) or the equivalent Unicode escape (e.g., \u221e) is used. The choice depends only on which makes the code easier to read and understand. Unicode escapes are discouraged for printable characters at any location and are strongly discouraged outside of string literals and comments.

ExampleDiscussion
val unitAbbrev = "μs"Best: perfectly clear even without a comment.
val unitAbbrev = "\u03bcs" // μsPoor: there’s no reason to use an escape with a printable character.
val unitAbbrev = “\u03bcs”`Poor: the reader has no idea what this is.
return "\ufeff" + contentGood: use escapes for non-printable characters, and comment if necessary.


1.3. Structure

.kt file comprises the following, in order:

  • Copyright and/or license header (optional)
  • File-level annotations
  • Package statement
  • Import statements
  • Top-level declarations

Exactly one blank line separates each of these sections.


1.3.1. Copyright / License

If a copyright or license header belongs in the file it should be placed at the immediate top in a multi-line comment.

/*
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
 

Do not use a KDoc-style or single-line-style comment.

/**
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
// Copyright 2017 Google, Inc.
//
// ...


1.3.2. File-level Annotations

Annotations with the “file” use-site target are placed between any header comment and the package declaration.


1.3.3. Package Statement

The package statement is not subject to any column limit and is never line-wrapped.


1.3.4. Import Statements

Import statements for classes, functions, and properties are grouped together in a single list and ASCII sorted.

Wildcard imports (of any type) are not allowed.

Similar to the package statement, import statements are not subject to a column limit and they are never line-wrapped.


1.3.5. Top-level Declarations

.kt file can declare one or more types, functions, properties, or type aliases at the top-level.

The contents of a file should be focused on a single theme. Examples of this would be a single public type or a set of extension functions performing the same operation on multiple receiver types. Unrelated declarations should be separated into their own files and public declarations within a single file should be minimized.

No explicit restriction is placed on the number nor order of the contents of a file.

Source files are usually read from top-to-bottom meaning that the order, in general, should reflect that the declarations higher up will inform understanding of those farther down. Different files may choose to order their contents differently. Similarly, one file may contain 100 properties, another 10 functions, and yet another a single class.

What is important is that each class uses some logical order, which its maintainer could explain if asked. For example, new functions are not just habitually added to the end of the class, as that would yield “chronological by date added” ordering, which is not a logical ordering.


1.3.6. Class Member Ordering

The order of members within a class follow the same rules as the top-level declarations.


2. Formatting


2.1. Braces

Braces are not required for when branches and if statement bodies which have no else if/else branches and which fit on a single line.

if (string.isEmpty()) return

when (value) {
    0 -> return
    // …
}

Braces are otherwise required for any ifforwhen branch, do, and while statements, even when the body is empty or contains only a single statement.

if (string.isEmpty())
    return  // WRONG!

if (string.isEmpty()) {
    return  // Okay
}


2.1.1. Non-empty Blocks

The braces follow the Kernighan and Ritchie style (“Egyptian brackets”) for nonempty blocks and block-like constructs:

  • Firstly, No line break before the opening brace.
  • Secondly, Line break after the opening brace.
  • Thirdly, Line break before the closing brace.
  • And finally, Line break after the closing brace, only if that brace terminates a statement or terminates the body of a function, constructor, or named class. For example, there is no line break after the brace if it is followed by else or a comma.

return Runnable {
    while (condition()) {
        foo()
    }
}

return object : MyClass() {
    override fun foo() {
        if (condition()) {
            try {
                something()
            } catch (e: ProblemException) {
                recover()
            }
        } else if (otherCondition()) {
            somethingElse()
        } else {
            lastThing()
        }
    }
}


2.1.2. Empty Blocks

An empty block or block-like construct must be in K&R style.

try {
    doSomething()
} catch (e: Exception) {} // WRONG!
try {
    doSomething()
} catch (e: Exception) {
} // Okay


2.1.3. Expressions

An if/else conditional that is used as an expression may omit braces only if the entire expression fits on one line.

val value = if (string.isEmpty()) 0 else 1  // Okay
val value = if (string.isEmpty())  // WRONG!
    0
else
    1
val value = if (string.isEmpty()) { // Okay
    0
} else {
    1
}


2.1.4. Indentation

Each time a new block or block-like construct is opened, the indent increases by four spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block.


2.1.5. One Statement Per Line

Each statement is followed by a line break. Semicolons are not used.


2.1.6. Line Wrapping

Code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained below.

Exceptions:

  • Lines where obeying the column limit is not possible (for example, a long URL in KDoc)
  • package and import statements
  • Command lines in a comment that may be cut-and-pasted into a shell


2.1.7. Where to break

The prime directive of line-wrapping is: prefer to break at a higher syntactic level. Also:

  • When a line is broken at an operator or infix function name, the break comes after the operator or infix function name.
  • When a line is broken at the following “operator-like” symbols, the break comes before the symbol:
    • The dot separator (.?.).
    • The two colons of a member reference (::).
  • A method or constructor name stays attached to the open parenthesis (() that follows it.
  • A comma (,) stays attached to the token that precedes it.
  • A lambda arrow (->) stays attached to the argument list that precedes it.


2.1.8. Functions

When a function signature does not fit on a single line, break each parameter declaration onto its own line. Parameters defined in this format should use a single indent (+4). The closing parenthesis ()) and return type are placed on their own line with no additional indent.

fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = ""
): String {
    // …
}


2.1.9. Expression Functions

When a function contains only a single expression it can be represented as an expression function.

override fun toString(): String {
    return "Hey"
}
override fun toString(): String = "Hey"

The only time an expression function should wrap to multiple lines is when it opens a block.

fun main() = runBlocking {
  // …
}

Otherwise, if an expression function grows to require wrapping, use a normal function body, a return declaration, and normal expression wrapping rules instead.


2.1.10. Properties

When a property initializer does not fit on a single line, break after the equals sign (=) and use an indent.

private val defaultCharset: Charset? =
        EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)

Properties declaring a get and/or set function should place each on their own line with a normal indent (+4). Format them using the same rules as functions.

var directory: File? = null
    set(value) {
        // …
    }

Read-only properties can use a shorter syntax which fits on a single line.

val defaultExtension: String get() = "kt"


2.2. Whitespace


2.2.1. Vertical

A single blank line appears:

  • Between consecutive members of a class: properties, constructors, functions, nested classes, etc.
    • Exception: A blank line between two consecutive properties (having no other code between them) is optional. Such blank lines are used as needed to create logical groupings of properties and associate properties with their backing property, if present.
    • Exception: Blank lines between enum constants are covered below.
  • Between statements, as needed to organize the code into logical subsections.
  • Optionally before the first statement in a function, before the first member of a class, or after the last member of a class (neither encouraged nor discouraged).

Multiple consecutive blank lines are permitted, but not encouraged or ever required.


2.2.2. Horizontal

Beyond where required by the language or other style rules, and apart from literals, comments, and KDoc, a single ASCII space also appears in the following places only:

  • Separating any reserved word, such as iffor, or catch from an open parenthesis (() that follows it on that line.
// WRONG!
for(i in 0..1) {
}
// Okay
for (i in 0..1) {
}
  • Separating any reserved word, such as else or catch, from a closing curly brace (}) that precedes it on that line.
// WRONG!
}else {
}
// Okay
} else {
}
  • Before any open curly brace ({).
// WRONG!
if (list.isEmpty()){
}
// Okay
if (list.isEmpty()) {
}
  • Before a colon (:) only if used in a class declaration for specifying a base class or interfaces, or when used in a where clause for generic constraints.
// WRONG!
class Foo: Runnable
// Okay
class Foo : Runnable
// WRONG
fun <T: Comparable> max(a: T, b: T)
// Okay
fun <T : Comparable> max(a: T, b: T)
// WRONG
fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay
fun <T> max(a: T, b: T) where T : Comparable<T>
  • After a comma (,) or colon (:).
// WRONG!
val oneAndTwo = listOf(1,2)
// Okay
val oneAndTwo = listOf(1, 2)
// WRONG!
class Foo :Runnable
// Okay
class Foo : Runnable
  • On both sides of the double slash (//) that begins an end-of-line comment. Here, multiple spaces are allowed, but not required.
// WRONG!
var debugging = false//disabled by default
// Okay
var debugging = false // disabled by default
  • On both sides of any binary operator.
// WRONG!
val two = 1+1
// Okay
val two = 1 + 1

This also applies to the following “operator-like” symbols:

  • the arrow in a lambda expression (->).
// WRONG!
ints.map { value->value.toString() }
// Okay
ints.map { value -> value.toString() }

But not:

  • the two colons (::) of a member reference.
// WRONG!
val toString = Any :: toString
// Okay
val toString = Any::toString
  • the dot separator (.).
// WRONG
it . toString()
// Okay
it.toString()
  • the range operator (..).
// WRONG
 for (i in 1 .. 4) print(i)
 
 // Okay
 for (i in 1..4) print(i)

This rule is never interpreted as requiring or forbidding additional space at the start or end of a line; it addresses only interior space.


2.3. Specific Constructs


2.3.1. Enum Classes

An enum with no functions and no documentation on its constants may optionally be formatted as a single line.

enum class Answer { YES, NO, MAYBE }

When the constants in an enum are placed on separate lines, a blank line is not required between them except in the case where they define a body.

enum class Answer {
    YES,
    NO,

    MAYBE {
        override fun toString() = """¯\_(ツ)_/¯"""
    }
}

Since enum classes are classes, all other rules for formatting classes apply.


2.3.2. Annotations

Member or type annotations are placed on separate lines immediately prior to the annotated construct.

@Retention(SOURCE)
@Target(FUNCTION, PROPERTY_SETTER, FIELD)
annotation class Global

Annotations without arguments can be placed on a single line.

@JvmField @Volatile
var disposable: Disposable? = null

When only a single annotation without arguments is present, it may be placed on the same line as the declaration.

@Volatile var disposable: Disposable? = null

@Test fun selectAll() {
    // …
}

@[...] syntax may only be used with an explicit use-site target, and only for combining 2 or more annotations without arguments on a single line.

@field:[JvmStatic Volatile]
var disposable: Disposable? = null


2.3.3. Implicit Return/Property Types

If an expression function body or a property initializer is a scalar value or the return type can be clearly inferred from the body then it can be omitted.

override fun toString(): String = "Hey"
// becomes
override fun toString() = "Hey"
private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png")
// becomes
private val ICON = IconLoader.getIcon("/icons/kotlin.png")

When writing a library, retain the explicit type declaration when it is part of the public API.


2.4. Naming

Identifiers use only ASCII letters and digits, and, in a small number of cases noted below, underscores. Thus each valid identifier name is matched by the regular expression \w+.

Special prefixes or suffixes, like those seen in the examples name_mNames_name, and kName, are not used except in the case of backing properties.


2.4.1. Package Names

Package names are all lowercase, with consecutive words simply concatenated together (no underscores).

// Okay
package com.example.deepspace
// WRONG!
package com.example.deepSpace
// WRONG!
package com.example.deep_space


2.4.2. Type Names

Class names are written in PascalCase and are typically nouns or noun phrases. For example, Character or ImmutableList. Interface names may also be nouns or noun phrases (for example, List), but may sometimes be adjectives or adjective phrases instead (for example Readable).

Test classes are named starting with the name of the class they are testing, and ending with Test. For example, HashTest or HashIntegrationTest.


2.4.3. Function Names

Function names are written in camelCase and are typically verbs or verb phrases. For example, sendMessage or stop.

Underscores are permitted to appear in test function names to separate logical components of the name.

@Test fun pop_emptyStack() {
    // …
}

Functions annotated with @Composable that return Unit are PascalCased and named as nouns, as if they were types.

@Composable
fun NameTag(name: String) {
    // …
}


2.4.4. Constant Names

Constant names use UPPER_SNAKE_CASE: all uppercase letters, with words separated by underscores. But what is a constant, exactly?

Constants are val properties with no custom get function, whose contents are deeply immutable, and whose functions have no detectable side-effects. This includes immutable types and immutable collections of immutable types as well as scalars and string if marked as const. If any of an instance’s observable state can change, it is not a constant. Merely intending to never mutate the object is not enough.

const val NUMBER = 5
val NAMES = listOf("Alice", "Bob")
val AGES = mapOf("Alice" to 35, "Bob" to 32)
val COMMA_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf()

These names are typically nouns or noun phrases.

Constant values can only be defined inside of an object or as a top-level declaration. Values otherwise meeting the requirement of a constant but defined inside of a class must use a non-constant name.

Constants which are scalar values must use the const modifier.


2.4.5. Non-constant Names

Non-constant names are written in camelCase. These apply to instance properties, local properties, and parameter names.

val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet = HashSet()
val mutableElements = listOf(mutableInstance)
val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2)
val logger = Logger.getLogger(MyClass::class.java.name)
val nonEmptyArray = arrayOf("these", "can", "change")

These names are typically nouns or noun phrases.


2.4.6. Backing Properties

When a backing property is needed, its name should exactly match that of the real property except prefixed with an underscore.

private var _table: Map? = null

val table: Map
    get() {
        if (_table == null) {
            _table = HashMap()
        }
        return _table ?: throw AssertionError()
    }


2.4.7. Type Variable Names

Each type variable is named in one of two styles:

  • A single capital letter, optionally followed by a single numeral (such as ETXT2).
  • A name in the form used for classes, followed by the capital letter T (such as RequestTFooBarT).


2.4.8. Camel Case

Sometimes there is more than one reasonable way to convert an English phrase into camel case, such as when acronyms or unusual constructs like “IPv6” or “iOS” are present. To improve predictability, use the following scheme.

Beginning with the prose form of the name:

  1. Convert the phrase to plain ASCII and remove any apostrophes. For example, “Müller’s algorithm” might become “Muellers algorithm”.
  2. Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens). Recommended: if any word already has a conventional camel-case appearance in common usage, split this into its constituent parts (e.g., “AdWords” becomes “ad words”). Note that a word such as “iOS” is not really in camel case per se; it defies any convention, so this recommendation does not apply.
  3. Now lowercase everything (including acronyms), then do one of the following:
    • Uppercase the first character of each word to yield pascal case.
    • Uppercase the first character of each word except the first to yield camel case.
  4. Finally, join all the words into a single identifier.

We note that the casing of the original words is almost entirely disregarded.

Prose formCorrectIncorrect
“XML Http Request”XmlHttpRequestXMLHTTPRequest
“new customer ID”newCustomerIdnewCustomerID
“inner stopwatch”innerStopwatchinnerStopWatch
“supports IPv6 on iOS”supportsIpv6OnIossupportsIPv6OnIOS
“YouTube importer”YouTubeImporterYoutubeImporter*

(* Acceptable, but not recommended.)


2.5. Documentation


2.5.1. Formatting

The basic formatting of KDoc blocks is seen in this example:

/**
 * Multiple lines of KDoc text are written here,
 * wrapped normally…
 */
fun method(arg: String) {
    // …
}

…or in this single-line example:

/** An especially short bit of KDoc. */

The basic form is always acceptable. The single-line form may be substituted when the entirety of the KDoc block (including comment markers) can fit on a single line. Note that this only applies when there are no block tags such as @return.


2.5.2. Paragraphs

One blank line—that is, a line containing only the aligned leading asterisk (*)—appears between paragraphs, and before the group of block tags if present.


2.5.3. Block Tags

Any of the standard “block tags” that are used appear in the order @constructor@receiver@param@property@return@throws@see, and these never appear with an empty description. When a block tag doesn’t fit on a single line, continuation lines are indented 4 spaces from the position of the @.


2.5.4. Summary Fragment

Each KDoc block begins with a brief summary fragment. This fragment is very important: it is the only part of the text that appears in certain contexts such as class and method indexes.

This is a fragment–a noun phrase or verb phrase, not a complete sentence. It does not begin with “A `Foo` is a...“, or “This method returns...“, nor does it have to form a complete imperative sentence like “Save the record.“. However, the fragment is capitalized and punctuated as if it were a complete sentence.


2.5.5. Usage

At the minimum, KDoc is present for every public type, and every public or protected member of such a type, with a few exceptions noted below.


2.5.5.1. Exception: Self-explanatory Functions

KDoc is optional for “simple, obvious” functions like getFoo and properties like foo, in cases where there really and truly is nothing else worthwhile to say but “Returns the foo”.

It is not appropriate to cite this exception to justify omitting relevant information that a typical reader might need to know. For example, for a function named getCanonicalName or property named canonicalName, don’t omit its documentation (with the rationale that it would say only /** Returns the canonical name. */) if a typical reader may have no idea what the term “canonical name” means!

2.5.5.2. Exception: Overrides

KDoc is not always present on a method that overrides a supertype method.

That’s all about in this article.


Conclusion

In this article, We understood about Kotlin Style Guide for Android application development. This article served as the complete definition of Google’s Android coding standards for source code in the Kotlin Programming Language. We discussed about Source code and formatting style guideline standard for Kotlin which is used in android application development.

Thanks for reading ! I hope you enjoyed and learned about Kotlin Style Guide concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – How To Apply Common Kotlin Patterns In Android Application ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Apply Common Kotlin Patterns In Android Application ?)

In this article, We will learn how to apply common Kotlin patterns in Android apps. This article will focus on some of the most useful aspects of the Kotlin language when developing for Android.

A famous quote about learning is :

” Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young. “

So Let’s begin.


Work with fragments

In this sections, we use Fragment examples to highlight some of Kotlin’s best features as below:


Inheritance

We can declare a class in Kotlin with the class keyword. In the following example, LoginFragment is a subclass of Fragment. We can indicate inheritance by using the : operator between the subclass and its parent:

class LoginFragment : Fragment()

In this class declaration, LoginFragment is responsible for calling the constructor of its superclass, Fragment.

Within LoginFragment, we can override a number of lifecycle callbacks to respond to state changes in our Fragment. To override a function, use the override keyword, as shown in the following example:

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.login_fragment, container, false)
}

To reference a function in the parent class, use the super keyword, as shown in the following example:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
}


Nullability and Initialization

In the previous examples, some of the parameters in the overridden methods have types suffixed with a question mark ?. This indicates that the arguments passed for these parameters can be null. Be sure to handle their nullability safely.

In Kotlin, we must initialize an object’s properties when declaring the object. This implies that when we obtain an instance of a class, we can immediately reference any of its accessible properties. The View objects in a Fragment, however, aren’t ready to be inflated until calling Fragment#onCreateView, so we need a way to defer property initialization for a View.

The lateinit lets us defer property initialization. When using lateinit, we should initialize our property as soon as possible.

The following example demonstrates using lateinit to assign View objects in onViewCreated:

class LoginFragment : Fragment() {

    private lateinit var usernameEditText: EditText
    private lateinit var passwordEditText: EditText
    private lateinit var loginButton: Button
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        usernameEditText = view.findViewById(R.id.username_edit_text)
        passwordEditText = view.findViewById(R.id.password_edit_text)
        loginButton = view.findViewById(R.id.login_button)
        statusTextView = view.findViewById(R.id.status_text_view)
    }

    ...
}

We aware that if we access a property before it is initialized, Kotlin throws an UninitializedPropertyAccessException.


SAM Conversion

We can listen for click events in Android by implementing the OnClickListener interface. Button objects contain a setOnClickListener() function that takes in an implementation of OnClickListener.

OnClickListener has a single abstract method, onClick(), that we must implement. Because setOnClickListener() always takes an OnClickListener as an argument, and because OnClickListener always has the same single abstract method, this implementation can be represented using an anonymous function in Kotlin. This process is known as Single Abstract Method conversion, or SAM conversion.

SAM conversion can make our code considerably cleaner. The following example shows how to use SAM conversion to implement an OnClickListener for a Button:

loginButton.setOnClickListener {
    val authSuccessful: Boolean = viewModel.authenticate(
            usernameEditText.text.toString(),
            passwordEditText.text.toString()
    )
    if (authSuccessful) {
        // Navigate to next screen
    } else {
        statusTextView.text = requireContext().getString(R.string.auth_failed)
    }
}

The code within the anonymous function passed to setOnClickListener() executes when a user clicks loginButton.


Companion Objects

The Companion objects provide a mechanism for defining variables or functions that linked conceptually to a type but do not tie to a particular object. Companion objects are similar to using Java’s static keyword for variables and methods.

In the following example, TAG is a String constant. We don’t need a unique instance of the String for each instance of LoginFragment, so we should define it in a companion object:

class LoginFragment : Fragment() {

    ...

    companion object {
        private const val TAG = "LoginFragment"
    }
}

We could define TAG at the top level of the file, but the file might also have a large number of variables, functions, and classes that are also defined at the top level. Companion objects help to connect variables, functions, and the class definition without referring to any particular instance of that class.


Property Delegation

When initializing properties, we might repeat some of Android’s more common patterns, such as accessing a ViewModel within a Fragment. To avoid excess duplicate code, we can use Kotlin’s property delegation syntax.

private val viewModel: LoginViewModel by viewModels()

Property delegation provides a common implementation that we can reuse throughout our app. Android KTX provides some property delegates for us. viewModels, for example, retrieves a ViewModel that is scoped to the current Fragment.

Property delegation uses reflection, which adds some performance overhead. The tradeoff is a concise syntax that saves development time.


Nullability

Kotlin provides strict nullability rules that maintain type-safety throughout our app. In Kotlin, references to objects cannot contain null values by default. To assign a null value to a variable, we must declare a nullable variable type by adding ? to the end of the base type.

As an example, the following expression is illegal in Kotlin. name is of type String and isn’t nullable:

val name: String = null

To allow a null value, we must use a nullable String type, String?, as shown in the following example:

val name: String? = null


Interoperability

Kotlin’s strict rules make our code safer and more concise. These rules lower the chances of having a NullPointerException that would cause our app to crash. Moreover, they reduce the number of null checks, we need to make in our code.

Often, we must also call into non-Kotlin code when writing an Android app, as most Android APIs are written in the Java programming language.

Nullability is a key area where Java and Kotlin differ in behavior. Java is less strict with nullability syntax.

As an example, the Account class has a few properties, including a String property called name. Java does not have Kotlin’s rules around nullability, instead relying on optional nullability annotations to explicitly declare whether we can assign a null value.

Because the Android framework is written primarily in Java, we might run into this scenario when calling into APIs without nullability annotations.


Platform Types

If we use Kotlin to reference a unannotated name member that is defined in a Java Account class, the compiler doesn’t know whether the String maps to a String or a String? in Kotlin. This ambiguity is represented via a platform typeString!.

String! has no special meaning to the Kotlin compiler. String! can represent either a String or a String?, and the compiler lets us assign a value of either type. Note that we risk throwing a NullPointerException if we represent the type as a String and assign a null value.

To address this issue, we should use nullability annotations whenever we write code in Java. These annotations help both Java and Kotlin developers.

For example, here’s the Account class as it’s defined in Java:

public class Account implements Parcelable {
    public final String name;
    public final String type;
    private final @Nullable String accessId;

    ...
}

One of the member variables, accessId, is annotated with @Nullable, indicating that it can hold a null value. Kotlin would then treat accessId as a String?.

To indicate that a variable can never be null, use the @NonNull annotation:

public class Account implements Parcelable {
    public final @NonNull String name;
    ...
}

In this scenario, name is considered a non-nullable String in Kotlin.

Nullability annotations are included in all new Android APIs and many existing Android APIs. Many Java libraries have added nullability annotations to better support both Kotlin and Java developers.


Handling nullability

If we are unsure about a Java type, we should consider it to be nullable. As an example, the name member of the Account class is not annotated, so we should assume it to be a nullable String.

If we want to trim name so that its value does not include leading or trailing whitespace, we can use Kotlin’s trim function. We can safely trim a String? in a few different ways. One of these ways is to use the not-null assertion operator!!, as shown in the following example:

val account = Account("name", "type")
val accountName = account.name!!.trim()

The !! operator treats everything on its left-hand side as non-null, so in this case, we are treating name as a non-null String. If the result of the expression to its left is null, then our app throws a NullPointerException. This operator is quick and easy, but it should be used sparingly, as it can reintroduce instances of NullPointerException into our code.

A safer choice is to use the safe-call operator?., as shown in the following example:

val account = Account("name", "type")
val accountName = account.name?.trim()

Using the safe-call operator, if name is non-null, then the result of name?.trim() is a name value without leading or trailing whitespace. If name is null, then the result of name?.trim() is null. This means that our app can never throw a NullPointerException when executing this statement.

While the safe-call operator saves us from a potential NullPointerException, it does pass a null value to the next statement. We can instead handle null cases immediately by using an Elvis operator (?:), as shown in the following example:

val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"

If the result of the expression on the left-hand side of the Elvis operator is null, then the value on the right-hand side is assigned to accountName. This technique is useful for providing a default value that would otherwise be null.

We can also use the Elvis operator to return from a function early, as shown in the following example:

fun validateAccount(account: Account?) {
    val accountName = account?.name?.trim() ?: "Default name"

    // account cannot be null beyond this point
    account ?: return

    ...
}


Android API changes

Android APIs are becoming increasingly Kotlin-friendly. Many of Android’s most-common APIs, including AppCompatActivity and Fragment, contain nullability annotations, and certain calls like Fragment#getContext have more Kotlin-friendly alternatives.

For example, accessing the Context of a Fragment is almost always non-null, since most of the calls that we make in a Fragment occur while the Fragment is attached to an Activity (a subclass of Context). That said, Fragment#getContext does not always return a non-null value, as there are scenarios where a Fragment is not attached to an Activity. Thus, the return type of Fragment#getContext is nullable.

Since the Context returned from Fragment#getContext is nullable (and is annotated as @Nullable), we must treat it as a Context? in our Kotlin code. This means applying one of the previously-mentioned operators to address nullability before accessing its properties and functions. For some of these scenarios, Android contains alternative APIs that provide this convenience. Fragment#requireContext, for example, returns a non-null Context and throws an IllegalStateException if called when a Context would be null. This way, we can treat the resulting Context as non-null without the need for safe-call operators or workarounds.


Property Initialization

Properties in Kotlin are not initialized by default. They must be initialized when their enclosing class is initialized.

We can initialize properties in a few different ways. The following example shows how to initialize an index variable by assigning a value to it in the class declaration:

class LoginFragment : Fragment() {
    val index: Int = 12
}

This initialization can also be defined in an initializer block:

class LoginFragment : Fragment() {
    val index: Int

    init {
        index = 12
    }
}

In the examples above, index is initialized when a LoginFragment is constructed.

However, we might have some properties that can’t be initialized during object construction. For example, we might want to reference a View from within a Fragment, which means that the layout must be inflated first. Inflation does not occur when a Fragment is constructed. Instead, it’s inflated when calling Fragment#onCreateView.

One way to address this scenario is to declare the view as nullable and initialize it as soon as possible, as shown in the following example:

class LoginFragment : Fragment() {
    private var statusTextView: TextView? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView?.setText(R.string.auth_failed)
    }
}

While this works as expected, we must now manage the nullability of the View whenever we reference it. A better solution is to use lateinit for View initialization, as shown in the following example:

class LoginFragment : Fragment() {
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView.setText(R.string.auth_failed)
    }
}

The lateinit keyword allows us to avoid initializing a property when an object is constructed. If our property is referenced before being initialized, Kotlin throws an UninitializedPropertyAccessException, so be sure to initialize our property as soon as possible.

That’s all about in this article.

Related Other Articles / Posts


Conclusion

In this article, We understood about how to apply common Kotlin patterns in Android apps. This article demonstrated the most useful aspects of the Kotlin language like Working with Fragments and Nullability when developing for Android.

Thanks for reading ! I hope you enjoyed and learned about common Kotlin patterns concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – How To Use Sensors In Android ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Use Sensors In Android ?).

In this article, We will learn how to use Android Sensors. We all must have played some Android games that includes the supports of sensors i.e. by tilting the phone some actions might happen in the game. For example, in the Temple Run game, by tilting the phone to left or right, the position of the runner changes. So, all these games are using the sensors present in your Android device. Other examples can be shaking our phone to lock the screen, finding the direction with the help of a compass, etc. All these are examples of Android sensors.

Use sensors on the device to add rich location and motion capabilities to our app, from GPS or network location to accelerometer, gyroscope, temperature, barometer, and more.

To understand the Android Sensors, we will discuss the below topics :

  • Overview
  • Sensor Coordinate System
  • Categories of Sensors
  • Android Sensor Framework
  • Perform Tasks To Use Sensor-Related APIs
  • Handling Different Sensor Configurations
  • Best Practices for Accessing and Using Sensors

A famous quote about learning is :

“The more that you read, the more things you will know. The more that you learn, the more places you’ll go.”

So Let’s begin.


Overview

In Android devices, there are various built-in sensors that can be used to measure the orientation, motions, and various other kinds of environmental conditions. In general, there are two types of sensors in Android devices:

  1. Hardware Sensors: Hardware sensors are physical components that are present in Android devices. They can directly measure various properties like field strength, acceleration, etc according to the types of the sensors and after measuring the environment properties they can send the data to Software Sensors.
  2. Software Sensors: Software sensors also know as virtual sensors are those sensors that take the help of one or more Hardware sensors and based on the data collected by various Hardware sensors, they can derive some result.

It is not necessary that all Android devices must have all the sensors. Some devices may have all sensors and some may lack one or two of them. At the same time, a particular device may have more than one sensors of the same type but with different configurations and capabilities.


Sensor Coordinate System

To express data values or to collect data, the sensors in Android devices uses a 3-axis coordinate system i.e. we will be having X, Y, and Z-axis. The following figure depicts the position of various axis used in sensors.

In default orientation, the horizontal axis is represented by X-axis, the vertical axis is represented by Y-axis and the Z-axis points towards the outside of the screen face i.e towards the user. This coordinate system is used by the following sensors:

  • Acceleration sensor
  • Gravity sensor
  • Gyroscope
  • Linear acceleration sensor
  • Geomagnetic field sensor

The most important point to understand about this coordinate system is that the axes are not swapped when the device’s screen orientation changes—that is, the sensor’s coordinate system never changes as the device moves. This behavior is the same as the behavior of the OpenGL coordinate system.

Another point to understand is that our application must not assume that a device’s natural (default) orientation is portrait. The natural orientation for many tablet devices is landscape. And the sensor coordinate system is always based on the natural orientation of a device.

Finally, if our application matches sensor data to the on-screen display, we need to use the getRotation() method to determine screen rotation, and then use the remapCoordinateSystem() method to map sensor coordinates to screen coordinates. We need to do this even if our manifest specifies portrait-only display.


Categories of Sensors

Following are the three broad categories of sensors in Android:

  1. Motion Sensors: The sensors that are responsible for measuring or identifying the shakes and tilts of your Android devices are called Motion sensors. These sensors measure the rotational forces along the three-axis. Gravity sensors, accelerometers, etc are some of the examples of Motion sensors.
  2. Position Sensors: As the name suggests, the Position sensors are used to determine the position of an Android device. Magnetometers, Proximity sensors are some of the examples of Position sensors.
  3. Environmental Sensors: Environmental properties like temperature, pressure, humidity, etc are identified with the help of Environmental sensors. Some of the examples of Environmental sensors are thermometer, photometer, barometer, etc.


Android Sensor Framework

Everything related to sensors in Android device is managed or controlled by Android Sensor Framework. By using Android Sensor Framework we can collect raw sensor data. It is a part of android.hardware package and includes various classes and interface:

  1. SensorManager: This is used to get access to various sensors present in the device to use it according to need.
  2. Sensor: This class is used to create an instance of a specific sensor.
  3. SensorEvent: This class is used to find the details of the sensor events.
  4. SensorEventListener: This interface can be used to trigger or perform some action when there is a change in the sensor values.

Following are the usages of the Android Sensor Framework:

  1. You can register or unregister sensor events.
  2. You can collect data from various sensors.
  3. You can find the sensors that are active on a device and determine its capabilities.


Perform Tasks To Use Sensor-Related APIs

In this section, we will see how we can identify various sensors present in a device and how to determine its capabilities. In a typical application we use these sensor-related APIs to perform two basic tasks:

  • Identifying sensors and sensor capabilities
  • Monitoring Sensor Events


Identifying sensors and sensor capabilities

Identifying sensors and sensor capabilities at runtime is useful if our application has features that rely on specific sensor types or capabilities. For example, we may want to identify all of the sensors that are present on a device and disable any application features that rely on sensors that are not present. Likewise, we may want to identify all of the sensors of a given type so we can choose the sensor implementation that has the optimum performance for our application.

It is not necessary that two Android devices must have the same number of sensors or the same type of sensors. The availability of sensors varies from device to device and from one Android version to other. So, we can not guarantee that two Android versions or two Android devices must have the same sensors. It becomes a necessary task to identify which sensors are present in a particular Android device.

As seen earlier, we can take the help of the Android Sensor Framework to find the sensors that are present in a particular Android device. Not only that, with the help of various methods of the sensor framework, we can determine the capabilities of a sensor like its resolution, its maximum range, and its power requirements.

Following are the steps that need to be followed to get the list of available sensors in a device:

  1. Create an instance of the SensorManager.
  2. Call the getSystemService() method and pass SENSOR_SERVICE as an argument. This SENSOR_SERVICE is used to retrieve a SensorManager to access sensors.
  3. Call the getSensorList() method to get the names of all the sensors present in the device. The parameter of this method is sensor type. Either we can use TYPE_ALL to get all the sensors available in the device or you can use a particular sensor, for example, TYPE_GRAVITY or TYPE_GYROSCOPE to get the list of sensors of that type only(we can have more than one sensors of the same type).
  4. If we are not using TYPE_ALL i.e. we want to get all the types of sensors of a particular type then we can do so by using the getDefaultSensor() method. This method returns null if there is no sensor of that type in the Android device.
//Step 1
private lateinit var sensorManager: SensorManager

//Step 2
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

//Step 3
//To get a list of all sensors, use TYPE_ALL
val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
//Or you can use TYPE_GRAVITY, TYPE_GYROSCOPE or some other sensor
//val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_GRAVITY)

//Step 4
if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null) {
    //There's a gravity sensor.
} else {
    //No gravity sensor.
}

Apart from finding the list of available sensors, we can also check the capability of a particular sensor i.e. we can check the resolution, power, range, etc of a particular sensor.

Sensor.getResolution() //returns a float value which is the resolution of the sensor

Sensor.getMaximumRange() //returns a float value which is the maximum range of the sensor

Sensor.getPower() //returns a float value which is the power in mA used by sensor


Monitoring Sensor Events

Monitoring sensor events is how we acquire raw sensor data. A sensor event occurs every time a sensor detects a change in the parameters it is measuring. A sensor event provides us with four pieces of information: the name of the sensor that triggered the event, the timestamp for the event, the accuracy of the event, and the raw sensor data that triggered the event.

To monitor raw sensor data we need to implement two callback methods that are exposed through the SensorEventListener interface: onAccuracyChanged() and onSensorChanged(). The Android system calls these methods whenever the following occurs:

  1. onAccuracyChanged(): This is called when there is a change in the accuracy of measurement of the sensor. This method will provide the Sensor object that has changed and the new accuracy. There are four statuses of accuracy i.e. SENSOR_STATUS_ACCURACY_LOW, SENSOR_STATUS_ACCURACY_MEDIUM, SENSOR_STATUS_ACCURACY_HIGH, SENSOR_STATUS_UNRELIABLE.
  2. onSensorChanged(): This is called when there is an availability of new sensor data. This method will provide us with a SensorEvent object that contains new sensor data.
class SensorActivity : Activity(), SensorEventListener {
    private lateinit var sensorManager: SensorManager
    private lateinit var mGravity: Sensor

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

        //gravity sensor
        mGravity = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        //If sensor accuracy changes.
    }

    override fun onSensorChanged(event: SensorEvent) {
        //If there is a new sensor data
    }

    //register
    override fun onResume() {
        super.onResume()
        mGravity?.also { gravity ->
            sensorManager.registerListener(this, gravity, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    //unregister
    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }
}

In this example, the default data delay (SENSOR_DELAY_NORMAL) is specified when the registerListener() method is invoked. The data delay (or sampling rate) controls the interval at which sensor events are sent to our application via the onSensorChanged() callback method. The default data delay is suitable for monitoring typical screen orientation changes and uses a delay of 200,000 microseconds. We can specify other data delays, such as SENSOR_DELAY_GAME (20,000 microsecond delay), SENSOR_DELAY_UI (60,000 microsecond delay), or SENSOR_DELAY_FASTEST (0 microsecond delay). As of Android 3.0 (API Level 11) we can also specify the delay as an absolute value (in microseconds).

The delay that we specify is only a suggested delay. The Android system and other applications can alter this delay. As a best practice, we should specify the largest delay that we can because the system typically uses a smaller delay than the one we specify (that is, we should choose the slowest sampling rate that still meets the needs of our application). Using a larger delay imposes a lower load on the processor and therefore uses less power.

There is no public method for determining the rate at which the sensor framework is sending sensor events to our application; however, we can use the timestamps that are associated with each sensor event to calculate the sampling rate over several events. We should not have to change the sampling rate (delay) once we set it. If for some reason we do need to change the delay, we will have to unregister and reregister the sensor listener.

It’s also important to note that this example uses the onResume() and onPause() callback methods to register and unregister the sensor event listener. As a best practice we should always disable sensors we don’t need, especially when our activity is paused. Failing to do so can drain the battery in just a few hours because some sensors have substantial power requirements and can use up battery power quickly. The system will not disable sensors automatically when the screen turns off.


Handling Different Sensor Configurations

Android does not specify a standard sensor configuration for devices, which means device manufacturers can incorporate any sensor configuration that they want into their Android-powered devices. As a result, devices can include a variety of sensors in a wide range of configurations. If our application relies on a specific type of sensor, we have to ensure that the sensor is present on a device so our app can run successfully.

We have two options for ensuring that a given sensor is present on a device:

  • Detect sensors at runtime and enable or disable application features as appropriate.
  • Use Google Play filters to target devices with specific sensor configurations.


Detecting sensors at runtime

If our application uses a specific type of sensor, but doesn’t rely on it, we can use the sensor framework to detect the sensor at runtime and then disable or enable application features as appropriate. For example, a navigation application might use the temperature sensor, pressure sensor, GPS sensor, and geomagnetic field sensor to display the temperature, barometric pressure, location, and compass bearing. If a device doesn’t have a pressure sensor, we can use the sensor framework to detect the absence of the pressure sensor at runtime and then disable the portion of our application’s UI that displays pressure. For example, the following code checks whether there’s a pressure sensor on a device:

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null) {
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}


Using Google Play filters to target specific sensor configurations

If we are publishing our application on Google Play we can use the <uses-feature> element in our manifest file to filter our application from devices that do not have the appropriate sensor configuration for our application. The <uses-feature> element has several hardware descriptors that let we filter applications based on the presence of specific sensors. The sensors we can list include: accelerometer, barometer, compass (geomagnetic field), gyroscope, light, and proximity. The following is an example manifest entry that filters apps that do not have an accelerometer:

<uses-feature android:name="android.hardware.sensor.accelerometer"
              android:required="true" />

If we add this element and descriptor to our application’s manifest, users will see our application on Google Play only if their device has an accelerometer.

We should set the descriptor to android:required="true" only if our application relies entirely on a specific sensor. If our application uses a sensor for some functionality, but still runs without the sensor, we should list the sensor in the <uses-feature> element, but set the descriptor to android:required="false". This helps ensure that devices can install our app even if they do not have that particular sensor. This is also a project management best practice that helps us keep track of the features our application uses. Keep in mind, if our application uses a particular sensor, but still runs without the sensor, then we should detect the sensor at runtime and disable or enable application features as appropriate.


Best Practices for Accessing and Using Sensors

As we design our sensor implementation, be sure to follow the guidelines that are discussed in this section. These guidelines are recommended best practices for anyone who is using the sensor framework to access sensors and acquire sensor data.


1. Only gather sensor data in the foreground

On devices running Android 9 (API level 28) or higher, apps running in the background have the following restrictions:

  • Sensors that use the continuous reporting mode, such as accelerometers and gyroscopes, don’t receive events.
  • Sensors that use the on-change or one-shot reporting modes don’t receive events.

Given these restrictions, it’s best to detect sensor events either when your app is in the foreground or as part of a foreground service.


2. Unregister sensor listeners

Be sure to unregister a sensor’s listener when we are done using the sensor or when the sensor activity pauses. If a sensor listener is registered and its activity is paused, the sensor will continue to acquire data and use battery resources unless we unregister the sensor. The following code shows how to use the onPause() method to unregister a listener:

private lateinit var sensorManager: SensorManager
...
override fun onPause() {
    super.onPause()
    sensorManager.unregisterListener(this)
}


3. Test with the Android Emulator

The Android Emulator includes a set of virtual sensor controls that allow you to test sensors such as accelerometer, ambient temperature, magnetometer, proximity, light, and more.

The emulator uses a connection with an Android device that is running the SdkControllerSensor app. Note that this app is available only on devices running Android 4.0 (API level 14) or higher. (If the device is running Android 4.0, it must have Revision 2 installed.) The SdkControllerSensor app monitors changes in the sensors on the device and transmits them to the emulator. The emulator is then transformed based on the new values that it receives from the sensors on our device.


4. Don’t block the onSensorChanged() method

Sensor data can change at a high rate, which means the system may call the onSensorChanged(SensorEvent) method quite often. As a best practice, we should do as little as possible within the onSensorChanged(SensorEvent) method so we don’t block it. If our application requires us to do any data filtering or reduction of sensor data, we should perform that work outside of the onSensorChanged(SensorEvent) method.


5. Avoid using deprecated methods or sensor types

Several methods and constants have been deprecated. In particular, the TYPE_ORIENTATION sensor type has been deprecated. To get orientation data we should use the getOrientation() method instead. Likewise, the TYPE_TEMPERATURE sensor type has been deprecated. We should use the TYPE_AMBIENT_TEMPERATURE sensor type instead on devices that are running Android 4.0.


6. Verify sensors before we use them

Always verify that a sensor exists on a device before we attempt to acquire data from it. Don’t assume that a sensor exists simply because it’s a frequently-used sensor. Device manufacturers are not required to provide any particular sensors in their devices.


7. Choose sensor delays carefully

When we register a sensor with the registerListener() method, be sure we choose a delivery rate that is suitable for our application or use-case. Sensors can provide data at very high rates. Allowing the system to send extra data that we don’t need wastes system resources and uses battery power.

That’s all about in this article.


Conclusion

In this article, we learned about how to use Android Sensors. We learned about the Hardware and the Software sensors. We saw how the Android Sensor Framework can be used to determine the sensors present in the Android device. At last, we saw how to use Sensor Event Listener.

Thanks for reading ! I hope you enjoyed and learned about Sensors Concept in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find other articles of CoolMonkTechie as below link :

You can also follow the official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – How To Work Flow APIs In Kotlin ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn how Flow APIs work in Kotlin and how can we start using it in our android projects.

If we are working as an Android developer and looking to build an app asynchronously we might be using RxJava as it has an operator for almost everything. RxJava has become one of the most important things to know in Android.

But with Kotlin a lot of people tend to use Co-routines. With Kotlin Coroutine 1.2.0 alpha release Jetbrains came up with Flow API as part of it. With Flow in Kotlin now you can handle a stream of data that emits values sequentially.

In Kotlin, Coroutine is just the scheduler part of RxJava but now with Flow APIs coming along side it, it can be alternative to RxJava in Android.”

We will cover the following topics to understanding the Flow API :

  • What is Flow APIs in Kotlin Coroutines?
  • Start Integrating Flow APIs in your project
  • Builders in Flows
  • Few examples using Flow Operators.

A famous quote about learning is :

“Learn as though you would never be able to master it; hold it as though you would be in fear of losing it.”

So Let’s begin.

What is Flow APIs in Kotlin Coroutines?

Flow API in Kotlin is a better way to handle the stream of data asynchronously that executes sequentially.

So, in RxJava, Observables type is an example of a structure that represents a stream of items. Its body does not get executed until it is subscribed to by a subscriber and once it is subscribed, subscriber starts getting the data items emitted. Similarly, Flow works on the same condition where the code inside a flow builder does not run until the flow is collected.

Start Integrating Flow APIs in your Project

Let us create an android project and then let’s start integrating the Kotlin Flow APIs.

Step 01

Add the following in the app’s build.gradle,

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"

and in the project’s build.gradle add,

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"

Step 02

In MainActivity’s layout file let’s create a UI that will have a button.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="Launch Kotlin Flow"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 03

Now, let’s begin the implementation of Flow APIs in MainActivity. In onCreate() function of Activity lets add two function like,

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setupFlow()
    setupClicks()
}

Here, setupFlow() is the function where we will define the flow and setupClicks() is the function where we will click the button to display the data which is emitted from the flow.

We will declare a lateinit variable of Flow of Int type,

lateinit var flow: Flow<Int>

Step 04

Now, in setupFlow() I will emit items after 500milliseconds delay.

fun setupFlow(){
    flow = flow {
        Log.d(TAG, "Start flow")
        (0..10).forEach {
            // Emit items with 500 milliseconds delay
            delay(500)
            Log.d(TAG, "Emitting $it")
            emit(it)

        }
    }.flowOn(Dispatchers.Default)
}

Here,

  • We will emit numbers from 0 to 10 at 500ms delay.
  • To emit the number we will use emit() which collects the value emittedIt is part of FlowCollector which can be used as a receiver.
  • and, at last, we use flowOn operator which means that shall be used to change the context of the flow emission. Here, we can use different Dispatchers like IO, Default, etc.

flowOn() is like subscribeOn() in RxJava.”

Step 05

Now, we need to write setupClicks() function where we need to print the values which we will emit from the flow.

private fun setupClicks() {
    button.setOnClickListener {
        CoroutineScope(Dispatchers.Main).launch {
            flow.collect {
                Log.d(TAG, it.toString())
            }
        }
    }
}

When we click the button we will print the values one by one.

Here,

flow.collect now will start extracting/collection the value from the flow on the Main thread as Dispatchers.Main is used in launch coroutine builder in CoroutineScope. The output which will be printed in Logcat is,

D/MainActivity: Start flow
D/MainActivity: Emitting 0
D/MainActivity: 0
D/MainActivity: Emitting 1
D/MainActivity: 1
D/MainActivity: Emitting 2
D/MainActivity: 2
D/MainActivity: Emitting 3
D/MainActivity: 3
D/MainActivity: Emitting 4
D/MainActivity: 4
D/MainActivity: Emitting 5
D/MainActivity: 5
D/MainActivity: Emitting 6
D/MainActivity: 6
D/MainActivity: Emitting 7
D/MainActivity: 7
D/MainActivity: Emitting 8
D/MainActivity: 8
D/MainActivity: Emitting 9
D/MainActivity: 9
D/MainActivity: Emitting 10
D/MainActivity: 10

As we can see Flow starts only when the button is clicked as it prints Start Flow and then starts emitting. This is what we meant by Flows are cold.

Let’s say we update the setupFlow() function like,

private fun setupFlow() {
    flow = flow {
        Log.d(TAG, "Start flow")
        (0..10).forEach {
            // Emit items with 500 milliseconds delay
            delay(500)
            Log.d(TAG, "Emitting $it")
            emit(it)
        }
    }.map {
        it * it
    }.flowOn(Dispatchers.Default)
}

Here we can see that we added map operator which will take each and every item will square itself and print the value.

Anything, written above flowOn will run in background thread.

Builders in Flow

Flow builders are nothing but the way to build Flows. There are 4 types of flow builders:

  1. flowOf() – It is used to create flow from a given set of values. For Example:
flowOf(4, 2, 5, 1, 7).onEach { delay(400) }.flowOn(Dispatcher.Default)

Here, flowOf() takes fixed values and prints each of them after a delay of 400ms. When we attach a collector to the flow, we get the output:

D/MainActivity: 4
D/MainActivity: 2
D/MainActivity: 5
D/MainActivity: 1
D/MainActivity: 7

2. asFlow() – It is an extension function that helps to convert type into flows. For Example:

(1..5).asFlow().onEach{ delay(300)}.flowOn(Dispatchers.Default)

Here, we converted a range of values from 1 to 5 as flow and emitted each of them at a delay of 300ms. When we attach a collector to the flow, we get the output,

D/MainActivity: 1
D/MainActivity: 2
D/MainActivity: 3
D/MainActivity: 4
D/MainActivity: 5

The output is the number being printed from 1 to 5, whichever were present in the range.

3. flow{} – This example has been explained in the Android Example above. This is a builder function to construct arbitrary flows.

4. channelFlow{} – This builder creates cold-flow with the elements using send provided by the builder itself. For Example,

channelFlow {
    (0..10).forEach {
        send(it)
    }
}.flowOn(Dispatchers.Default)

This will print,

D/MainActivity: 0
D/MainActivity: 1
D/MainActivity: 2
D/MainActivity: 3
D/MainActivity: 4
D/MainActivity: 5
D/MainActivity: 6
D/MainActivity: 7
D/MainActivity: 8
D/MainActivity: 9
D/MainActivity: 10

Now, finally, let’s discuss how to use an operator of Flow in our project.

Few examples using Flow Operators

Zip Operator

In above Android Example, we have two functions setupFlow() and setupClicks(). We will modify both functions in MainActivity.

First, we will declare two lateinit variables of Flow of String type,

lateinit var flowOne: Flow<String>
lateinit var flowTwo: Flow<String>

Now, the two functions setupFlow() and setupClicks() will be called in onCreate() of MainActivity.

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    setupFlow()
    setupClicks()

}

and in the setupFlow(), we will initialize two flows to the two variables,

private fun setupFlow() {
    flowOne = flowOf("1", "2", "3").flowOn(Dispatchers.Default)
    flowTwo = flowOf("4", "5", "6").flowOn(Dispatchers.Default)

}

and in setupClicks(), we will zip both the flows using zip operator,

private fun setupClicks() {
    button.setOnClickListener {
        CoroutineScope(Dispatchers.Main).launch {
            flowOne.zip(flowTwo)
            { firstString, secondString ->
                "$firstString $secondString"
            }.collect {
                Log.d(TAG, it)
            }
        }
    }
}

Here,

  • when the button is clicked the scope is launched.
  • flowOne is zipped with flowTwo to give a pair of values that we have created a string and,
  • then we collect it and print it in Logcat. The resulting output will be,

D/MainActivity: 14
D/MainActivity: 25
D/MainActivity: 36

If both flows doesn’t have the same number of item, then the flow will stop as soon as one of the flow completes.

That’s all about in this article.

Conclusion

In this article, we learned about how Flow APIs work in Kotlin and how can we start using it in our android projects. We have also discussed builders in flow and zip operators.

Thanks for reading ! I hope you enjoyed and learned about Flow API concepts in Kotlin. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – Extension Functions vs Static Utility Class in Kotlin

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, We will learn about Extension Functions and Static Utility Class differences in Kotlin. We will talk about when to use extension function and when to use static utility class in the android project using Kotlin.

To understand the extension function and static utility class concepts, We will focus on the below topics :

  • What are Extension function and Util Class?
  • Use Cases
  • Where we use them?
  • Advantages of using Wrapper like Extension or Util Class.

A famous quote about Learning is :

” Change is the end result of all true learning.”


So Let’s Start.


What are Extension function and Util class?

Extension function:

These are like extensive property attached to any class in Kotlin. It provides additional methods to that class without manually inheriting the class.

For example, Let’s say, we have views where we need to play with the visibility of the views. So, we can create extension function for views like,

fun View.show() {
    this.visibility = View.VISIBLE
}

fun View.hide() {
    this.visibility = View.GONE
}

and to use it we use, like,

toolbar.hide()

Here, we have attached an additional feature of hide() and show() to views in android.

Here, these above two extension functions can only be used by View Type and not any other type. For example, String can’t use the functions here.

And to access, that view in the function we use this.

Util Class:

Util class is like a collection of static functions whose code can be reused again and again by passing the reference of the type as a parameter.

For example, if we take the above example of visibility Gone and Visible then update the code according to Util class way,

object Util {
    
    fun show(view: View){
        view.visibility = View.VISIBLE
    }
    fun hide(view: View){
        view.visibility = View.GONE
    }
}

and to use it we use,

Util.show(imageView)

Here, we can see unlike extension function, we need to pass the reference of the view as a parameter.

Or we can directly write the util functions without Util object. For example, we will create a file Util.kt and update the file as,

fun show(view: View){
    view.visibility = View.VISIBLE
}
fun hide(view: View){
    view.visibility = View.GONE
}

and to use this we can directly call,

show(imageView)

We can create Util both ways mentioned above.


Use Cases

Consider this like, when creating an extension function in Kotlin, it creates a property for that specific type(let’s say ImageView) which can be accessed across by all the ImageViews.

But, when Using Util, we don’t add this property again, and again to use the util functions, we need to explicitly call it.

So, now let’s understand this with an example.

Consider we have an ImageView, where we want to load image from url. We might consider using Glide or Picasso for this. So, here I will use Glide and create an extension function like

fun ImageView.loadImage(url: String) {
    Glide.with(this.context).load(url).into(this)
}

Here, we can see, we created an extension function called loadImage() which takes url as a parameter and we load the url into the ImageView.

Now, to use this we just use,

imageViewProfile.loadImage("url")

This loadImage property is accessed by the imageViewProfile and here the extension function is helping the ImageView to load the image from url.

Now, think this as this won’t be just accessible for imageViewProfile but for all the ImageViews in the app but will only be accesible by ImageViews. loadImage() has become like property to ImageViews now. All the ImageViews can access it but just using the dot operator.

Now, Consider the same example using Util class. Here we need to pass both them imageView and the url to load the image.

fun loadImage(imgView:ImageView,url:String){
    Glide.with(this.context).load(url).into(imgView)
}

and to use this we have to call 

Util.loadImage()

Now, here it is not associated with imageView. We are passing the ImageView as a parameter to this function along with the url to load the url in the image view.


When to use Extension function or Util function?

Let’s say if we have a multiple occurrence of the same action across the app like loading image from url in ImageView in multiple ImageViews across the app, we should prefer using extension function as it is like the major use-case here and so it gets attached to the imageView as a property.

But, when we don’t want to use the property heavily and its a very rare occurrence in the app like once or twice we should use util functions.


Advantages of using Wrapper like Extension or Util Class

In any case, using either extension functions or util class helps us a lot while developing the android app. It helps us,

  • to make the code reusable.
  • to make the code more readable.
  • to migrate to another library very easily.

Let’s say in the above example,

fun ImageView.loadImage(url: String) {
    Glide.with(this.context).load(url).into(this)
}

Here, the loading of image using glide is written once and can be used multiple times.

Using the loadImage() as the name makes our code readable as when we use it like,

imageViewProfile.loadImage("url")

the name itself makes it more clear that we are trying to load a url to the ImageView.

And at last, the biggest advantage of using a wrapper helps us in migration to another library if required. For example, let’s say someday we need to remove the Glide library and replace the code with Picasso to load images.

Then we need to only update the code only once in either extension function or the util class and the library would be replaced successfully. We would not have to change or tweak any code in view files.

That’s all about in this article.


Conclusion

In this article, We understood about Extension Functions and Static Utility Class Usage in Kotlin. Depending upon the use-case we should decide which way to use to make our code reusable.

Thanks for reading ! I hope you enjoyed and learned about Extension Functions and Static Utility Class Concepts in Kotlin. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – Understanding Property Delegation In Kotlin

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, We will learn about Property Delegation concepts in Kotlin. The Kotlin programming language has native support for class properties. Properties are usually backed directly by corresponding fields, but it does not always need to be like this – as long as they are correctly exposed to the outside world, they still can be considered properties. This can be achieved by handling this in getters and setters, or by leveraging the power of Delegates.

We will discuss the below topics to understand the Property Delegation Concepts in Kotlin:

  • What is Property Delegation?
  • What are Default Delegated Properties in Kotlin?
  • How to create a custom Delegation Property?
  • How to use Kotlin Delegation Property with Android Development?

A famous quote about Learning is :

” The beautiful thing about learning is that nobody can take it away from you.”


So Let’s begin.


What is Property Delegation?

A “Delegate” is just a class that provides the value of a property and handles its changes. This will help us to delegate(assign or pass on)the getter-setter logic altogether to a different class so that it can help us in reusing the code.

Simply put, delegated properties are not backed by a class field and delegate getting and setting to another piece of code. This allows for delegated functionality to be abstracted out and shared between multiple similar properties – e.g. storing property values in a map instead of separate fields.

Delegated properties are used by declaring the property and the delegate that it uses. The by keyword indicates that the property is controlled by the provided delegate instead of its own field.

This is an alternative to inheritance property.


What are Default Delegated Properties in Kotlin?

Kotlin contains some inbuilt examples for Delegated Properties such as:

  • lazy properties: the value gets computed only upon first access;
  • observable properties: listeners get notified about changes to this property;
  • storing properties in a map, instead of a separate field for each property.


How to create a custom Delegation Property?

For the case of simplicity, let’s take a very simple use-case. Let’s consider a scenario where we want a String property that always gets trimmed and has a common appended string after the original value.

The general way we would do this is:

var string: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
fun main() {
    string = "checking.....        "
    println(string)
}

//output: checking..... is a String!

We can see that the extra spaces are trimmed and the required string is appended.

Well, this is good for one variable. What if we have a bunch of string variables containing the same functionality?

We have to keep adding this setter property repeatedly:

var stringOne: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
var stringTwo: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
var stringThree: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
.
.
.
.

This meets our requirement but we see a lot of repetitive code. So, how can we resolve this?

Yes, We can resolve by Property Delegation!

Here, we shall “delegate” the property of trimming and appending the common string to a separate class so that we can reuse this wherever required.

There will be times that we want to write our delegates, rather than using ones that already exist. This relies on writing a class that extends one of two interfaces – ReadOnlyProperty or ReadWriteProperty.

Both of these interfaces define a method called getValue – which is used to supply the current value of the delegated property when it is read. This takes two arguments and returns the value of the property:

  • thisRef – a reference to the class that the property is in
  • property – a reflection description of the property being delegated

The ReadWriteProperty interface additionally defines a method called setValue that is used to update the current value of the property when it is written. This takes three arguments and has no return value:

  • thisRef – A reference to the class that the property is in
  • property – A reflection description of the property being delegated
  • value – The new value of the property

Let’s understand by String Trimming Example below as step by step .

  • Let’s create a custom class, let’s name it TrimAppendDelegate
  • To use this class as a Delegate, we have to implement the ReadWriteProperty<> interface
  • Once we implement the interface, we have to implement the abstract members in the interface which are getValue and setValue
  • Finally, we define a private variable of String type(since we are defining a custom Delegate for our String property) inside our Delegate class and define the getValue and setValue properties.
class TrimAppendDelegate : ReadWriteProperty<Any, String> {
    private var trimAppendedString = ""
    override fun getValue(thisRef: Any, property: KProperty<*>) =   trimAppendedString

override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
        trimAppendedString = "${value.trim()} is a String!"
    }
}
}

//Usage :

private var trimmedString by TrimAppendDelegate()
trimmedString = "This..      "
println(trimmedString)

//Output : This.. is a String!

This is how we can achieve the property of Delegation.


How to use Kotlin Delegation Property with Android Development?


Using Built-in delegated property Lazy:

Most of the time, we see the usage of lateinit var in our UI(Activities/Fragments) classes or View Models. We can use the concept of the lazy delegate in place of lateinit var. The variable will be initialized the first time it is used.


Using Built-in delegated property Observable:

  • Most of our Android applications use Recycler views. We know that every recycler view is associated with its respective adapter. Every time the data structure (list of objects) changes in the adapter, we call notifyDataSetChanged to update our Recycler view.
  • We can use the inbuilt delegated property “Observable” to get the old and changed values of the data structure(list of objects)
private var users: ArrayList<User> by Delegates.observable(arrayListOf()) { property, oldValue, newValue ->
    Log.d("Old value ${property.name} size", "${oldValue.size}")
    Log.d("New value ${property.name} size", "${newValue.size}")
    notifyDataSetChanged()
}

In the above code snippet, we can consider User as one of the model classes, and “users” is a list of User objects. We can access the old and new values whenever the value for “user” changes. Finally, we can call notifyDataSetChanged, if there is a change in oldValue and newValue by comparison.

To access the advantage of this Observable delegated property, the parameter should be a “var” instead of “val”. Else the changes cannot be identified, because, val, in Kotlin, is read-only.


Using Built-in delegated property Observable along with Lazy:

We can also use this “Observable” property along with “lazy” for updating our views. This can be very helpful if we are not using the concept of LiveData in our application.

Let’s understand the code snippet :

class MainActivity : AppCompatActivity() {
    
    private val textView: TextView by lazy { textview }
    private var text: String by Delegates.observable("") { _, _, newValue ->
        textView.text = newValue
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text = "NewText"
    }
    
}

In the above code snippet, We have initiated textView with a lazy delegate just for our safety so that there will be no null pointer exception while accessing it. Whenever there is a change in the “text” variable, the text view is updated with the new value. This is similar to the concept of Live Data.


Using Built-in delegated property Lazy along with Custom Delegate:

Let’s say we want to save a boolean shared preference in our MainActivity.kt file (Any activity class file). Let’s create a custom delegate to store a Boolean shared preference value:

//Delegating the boolean preference saving option
class BooleanPreference(
    private val preferences: Lazy<SharedPreferences>,
    private val name: String,
    private val defaultValue: Boolean
) : ReadWriteProperty<Any, Boolean> {
    
    @WorkerThread
    override fun getValue(thisRef: Any, property: KProperty<*>): Boolean {
        return preferences.value.getBoolean(name, defaultValue)
    }
    override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) {
        preferences.value.edit().putBoolean(name, value).apply()
    }
    
}

We can see that our custom delegate class takes three parameters, the preferences instance, the name(Key in this case), and the default value.

So, let’s create our shared preferences global instance in our Activity file.

private val prefs: Lazy<SharedPreferences> = lazy { // Lazy to prevent IO access to main thread.
    this.applicationContext.getSharedPreferences(
        PREFS_NAME, MODE_PRIVATE
    )
}
companion object {
    const val PREFS_NAME = "Preferences"
    const val TOGGLE_PREFS = "toggle"
}

Let’s say we are handling the preference of a toggle here in our Activity class. We can just use our custom delegate as follows:

private var togglePreference by BooleanPreference(prefs, TOGGLE_PREFS, false)

Now, let’s say if we want to change the value in the onCreate method(or any click listener, in general):

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    togglePreference = true
    Toast.makeText(this, "$togglePreference", Toast.LENGTH_SHORT).show()  // Shows true
}

We just use it as a simple variable assignment. Looks clean and concise!

That’s all about in this article.


Conclusion

In this article, We understood about Property Delegation concepts in Kotlin. We have discussed about What is Property Delegation, Default Delegated Properties, create Custom Property Delegation and how to use Property delegation with Android Development. Property delegation is a powerful technique, that allows you to write code that takes over control of other properties, and helps this logic to be easily shared amongst different classes. This allows for robust, reusable logic that looks and feels like regular property access.

Thanks for reading ! I hope you enjoyed and learned about Property Delegation concepts in Kotlin. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Exit mobile version