Android – An Overview Of Navigation Component In Android

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn about an overview of navigation component in Android. We will discuss about navigation graph and how to pass arguments safely.

The Navigation Architecture Component simplifies implementing navigation, while also helping you visualize our app’s navigation flow. The library provides a number of benefits, including:

  • Automatic handling of fragment transactions
  • Correctly handling up and back actions by default
  • Default behaviors for animations and transitions
  • Deep linking as a first-class operation
  • Implementing navigation UI patterns (like navigation drawers and bottom nav) with little additional work
  • Type safety when passing information while navigating
  • Android Studio tooling for visualizing and editing the navigation flow of an app

The Navigation component requires Android Studio 3.3 or higher and is dependent on Java 8 language features.

A famous quote about learning is :

” I am still learning.”


So Let’s begin.


Overview

The Navigation Component consists of three key parts:

  1. Navigation Graph (New XML resource) — This is a resource that contains all navigation-related information in one centralized location. This includes all the places in our app, known as destinations, and possible paths a user could take through our app.
  2. NavHostFragment (Layout XML view) — This is a special widget you add to our layout. It displays different destinations from our Navigation Graph.
  3. NavController (Kotlin/Java object) — This is an object that keeps track of the current position within the navigation graph. It orchestrates swapping destination content in the NavHostFragment as we move through a navigation graph.


Navigation Component Integration

Just include the following code in the dependencies block of our module-level build.gradle file:

def nav_version = "2.2.2"
  // Java language implementation
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"


Navigation Graph

First, we will create a file that will contain our navigation graph. In the res, directory create a new android resource file as follows:

This will create an empty resource file named nav_graph.xml under the navigation directory.

For example, we have two fragments named FirstFragment and SecondFragment. FirstFragment has a button on click of which we will navigate to the SecondFragment.

We define these fragments in the navigation graph as below:

<navigation 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:id="@+id/nav_graph"
    app:startDestination="@id/nav_first_fragment">

    <fragment
        android:id="@+id/nav_first_fragment"
        android:name="app.navigationcomponentexample.FirstFragment"
        tools:layout="@layout/fragment_first">

        <action
            android:id="@+id/action_first_to_second"
            app:destination="@id/nav_second_fragment"/>

    </fragment>

    <fragment
        android:id="@+id/nav_second_fragment"
        android:name="app.navigationcomponentexample.SecondFragment"
        tools:layout="@layout/fragment_second"/>

</navigation>

Here the root tag named navigation has a parameter called app:startDestination which has the id of our first fragment. This defines that the first fragment will be loaded in the NavHostFragment automatically.

The Navigation Component introduces the concept of a destination. A destination is any place you can navigate to in your app, usually a fragment or an activity. These are supported out of the box, but we can also make our own custom destination types if needed.

Notice that for first fragment we have defined an action with the following attributes:

android:id="@+id/nav_first_fragment"
app:destination="@id/nav_second_fragment"

Each action should have a unique id which we will use to navigate to the required destination.

Here the destination points to the id of the second fragment defined in the nav graph, which means that with this action we will navigate to the second fragment.

After this step when we open the nav_graph.xml and switch to the design tab, it should look like the following:


Navigation Types

With the navigation component, we have multiple ways to navigate :


1. Navigation using destination Id

We can provide the id of the destination fragment to navigate to it, like the following:

button.setOnClickListener {
    findNavController().navigate(R.id.nav_second_fragment)
}


2. ClickListener

For views, we can also use createNavigateOnClickListener() method from the Navigation class as follows:

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.nav_second_fragment, null))


3. Navigation using Actions

As in the above nav graph, we have defined action in the first fragment, we can use the id of the action as follows:

button.setOnClickListener {
    findNavController().navigate(R.id.action_first_to_second)
}

The last piece required is to define the NavHostFragment. It is a special widget that will display the different destinations defined in the nav graph. Copy the following code and paste it in the layout of the activity in which we want to load our FirstFragment.

<fragment
    android:id="@+id/nav_host_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:navGraph="@navigation/nav_graph"
    app:defaultNavHost="true"/>

android:name="androidx.navigation.fragment.NavHostFragment" defines the NavHostFragment used by NavController

app:defaultNavHost="true" is simply stating that you want this to be the NavHost that intercepts and works as the back button on our device.

app:navGraph="@navigation/app_navigation" associates the NavHostFragment with a navigation graph. This navigation graph specifies all the destinations the user can navigate to, in this NavHostFragment.

