Power of noinline in Kotlin

However, there are scenarios where you might not want all lambda parameters in an inline function to be inlined. This is where the noinline modifier comes into play. In this blog, we’ll explore the purpose of noinline, how it works, and when to use it.

What is noinline?

By default, when you mark a function as inline, all the lambda parameters within that function are also inlined.

The noinline keyword allows you to exclude specific lambda parameters from being inlined, providing greater control over how your inline functions behave.

inline fun performTask(noinline task2: () -> Unit) {}

Why Use noinline?

There are several reasons to use noinline:

  1. Avoid Bytecode Bloat: Prevent large or complex lambdas from being inlined, which can lead to an increase in bytecode size.

  2. Serialization: Lambdas that need to be serialized cannot be inlined.

  3. Higher-order Function Passing: If a lambda is passed as an argument to another function, inlining it might not work as expected.

  4. Reflection: Lambdas marked with noinline can be used with reflection.

Usage of noinline

Here’s an example to understand how noinline works:

Without noinline

inline fun performTask(task1: () -> Unit, task2: () -> Unit) {
    task1()
    task2()
}

fun main() {
    performTask(
        { println("Task 1") },
        { println("Task 2") }
    )
}

In this example, both task1 and task2 are inlined at the call site.

With noinline

inline fun performTask(task1: () -> Unit, noinline task2: () -> Unit) {
    task1()
    task2()
}

fun main() {
    performTask(
        { println("Task 1") },
        { println("Task 2") }
    )
}

In this case, only task1 is inlined, while task2 remains a regular lambda. This gives you more flexibility when handling higher-order functions.

Practical Examples

1. Passing Lambdas to Other Functions

When a lambda is passed to another function, it cannot be inlined. Using noinline ensures compatibility.

inline fun execute(noinline task: () -> Unit) {
    repeat(3) { task() }
}

fun main() {
    execute { println("Executing task") }
}

2. Serialization of Lambdas

Lambdas that need to be serialized cannot be inlined. Use noinline in such scenarios.

import java.io.Serializable

inline fun saveTask(noinline task: () -> Unit): Serializable {
    return object : Serializable {
        val savedTask = task
    }
}

fun main() {
    val task = saveTask { println("Serialized Task") }
    println("Task saved: $task")
}

3. Using Reflection

If you need to analyze a lambda at runtime using reflection, it must not be inlined.

inline fun analyze(noinline task: () -> Unit) {
    println("Lambda class: ${task::class}")
}

fun main() {
    analyze { println("Analyzing lambda") }
}

4. Android Example: Coroutine with noinline

Using coroutines in Android, you can leverage noinline to pass a lambda function to another coroutine or delay execution without inlining.

import kotlinx.coroutines.*

inline fun executeTasks(
    crossinline task1: suspend () -> Unit,
    noinline task2: suspend () -> Unit
) {
    GlobalScope.launch {
        task1() // Can be inlined as it's part of the coroutine's scope
        withContext(Dispatchers.Main) {
            task2() // Will not be inlined; switched to the main thread
        }
    }
}

fun main() {
    executeTasks(
        task1 = {
            println("Task 1 running on ${Thread.currentThread().name}")
        },
        task2 = {
            println("Task 2 running on ${Thread.currentThread().name}")
        }
    )
}

In this example:

  • task1 is marked as crossinline because it runs on the coroutine's background context and cannot return non-locally.

  • task2 is marked as noinline because it's passed to withContext to execute on the main thread without inlining.


When to Use noinline?

Use noinline when:

  • The lambda is passed as an argument to another function.

  • The lambda needs to be serialized.

  • You need to use reflection on the lambda.

  • You want to avoid inlining to reduce bytecode size or complexity.

Conclusion

  • inline Functions: Allow the compiler to copy the function body and lambdas into the call site for performance optimization.

  • noinline: Excludes specific lambda parameters from being inlined, offering greater flexibility and control.

  • Use noinline in scenarios involving serialization, reflection, or when passing lambdas to other functions.

Akshay Nandwana
Founder AndroidEngineers

You can connect with me on:


Book 1:1 Session here
Click Here

Join our upcoming classes
https://www.androidengineers.in/courses

Love from our past students

Excellent list of questions really helped me to coverup all the topics before interview.

Saiteja Janjirala

10th Oct, 2024

I had an exceptional experience with the 1:1 mentorship session. Akshay was incredibly friendly and provided invaluable guidance on focusing on long-term goals. They also gave great interview tips, including a thorough resume review. Additionally, the discussion on Data Structures and Algorithms (DSA) was insightful and practical. Highly recommended for anyone looking to advance their career!

Nayab khan

11th Sep, 2024

Cleared my major points for what I am missing in the resume and also suggested what I can work on for further growth in the career.

Ketan Chaurasiya

7th Aug, 2024

What impressed me most was his personalized approach and practical tips that I could immediately apply. Akshay’s guidance not only improved my technical skills but also boosted my confidence in navigating my career path. His insights and encouragement have been a game-changer for me. I highly recommend Akshay’s mentorship to anyone looking to advance their Android development career.

Hardik Kubavat

5th Aug, 2024

2025© Made with   by Android Engineers.