After these steps when we run the app, FirstFragment should be loaded automatically and when we click the button it should open the SecondFragment. Also when we press the back button, it should navigate back to the FirstFragment.


Safe Arguments

The navigation component has a Gradle plugin, called safe args, that generates simple object and builder classes for type-safe access to arguments specified for destinations and actions.

Safe args allows getting rid of the code like below:

val username = arguments?.getString("usernameKey")

with the following:

val username = args.username


Safe Arguments Integration

Add the following code in the top-level Gradle file:

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.2.2"

Now import the plugin into the module level Gradle file:

apply plugin: 'androidx.navigation.safeargs.kotlin'

Now the safe args plugin is active in our project. We will add 2 arguments to be passed to the SecondFragment from the FirstFragment. We will define arguments in the nav graph as follows:

<fragment
    android:id="@+id/nav_second_fragment"
    android:name="app.navigationcomponentexample.SecondFragment"
    tools:layout="@layout/fragment_second">

    <argument
        android:name="arg1"
        app:argType="integer"
        android:defaultValue="0"/>
    <argument
        android:name="arg2"
        app:argType="string"
        android:defaultValue="default"/>

</fragment>

Here the first argument is named arg1 which is of type Integer and has the default value of 0. Similarly, the second argument is named arg2 which is of type String and has a default value of “default”.

After we define these arguments, Gradle will generate a class named SecondFragmentArgs which can be used in SecondFragment to retrieve the arguments in the following way.

val safeArgs: SecondFragmentArgs by navArgs()

val arg1 = safeArgs.arg1
val arg2 = safeArgs.arg2

Here we are assured that arg1 is of type Integer and arg2 is of type String and thus we don’t need to cast them to their respective types.

Now in order to pass these arguments from the FirstFragment, another class named FirstFragmentDirections gets created which has a static method named actionFirstToSecond. This can be used to pass the arguments in the following way.

button.setOnClickListener {
    val directions = FirstFragmentDirections.actionFirstToSecond(arg1 = 1234, arg2 = "abcd")
    findNavController().navigate(directions)
}

That’s all is required to pass arguments in a type-safe manner. Apart from the inbuilt types, we can also define the custom type of arguments by creating a Parcelable class.

That’s all about in this article.


Conclusion

In this article, we learned about an overview of navigation component in Android. We have also discussed about navigation graph and how to pass arguments safely in Android.

Thanks for reading ! I hope you enjoyed and learned about Navigation Component 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.

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 GradientDrawable In Android ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn when and how to use GradientDrawable in android. The UI of modern-day apps is getting better and better. Designers are trying out different styles and combinations which go best with the Android App. One of the key components of Android being used these days is called GradientDrawable.

A famous quote about learning is :

” I am always ready to learn although I do not always like being taught.”


So Let’s Begin.


Introduction

GradientDrawable is drawable with a color gradient for buttons, backgrounds, etc.

Let’s start by taking a basic example of creating a button in Android having an aqua colored background:

This can be done simply by setting the android:background attribute in the XML:

<Button
    android:layout_width="0dp"
    android:layout_height="48dp"
    android:layout_marginStart="24dp"
    android:layout_marginEnd="24dp"
    android:text="Continue"
    android:background="#00FFFF"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent" />

Here #00FFFF is the color code for Aqua color.

Now, what if we want something like this:

For this type of button ,we need to use gradients.


Different Ways To Use Gradient

There are 2 ways to do this:


1. Using a drawable resource in XML

For this, under the drawable package, create a new file tri_color_drawable.xml and enter the following code:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <gradient
        android:angle="0"
        android:startColor="#D98880"
        android:centerColor="#F4D03F"
        android:endColor="#48C9B0"/>
</shape>

Here we have given a <gradient /> tag and added the three color codes – startColorcenterColor and endColor. We have also given the angle as 0 which denotes LEFT-RIGHT orientation.

We note that Angles can only be multiples of 45. Also, max 3 colors can be specified in XML – startColor, centerColor and endColor. All 3 will occupy equal space.

Then make changes in the <Button /> tag to make it use this background drawable:

android:background="@drawable/tri_color_drawable"


2. GradientDrawable

We can get the same functionality programmatically as well by using GradientDrawable.
In our activity, create the GradientDrawable.

val gradientDrawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
    intArrayOf(
        0XFFD98880.toInt(),
        0XFFF4D03F.toInt(),
        0XFF48C9B0.toInt()
    ))

Here we have given the orientation as LEFT_RIGHT (which corresponds to 0 we added in XML earlier). We have also given the same three colors we used earlier.

Next, set this gradientDrawable as the background of the button.

val continueBtn: Button = findViewById(R.id.continue_btn)
continueBtn.background = gradientDrawable


The need of GradientDrawable

So why do we need GradientDrawable if the work can be done using XML?

Example 1

Let’s take a recap of the previous example.

Here each color is taking equal space. We can say that color 1 is starting at 0 percent, color 2 at ~33 percent, and color 3 at ~66 percent. What if you don’t want all colors to occupy equal space? Instead, you want something like this:

If we notice here, color 1 is taking half of the entire space (50 percent) whereas the other two colors are equally covering the remaining space. (25 percent each).

This cannot be achieved via XML. This is where the actual power of GradientDrawable is unleashed.

To achieve the above result, we can do something like this:

val gradientDrawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
    intArrayOf(
        0XFFD98880.toInt(),
        0XFFD98880.toInt(),
        0XFFF4D03F.toInt(),
        0XFF48C9B0.toInt()
    ))

Here color 1 is used twice, hence it will take 50 percent of the total space. Remaining two colors will equally occupy the remaining 50 percent space.

Example 2

As another example, let’s say we want a 5 colors gradient.

This also is not possible via XML but can be easily done using GradientDrawable like this:

val gradientDrawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
    intArrayOf(
        0XFFD98880.toInt(),
        0XFFF4D03F.toInt(),
        0XFF48C9B0.toInt(),
        0XFF2C3E50.toInt(),
        0XFFAF7AC5.toInt()
    ))

Here we have given 5 colors and each will cover equal space. You can give as many colors as required.


GradientDrawable’s Main Components

let’s take a closer look at GradientDrawable’s main components:


1. Orientation

It can be one of the orientations as defined in the enum GradientDrawable.Orientation. In our examples, we have used LEFT_RIGHT. Other possible orientations are TOP_BOTTOMTR_BLRIGHT_LEFT, etc. They are self-explanatory.


2. Array of Colors

Here we need to provide an array of hexadecimal color values.
For 0XFFD98880,

  • 0X -Represents a hexadecimal number.
  • FF – Represents the alpha value to be applied to the color. Alpha can be 0 to 100. Here FF represents 100% alpha.
  • D98880 – Represents the RRGGBB hexadecimal color value.

That’s all about in this article.


Conclusion

In this article, we learned about how to use GradientDrawable in Android. We have also discussed different ways and main components of GradientDrawable in Android with examples.

Thanks for reading ! I hope you enjoyed and learned about GradientDrawable 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.

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 !!???

A Short Note – R8 vs Proguard In Android

Hello Readers, CoolMonkTechie heartily welcomes you in A Short Note Series (R8 vs Proguard In Android).

In this note series, we will understand about R8 vs Proguard in Android. Google released R8 as a replacement of Proguard to help developers shrink the code with the better-generated output (APK). They are considered much faster compared to Proguard.

So Let’s begin.

What is R8?

R8 is a tool that converts our java byte code into an optimized dex code. It iterates through the whole application and then it optimizes like removing unused classes, methods, etc. and runs on the compile time. It helps us to reduce the size of the build and to make our app to be more secure. R8 uses Proguard rules to modify its default behavior.

How does R8 shrinking work?

While optimizing the code, R8 reduces the code of our application, and then APK size is reduced.

To reduce the APK size, we have three different techniques:

  1. Shrinking or Tree Shaking: Shrinking is the process of removal of unreachable code from our Android project. R8 performs some static analysis to get rid of unreachable code and removes the un-instantiated object.
  2. Optimization: This is used to optimize the code for size. It involves dead code removal, unused argument removal, selective in-lining, class merging, etc.
  3. Identifier Renaming: In this process, we obfuscate the class name and other variable names. For example, if the name of the class is “MainActivity“, then it will be obfuscated to “a” or something else but smaller in size.

How to enable R8 Shrinking in our app?

R8 is present by default in our application but to enable R8 shrinking in our application, set the minifyEnabled to true in our app’s main build.gradle file.

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
        }
    }
}

R8 vs Proguard

So, let’s now compare both R8 and Proguard both and see how it fares,

  • With the Android app using Gradle plugin above 3.4.0 or more the project uses R8 by default and no longer uses the Proguard to perform optimizations. But, it uses Proguard rules only.
  • R8 inlines the container classes and removes unused class, fields, and methods. Proguard reduces the app size by 8.5% and compared to R8 which reduces the code by 10%.
  • R8 has more Kotlin support compared to Proguard.
  • R8 gives better outputs than Proguard, and to do so faster than Proguard does, reducing overall build time.

So, let’s now compare how both Proguard and R8 performs.

Proguard

While using Proguard, it converts the Applications code to Java byte-code by the Java compiler. After the conversion, it is then optimized by Proguard using the rules which we have written. Then dex converts it to optimized Dalvik byte code.

This is roughly a 4 step process to convert it to Dalvik byte-code.

R8

While using R8, first the app’s code is converted to Java byte-code by the java compiler and then using R8 directly, it converts the java byte-code in Dalvik byte-code.

By using R8, it directly reduces the steps of conversion of Java byte-code to Dalvik Byte-code from 2 to 1.

  • Proguard applies 520 peephole optimizations compared to R8, which is very less. Peephole optimizations are the optimizations that are performed on a set of compiler-generated code to improve the performance of the code by making it shorter and faster.
  • In both Proguard and R8, we have to handle the reflection by writing the custom configuration.
  • R8 is faster compared to Proguard in the execution of converting the code.

Optimization Comparison between Proguard and R8

Let us discuss a few features supported by both Proguard and R8.

For example, Proguard and R8 both make the methods private in the code. Both of them also remove unused class, fields, or even methods in the project are not in use. Both of them support the simplification of Enum types. They both inline methods, merge codes, etc.

Proguard also makes the classes final whereas R8 cannot do it. But comparing R8, which is highly supported by Kotlin, optimizes the Kotlin construct which is not possible with Proguard.

Now, If you also want to enable aggressive optimization in R8 and reduce the size more, enable the following in gradle.properties.

android.enableR8.fullMode=true

Conclusion

In this note series, we understood about R8 vs Proguard in Android. We have also discussed R8 and Proguard process and optimization differences. With R8 coming as a default compile-time optimizer, it reduces the size of the app.

Thanks for reading! I hope you enjoyed and learned about R8 vs Proguard 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 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 Optimize APK Size ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn how to optimize application size (APK) in Android. Most of the user would not like to download a large APK as it might consume most of his Network/Wifi Bandwidth, also most importantly, space inside the mobile device. The size of our APK has an impact on how fast our app loads, how much memory it uses, and how much power it consumes.

It’s important to optimize the size of the app since mobiles are always memory and space constraint devices. We will discuss about the various ways in which we can improve our apk size in Android-Development.

A famous quote about learning is :

” That is what learning is. You suddenly understand something you’ve understood all your life, but in a new way.”

So Let’s begin.

Understanding Android App Bundles

An Android App Bundle is a publishing format that includes all your app’s compiled code and resources, and defers APK generation and signing to Google Play.

Google Play uses our app bundle to generate and serve optimized APKs for each device configuration, so only the code and resources that are needed for a specific device are downloaded to run our app. We no longer have to build, sign, and manage multiple APKs to optimize support for different devices, and users get smaller, more optimized downloads.

App Bundles are Publishing formats

An Android App Bundle is a file (with the .aab file extension) that we upload to Google Play. App bundles are signed binaries that organize your app’s code and resources into modules. Code and resources for each module are organized similarly to what you would find in an APK—and that makes sense because each of these modules may be generated as separate APKs. Google Play then uses the app bundle to generate the various APKs that are served to users, such as the base APK, feature APKs, configuration APKs, and (for devices that do not support split APKs) multi-APKs. The directories that are colored in blue—such as the drawable/values/, and lib/ directories—represent code and resources that Google Play uses to create configuration APKs for each module.

Android App Bundles — File Targeting and Serving

  • How do these Android App bundles help in File targeting? It’s simple, let’s say we have hdpi, xhdpi, xxhdpi resources in our application. Based on the device on which the application is being downloaded, if it’s a hdpi device (for example), only the resources from hdpi will be installed on the device.
  • If the application is targeting multiple languages (English, Spanish, French, etc.), only the specific string resources will be downloaded onto the device.
  • This helps in saving the space on the device’s memory.

Building the Android App Bundle

Building the Android app bundle is straight forward. Just select the Build option from the Android Studio menu and select Build Bundles.

Android Size Analyzer

In order to understand which files are actually taking up more space in the application, use the Android Size Analyser plugin inside Android Studio. For installing the plugin.

  1. Select File > Settings (or on Mac, Android Studio > Preferences.)
  2. Select the Plugins section in the left panel.
  3. Click the Marketplace tab.
  4. Search for the “Android Size Analyzer” plugin.
  5. Click the Install button for the analyzer plugin.

Restart the IDE after installing the plugin. Now, to analyze the application, go to Analyze > Analyze App Size from the menu bar. We will get a window something similar to this:

The recommendations can help us in reducing the app size in a much better way.

Remove Unused Resources

As we have already discussed the size of the apk has an impact on how fast the app loads, how much memory it uses, and how much memory power it consumes. Hence, one of the main things that can be implemented to reduce apk size is to remove unused resources in the application.

Also, it is advised to use scalable drawable objects(importing vector assets) instead of other image formats like PNG, JPEG, etc.

Using Vector Drawable is one of the best ways to reduce the size significantly.

Using Lint

Lint actually helps in generating warnings or unused code inside the application. So this can actually help in removing the same thereby helping in reducing the size of the application.

Reduce libraries size

Check if we can reduce the size when it comes to the usage of libraries. For example, use only specific libraries of Google Play Services. Compile only which is required.

Reuse Code

Object-Oriented Programming has solved a lot of problems in the programming world. Try reusing the code as much as possible instead of repetitive code. Repetitive code also leads to increased file size thereby effecting the Apk size.

Compress PNG and JPEG files

If using PNG and JPEG files is something mandatory in our project, we can compress them using image quality tools like TinyPNG.

In most of the applications, Images are used to convey messages or improve the UX. But the biggest drawback here might be using a lot of images that can bloat up the size of the app. Ensure that the Image Compression techniques are understood and implemented to reduce the size of the apk before releasing the app to the play store.

Use WebP file format

As we have seen in the image shared for the Android Analyser plugin above, one of the recommendations was to change the PNG file to a WebP file format.

Use Proguard

Every time we build a new project, we see the following piece of code in the app-level build.gradle file.

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

ProGuard makes the following impact on our project,

  • It reduces the size of the application.
  • It removes the unused classes and methods that contribute to the 64K method counts limit of an Android application.
  • It makes the application difficult to reverse engineer by obfuscating the code.

Create Multiple APKs

If we are not using App Bundles, we can go with the traditional way. We can create multiple APKs similar to App Bundle. Multiple apk is mainly used to generate specific APKs for different screen densities and different CPU architecture.

ShrinkResources

Reduce resources where ever possible. Using shrinkResources attribute in the Gradle will remove all the resources which are not being used anywhere in the project. Enable this in your app-level build.gradle file by adding below line:

buildTypes {
    release {
        ........
        shrinkResources true
        ........
    }
}

ResConfigs

Remove the localized resources which are not needed by using resConfigs. All the support libraries may have localized folders for the other languages which we don’t need.

The Gradle resource shrinker removes only resources that are not referenced by our app code, which means it will not remove alternative resources(device/location-specific) for different device configurations. If necessary, you can use the Android Gradle plugin’s resConfigs property to remove alternative resource files that our app does not need.

The following snippet shows how to limit our language resources to just English and French:

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

As discussed, having unused language resources only swells the apk size. Hence it is important to remove unused files and resources.

DebugImplementation

Remove any debug library you have in the app. It can be done by using debugImplementation while building testing debug apk.

Use R8 to reduce APK size

R8 shrinking is a process in which we reduce the amount of code of our application and by doing so, the APK size automatically gets reduced. R8 does most of the work as Proguard. 

So Why do we need to prefer it?

The reason is it works with Proguard rules and shrinks the code faster while improving the output size.

So understanding and implementing these different methods in our applications to deliver an optimized apk. 

That’s all about in this article.

Conclusion

In this article, we learned about different methods to optimize app size in Android.

Thanks for reading ! I hope you enjoyed and learned about Reducing application size 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.

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 Communicate Between Fragments Using SharedViewModel ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn how we can use the ViewModel in our application to communicate between various fragments in our application. We say it as SharedViewModel. Communication between Activities or Fragments in Android is a very common thing. Almost every application has some communication between various activities or fragments.

Using SharedViewModel, we can communicate between fragments.

A famous quote about learning is :

“Live as if you were to die tomorrow. Learn as if you were to live forever.”

So Let’s begin.

Data sharing between Fragments

we can communicate between fragments using SharedViewModel. If we consider two fragments, both the fragments can access the ViewModel through their activity.

In this example, one fragment updates the data within the ViewModel which is shared between both the fragments and another fragment observes the changes on that data. We need to add the dependencies required for ViewModel, LiveData in the example.

First, we will create a class SharedViewModel.

class SharedViewModel : ViewModel() {
    val message = MutableLiveData<String>()

    fun sendMessage(text: String) {
        message.value = text
    }
}

Now, we are going to create two Fragments:

  • MessageReceiverFragment: This Fragment is going to receive the message which will be sent by MessageSenderFragment. It will have a TextView which will show the received message.
  • MessageSenderFragment: This Fragment is going to send the message which will be received by MessageReceiverFragment. It will have a Button to send the message.

MessageReceiverFragment class:

class MessageReceiverFragment : Fragment() {

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
        model.message.observe(viewLifecycleOwner, Observer {
            textViewReceiver.text = it
        })
    }
}

fragment_receiver layout:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textViewReceiver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Your Message"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MessageSenderFragment class:

class MessageSenderFragment : Fragment() {
    lateinit var model: SharedViewModel

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
        button.setOnClickListener { model.sendMessage("MindOrks") }
    }
}

fragment_sender layout:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Your Message"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Now, let’s update the activity_main.xml.

<?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">

    <fragment
        android:id="@+id/receiverFragment"
        android:name="com.mindorks.sharedviewmodelsample.MessageReceiverFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toTopOf="@+id/senderFragment"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <fragment
        android:id="@+id/senderFragment"
        android:name="com.mindorks.sharedviewmodelsample.MessageSenderFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/receiverFragment" />


</androidx.constraintlayout.widget.ConstraintLayout>

Our MainActivity Class:

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

Now, we are ready to run the app, see if it is working as expected. It should work.

Let’s discuss how it is working:

  • Here, we have created an activity that consists of two fragments. The same activity is the host for both the fragment.
  • In both the fragment, we have created the object of SharedViewModel which is the same object as we are using the same single activity as an owner. This is the reason it is shared. Notice that we have used the requireActivity().
  • In the MessageSenderFragment, we are sending the message on button click, which sets the value of message - LiveData in the SharedViewModel.
  • Then, in the MessageReceiverFragment, we are observing the change in the message - LiveData, and whenever the message is coming, we are updating the data in textView.

This is how it works.

That’s all about in this article.

Conclusion

In this article, we learned about Shared ViewModel in Android to communicate with other fragments. In the last, we saw one example of fragment communication using Shared ViewModel

Thanks for reading ! I hope you enjoyed and learned about Shared ViewModel in Android to communicate with other fragments. 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 !! ???

A Short Note – RxJava And RxAndroid For Android

Hello Readers, CoolMonkTechie heartily welcomes you in A Short Note Series.

Multi-threading in Android has always been an enormous task for engineers. RxJava has made life easy for all the Android developers using it. In this note series, we are going to discuss RxJava for Android specifically.


So Let’s begin.


What is RxJava?

RxJava is a JVM library for doing asynchronous and executing event-based programs by using observable sequences. Its main building blocks are triple O’s, Operator, Observer, and Observables. And using them we perform asynchronous tasks in our project. It makes multi-threading very easy in our project. It helps us to decide on which thread we want to run the task.

But RxJava is made for primarily any Java projects. To use RxJava in Android, we will also need RxAndroid.


What is RxAndroid?

RxAndroid is an extension of RxJava for Android, which is used only in Android application.

RxAndroid introduced the Main Thread required for Android.

To work with the multithreading in Android, we will need the Looper and Handler for Main Thread execution.

RxAndroid provides AndroidSchedulers.mainThread() which returns a scheduler and that helps in performing the task on the main UI thread that is mainly used in the Android project. So, here AndroidSchedulers.mainThread() is used to provide us access to the main thread of the application to perform actions like updating the UI.

In Android, updating UI from background thread is technically not possible, so using AndroidSchedulers.mainThread() we can update anything on the main thread. Internally it utilizes the concept of Handler and Looper to perform the action on the main thread.

RxAndroid uses RxJava internally and compiles it. But while using RxAndroid in our project we still add the dependency of RxJava to work with like,

implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'


Use-Cases in Android

RxJava has the power of operators and as the saying goes by, “RxJava has an operator for almost everything“.


Case 1:

Consider an example, where we want to do an API call and save it to some storage/file. It would be a long-running task and doing a long-running task on the main thread might lead to unexpected behavior like App Not Responding.

So, to do the above-mentioned task, we might think to use AsyncTask as our go-to solution. But with Android R, AsyncTask is going to be deprecated, and then libraries like RxJava will be the solution for it.

Using RxJava over AsyncTask helps us to write less code. It provides better management of the code, as using AsyncTask might make the code lengthy and hard to manage.


Case 2:

Consider a use-case where we might want to fetch user details from an API and from the user’s ID which we got from the previous API we will call another API and fetch the user’s friend list.

Doing it using AsyncTask we might have to use multiple Async task and manage the results in was way where we want to combine all the AsyncTask to return the result as a single response.

But using RxJava, we can use the power of zip operator to combine the result of multiple different API calls and return a single response.


Case 3:

Consider an example of doing an API call and getting a list of users, and from that, we want only the data which matches the given current condition.

A general approach is to do the API call, and from the Collection, we can then filter the content of that specific user based on the condition and then return the data.

But using RxJava we can directly filter out the data while returning the API response by using the filter operator, and we do all of this by doing the thread management.

These are a few use cases to understand RxJava for Android and why we need RxAndroid in our project.


Conclusion

In this note series, we understood about the RxJava and RxAndroid for Android. We discussed a few use cases to understand RxJava for Android and why we need RxAndroid in our project.

Thanks for reading ! I hope you enjoyed and learned about the RxJava and RxAndroid Concepts for 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 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 – Understanding Dex and Multidex

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, We will learn about Dex and Multidex in Android. Most of developers get 64K method limit exceeded error while building APK. This error comes due to DEX. So, in this article, we will try to gain more knowledge about Dex and Multidex in Android.

In Java, if we compile our code then that code is converted into a .class file by the compiler because our machine understands that .class file. The same is the process for the Android Application. In Android, the compiler converts our source code into DEX(Dalvik Executable) file.

So understanding the Dex and Multidex Concepts, We will cover the below topics:

  • Android Build System
  • Multidex in Android
  • Enabling Multidex support in Project
  • Limitations of Multidex support library

A famous quote about Learning is :

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


So Let’s Start.


Android Build System

Before moving on to DEX, let’s revise some concepts of the build system of Android. Android compiles the resources and source code of our app and packages them into APKs. Conversion of our project into an APK requires many tools and involves many processes. You can understand the build process with the help of the below diagram:

The build process of a typical Android App can be summarized as below:

  1. The compilers convert the source code into DEX (Dalvik Executable) files, which include the bytecode that runs on Android devices.
  2. After having the DEX files, the APK Packager combines the DEX files and compiled resources into a single APK.
  3. After that, the APK Packager signs the APK.
  4. Before generating the final APK, various methods are followed to optimize the code so as to avoid the unused code.


Multidex in Android

In Android, the compilers convert your source code into DEX files. This DEX file contains the compiled code used to run the app. But there is a limitation with the DEX file. The DEX file limits the total number of methods that can be referenced within a single DEX file to 64K i.e. 65,536 methods. So, you can’t use more than 64K methods in a particular DEX file. These 64K methods include Android framework methods, library methods, and methods in our code also. This limit of 64K is referred to as the “64K reference limit“.

So, if our app exceeds 65,536 methods, we will encounter a build error that indicates our app has reached the limit of the Android build architecture. The error is as follows:

Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

Older versions of the build system report a different error, which also indicates the same problem:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

Both the above error are known as 64K reference limit i.e we are trying to use more than 64K methods in our code. So, here comes the role of Multidex support in our Android Project. Next time if we want to use more than 64K methods in our project then we can use the Multidex to achieve this.

If we are getting some type of DEX error then before configuring our app for multidex to enable the use of 64K or more reference methods, we should try to reduce the total number of reference methods that are being used by our app. Try the following strategies to avoid using 64K reference methods:

  1. Manage dependencies: While importing some dependencies for our project, sometimes we import each and every library and use only a few of them. So, instead of keeping all those dependencies, we can keep only the required one.
  2. Remove unused code: Enable code shrinking in your project. By doing so, we are not shipping unused code with our APK.


Enabling Multidex support in Project

Here, we will see how to enable multidex for following:

  • API level lower than 21: That is if your minSdkVersion is set to lower than 21.
  • API level 21 and higher.

We follow only one of the following based on the minSdkVersion.

  • Multidex support for API level lower than 21
  • Multidex support for API level 21 and higher


Multidex support for API level lower than 21

For executing app code, versions of Android lower than Android 5.0 (API level 21) use the Dalvik runtime, and the limitation of using Dalvik is that you can’t use more than one classes.dex bytecode file per APK. To overcome this limitation, you will have to add the Multidex support library.

To add the multidex support library to our project for API level lower than 21, add the following dependency in our build.gradle file.

dependencies {
    implementation 'androidx:multidex:{latest-version}'
}

If we aren’t using AndroidX, add the following support library dependency instead:

dependencies {
  implementation 'com.android.support:multidex:{latest-version}'
}

By adding this library, our app can manage the access of additional DEX files. In other words, if we are having more than 64K methods, then we will be having more than one DEX file and these DEX files will be managed by using this multidex support library.

Then, modify the module-level build.gradle file to enable multidex using multiDexEnabled true.

android {
    defaultConfig {
 ...
        minSdkVersion 15 
        targetSdkVersion 28
        multiDexEnabled true
 }
 ...
}

dependencies {
 implementation 'androidx:multidex:{latest-version}'
}

Then, depending on whether we override the Application class, perform one of the following:

1. If we do not override the Application class, edit our manifest file to set android:name in the <application> tag as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">
    <application
            android:name="android.support.multidex.MultiDexApplication" >
        ...
    </application>
</manifest>

2. If you do override the Application class, change it to extend MultiDexApplication (if possible) as follows:

public class MyApplication extends MultiDexApplication { ... }

3. Or if we do override the Application class but it’s not possible to change the base class, then we can instead override the attachBaseContext() method and call MultiDex.install(this) to enable multidex:

public class MyApplication extends SomeOtherApplication {
  @Override
  protected void attachBaseContext(Context base) {
     super.attachBaseContext(base);
     MultiDex.install(this);
  }
}


Multidex support for API level 21 and higher

Our task becomes easier if we are making an app for Android version 5.0 (API level 21) or higher. API level 21 uses a runtime called ART which supports loading multiple DEX files from APK files. So, we do NOT need to add the support library for multidex in Android 5.0 or higher.

Now, for API level 21 or higher, we need to set multiDexEnabled to true in our module-level build.gradle file, as shown here:

android {
    defaultConfig {
        ...
        minSdkVersion 21 
        targetSdkVersion 28
        multiDexEnabled true
    }
    ...
}

Then, depending on whether we override the Application class, perform one of the following:

1. If we do not override the Application class, edit our manifest file to set android:name in the <application> tag as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">
    <application
            android:name="android.support.multidex.MultiDexApplication" >
        ...
    </application>
</manifest>

2. If we do override the Application class, change it to extend MultiDexApplication (if possible) as follows:

public class MyApplication extends MultiDexApplication { ... }

3. Or if we do override the Application class but it’s not possible to change the base class, then we can instead override the attachBaseContext() method and call MultiDex.install(this) to enable multidex:

public class MyApplication extends SomeOtherApplication {
  @Override
  protected void attachBaseContext(Context base) {
     super.attachBaseContext(base);
     MultiDex.install(this);
  }
}

So, now when we build our app, one primary DEX file will be constructed, and supporting DEX files will also be added to it. The primary DEX file is normally marked as classes.dex and other supporting DEX files as classes1.dex, classes2.dex, … and so on.


Limitations of Multidex support library

A good Android Developer know about the benefits of using some library but a professional developer also knows the limitations of using that library.

The multidex support library has some known limitations that we should be aware of before using it in our app. Some of the limitations are:

  • If our secondary DEX file is larger than the primary DEX file, then we may encounter some Application Not Responding (ANR) error. In this case, we should apply code shrinking to minimize the size of DEX files and remove unused portions of code.
  • If we are targeting API levels lower than 21, test thoroughly on those versions of the platform, because our app might have issues at startup or when particular groups of classes are loaded.

Code Shrinking can reduce or possibly eliminate these issues.

That’s all about in this article.


Conclusion

In this article, We understood about Dex and Multidex in Android. If we are working on an Android application that includes more than 64K methods , then to avoid the 64K reference limit, we can use Multidex in our application. So, implement Multidex in our project, but before doing so, make sure that our code is totally shrunk because code shrinking will help in reducing or possibly eliminating the Dex error.

Thanks for reading ! I hope you enjoyed and learned about Dex and Multidex concepts 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.

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 !!???

Exit mobile version