Android – 5 Quick Valuable Concepts To Learn Kotlin Programming Language

Hello Readers, CoolMonkTechie heartily welcomes you in this article (5 Quick Valuable Concepts To Learn Kotlin Programming Language).

In this article, We will learn about Valuable Concepts To Learn Kotlin Programming Language. At Google I/O 2019, Google announced that Android development will be increasingly Kotlin-first, and we’ve stood by that commitment. Kotlin is an expressive and concise programming language that reduces common code errors and easily integrates into existing apps. If we’re looking to build an Android app, we recommend starting with Kotlin to take advantage of its best-in-class features. This article shows the kotlin fundamental for android application development

For understanding the basic fundamental concepts of kotlin, we will explore the below topics one by one:

  • Variable declaration
  • Conditionals
  • Functions
  • Classes
  • Interoperability

A famous quote about learning is :

” Develop a passion for learning. If you do, you will never cease to grow.”

So Let’s begin.


1. Variable declaration

Kotlin uses two different keywords to declare variables: val and var.

  • Use val for a variable whose value never changes. We can’t reassign a value to a variable that was declared using val.
  • Use var for a variable whose value can change.

In the example below, count is a variable of type Int that is assigned an initial value of 10:

var count: Int = 10

Int is a type that represents an integer, one of the many numerical types that can be represented in Kotlin. Similar to other languages, we can also use ByteShortLongFloat, and Double depending on our numerical data.

The var keyword means that we can reassign values to count as needed. For example, we can change the value of count from 10 to 15:

var count: Int = 10
count = 15

Some values are not meant to be changed, though. Consider a String called languageName. If we want to ensure that languageName always holds a value of “Kotlin”, then we can declare languageName using the val keyword:

val languageName: String = "Kotlin"

These keywords allow us to be explicit about what can be changed. Use them to our advantage as needed. If a variable reference must be reassignable, then declare it as a var. Otherwise, use val.


Type inference

Continuing the previous example, when we assign an initial value to languageName, the Kotlin compiler can infer the type based off of the type of the assigned value.

Since the value of "Kotlin" is of type String, the compiler infers that languageName is also a String. Note that Kotlin is a statically-typed language. This means that the type is resolved at compile time and never changes.

In the following example, languageName is inferred as a String, so we can’t call any functions that aren’t part of the String class:

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

toUpperCase() is a function that can only be called on variables of type String. Because the Kotlin compiler has inferred languageName as a String, we can safely call toUpperCase()inc(), however, is an Int operator function, so it can’t be called on a String. Kotlin’s approach to type inference gives us both conciseness and type-safety.


Null safety

In some languages, a reference type variable can be declared without providing an initial explicit value. In these cases, the variables usually contain a null value. Kotlin variables can’t hold null values by default. This means that the following snippet is invalid:

// Fails to compile
val languageName: String = null

For a variable to hold a null value, it must be of a nullable type. We can specify a variable as being nullable by suffixing its type with ?, as shown in the following example:

val languageName: String? = null

With a String? type, we can assign either a String value or null to languageName.

We must handle nullable variables carefully or risk a dreaded NullPointerException. In Java, for example, if we attempt to invoke a method on a null value, our program crashes.

Kotlin provides a number of mechanisms for safely working with nullable variables.


2. Conditionals

Kotlin features several mechanisms for implementing conditional logic. The most common of these is an if-else statement. If an expression wrapped in parentheses next to an if keyword evaluates to true, then code within that branch (i.e. the immediately-following code that is wrapped in curly braces) is executed. Otherwise, the code within the else branch is executed.

Example : if / else

if (count == 42) {
    println("I have the answer.")
} else {
    println("The answer eludes me.")
}

We can represent multiple conditions using else if. This lets us represent more granular, complex logic within a single conditional statement, as shown in the following example:

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

Conditional Expressions

Conditional statements are useful for representing stateful logic, but we may find that we repeat ourself when writing them. In the example above, we simply print a String in each branch. To avoid this repetition, Kotlin offers conditional expressions. The last example can be rewritten as follows:

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

Implicitly, each conditional branch returns the result of the expression on its final line, so we don’t need to use a return keyword. Because the result of all three branches is of type String, the result of the if-else expression is also of type String. In this example, answerString is assigned an initial value from the result of the if-else expression. Type inference can be used to omit the explicit type declaration for answerString, but it’s often a good idea to include it for clarity.

We aware that Kotlin does not include a traditional ternary operator, instead favoring the use of conditional expressions.

As the complexity of our conditional statement grows, we might consider replacing our if-else expression with a when expression, as shown in the following example:

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

Each branch in a when expression is represented by a condition, an arrow (->), and a result. If the condition on the left-hand side of the arrow evaluates to true, then the result of the expression on the right-hand side is returned. Note that execution does not fall through from one branch to the next. The code in the when expression example is functionally-equivalent to that in the previous example but is arguably easier to read.

Smart Casting

Kotlin’s conditionals highlight one of its more powerful features, smart casting. Rather than using the safe-call operator or the not-null assertion operator to work with nullable values, we can instead check if a variable contains a reference to a null value using a conditional statement, as shown in the following example:

val languageName: String? = null
if (languageName != null) {
    // No need to write languageName?.toUpperCase()
    println(languageName.toUpperCase())
}

Within the conditional branch, languageName may be treated as non-nullable. Kotlin is smart enough to recognize that the condition for executing the branch is that languageName does not hold a null value, so we do not have to treat languageName as nullable within that branch. This smart casting works for null checkstype checks, or any condition that satisfies a contract.


3. Functions

We can group one or more expressions into a function. Rather than repeating the same series of expressions each time that we need a result, we can wrap the expressions in a function and call that function instead.

To declare a function, use the fun keyword followed by the function name. Next, define the types of inputs that our function takes, if any, and declare the type of output that it returns. A function’s body is where we define expressions that are called when our function is invoked.

Building on previous examples, here’s a complete Kotlin function:

fun generateAnswerString(): String {
    val answerString = if (count == 42) {
        "I have the answer."
    } else {
        "The answer eludes me"
    }

    return answerString
}

The function in the example above has the name generateAnswerString. It doesn’t take any input. It outputs a result of type String. To call a function, use its name, followed by the invocation operator (()). In the example below, the answerString variable is initialized with the result from generateAnswerString().

val answerString = generateAnswerString()

Functions can take arguments as input, as shown in the following example:

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

When declaring a function, we can specify any number of arguments and their types. In the example above, generateAnswerString() takes one argument named countThreshold of type Int. Within the function, we can refer to the argument by using its name.

When calling this function, we must include an argument within the function call’s parentheses:

val answerString = generateAnswerString(42)


Simplifying function declarations

generateAnswerString() is a fairly simple function. The function declares a variable and then immediately returns. When the result of a single expression is returned from a function, we can skip declaring a local variable by directly returning the result of the if-else expression contained in the function, as shown in the following example:

fun generateAnswerString(countThreshold: Int): String {
    return if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }
}

We can also replace the return keyword with the assignment operator:

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }


Anonymous functions

Not every function needs a name. Some functions are more directly identified by their inputs and outputs. These functions are called anonymous functions. We can keep a reference to an anonymous function, using this reference to call the anonymous function later. We can also pass the reference around our application, as with other reference types.

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

Like named functions, anonymous functions can contain any number of expressions. The returned value of the function is the result of the final expression.

In the example above, stringLengthFunc contains a reference to an anonymous function that takes a String as input and returns the length of the input String as output of type Int. For that reason, the function’s type is denoted as (String) -> Int. This code does not invoke the function, however. To retrieve the result of the function, we must invoke it with like we would a named function. We must supply a String when calling stringLengthFunc, as shown in the following example:

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

val stringLength: Int = stringLengthFunc("Android")


Higher-order functions

A function can take another function as an argument. Functions that use other functions as arguments are called Higher-order functions. This pattern is useful for communicating between components in the same way that we might use a callback interface in Java.

Here’s an example of a higher-order function:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

The stringMapper() function takes a String along with a function that derives an Int value from a String that we pass into it.

We can call stringMapper() by passing a String and a function that satisfies the other input parameter, namely a function that takes a String as input and outputs an Int, as shown in the following example:

stringMapper("Android", { input ->
    input.length
})

If the anonymous function is the last parameter defined on a function, we can pass it outside of the parentheses used to invoke the function, as shown in the following example:

stringMapper("Android") { input ->
    input.length
}

Anonymous functions can be found throughout the Kotlin standard library. 


4. Classes

All of the types mentioned so far are built into the Kotlin programming language. If we would like to add our own custom type, we can define a class using the class keyword, as shown in the following example:

class Car


Properties

Classes represent state using properties. A property is a class-level variable that can include a getter, a setter, and a backing field.

Since a car needs wheels to drive, we can add a list of Wheel objects as a property of Car, as shown in the following example:

class Car {
    val wheels = listOf<Wheel>()
}

Note that wheels is a public val, meaning that wheels can be accessed from outside of the Car class, and it can’t be reassigned. If we want to obtain an instance of Car, we must first call its constructor. From there, we can access any of its accessible properties.

val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

If we want to customize our wheels, we can define a custom constructor that specifies how our class properties are initialized:

class Car(val wheels: List<Wheel>)

In the example above, the class constructor takes a List<Wheel> as a constructor argument and uses that argument to initialize its wheels property.


Class functions and encapsulation

Classes use functions to model behavior. Functions can modify state, helping us to expose only the data that we wish to expose. This access control is part of a larger object-oriented concept known as encapsulation.

In the following example, the doorLock property is kept private from anything outside of the Car class. To unlock the car, we must call the unlockDoor() function passing in a valid key, as shown in the following example:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

If we would like to customize how a property is referenced, we can provide a custom getter and setter. For example, if we would like to expose a property’s getter while restricting access to its setter, we can designate that setter as private:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    var gallonsOfFuelInTank: Int = 15
        private set

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

With a combination of properties and functions, we can create classes that model all types of objects.


5. Interoperability

One of Kotlin’s most important features is its fluid interoperability with Java. Because Kotlin code compiles down to JVM bytecode, our Kotlin code can call directly into Java code and vice-versa. This means that we can leverage existing Java libraries directly from Kotlin. Furthermore, the majority of Android APIs are written in Java, and we can call them directly from Kotlin.

That’s all about in this article.


Conclusion

In this article, We understood about Valuable Concepts To Learn Kotlin Programming Language. Kotlin is a flexible, pragmatic language with growing support and momentum. We discussed about Kotlin fundamental like variable declaration, function, conditions and classes concepts which is widely used by Android developers everywhere for application development. This article showed the basic Kotlin fundamental concepts for android application development

Thanks for reading ! I hope you enjoyed and learned about the basic fundamental concepts of Kotlin for android development. 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 – Understanding App Architecture

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Understanding App Architecture in Android).

In this article, We will learn about App Architecture in Android. We will also discuss about best practices and recommended architecture for building robust, production-quality apps.

A famous quote about learning is :

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

So Let’s begin.


Mobile App User Experiences

In the majority of cases, desktop apps have a single entry point from a desktop or program launcher, then run as a single, monolithic process. Android apps, on the other hand, have a much more complex structure. A typical Android app contains multiple app components, including activities, fragments, services, content providers, and broadcast receivers.

We declare most of these app components in our app manifest. The Android OS then uses this file to decide how to integrate our app into the device’s overall user experience. Given that a properly-written Android app contains multiple components and that users often interact with multiple apps in a short period of time, apps need to adapt to different kinds of user-driven workflows and tasks.

For example, consider what happens when we share a photo in our favorite social networking app:

  1. The app triggers a camera intent. The Android OS then launches a camera app to handle the request. At this point, the user has left the social networking app, but their experience is still seamless.
  2. The camera app might trigger other intents, like launching the file chooser, which may launch yet another app.
  3. Eventually, the user returns to the social networking app and shares the photo.

At any point during the process, the user could be interrupted by a phone call or notification. After acting upon this interruption, the user expects to be able to return to, and resume, this photo-sharing process. This app-hopping behavior is common on mobile devices, so our app must handle these flows correctly.

Keep in mind that mobile devices are also resource-constrained, so at any time, the operating system might kill some app processes to make room for new ones.

Given the conditions of this environment, it’s possible for our app components to be launched individually and out-of-order, and the operating system or user can destroy them at any time. Because these events aren’t under our control, we shouldn’t store any app data or state in our app components, and our app components shouldn’t depend on each other.


Common Architectural Principles

If we shouldn’t use app components to store app data and state, how should we design our app?


Separation of concerns

The most important principle to follow is separation of concerns. It’s a common mistake to write all our code in an Activity or a Fragment. These UI-based classes should only contain logic that handles UI and operating system interactions. By keeping these classes as lean as possible, we can avoid many lifecycle-related problems.

Keep in mind that we don’t own implementations of Activity and Fragment; rather, these are just glue classes that represent the contract between the Android OS and our app. The OS can destroy them at any time based on user interactions or because of system conditions like low memory. To provide a satisfactory user experience and a more manageable app maintenance experience, it’s best to minimize our dependency on them.


Drive UI from a model

Another important principle is that you should drive your UI from a model, preferably a persistent model. Models are components that are responsible for handling the data for an app. They’re independent from the View objects and app components in our app, so they’re unaffected by the app’s lifecycle and the associated concerns.

Persistence is ideal for the following reasons:

  • Our users don’t lose data if the Android OS destroys our app to free up resources.
  • Our app continues to work in cases when a network connection is flaky or not available.

By basing our app on model classes with the well-defined responsibility of managing the data, our app is more testable and consistent.


Recommended App Architecture

In this section, we demonstrate how to structure an app using Architecture Components by working through an end-to-end use case.

Imagine we’re building a UI that shows a user profile. We use a private backend and a REST API to fetch the data for a given profile.


Overview

To start, consider the following diagram, which shows how all the modules should interact with one another after designing the app:

Notice that each component depends only on the component one level below it. For example, activities and fragments depend only on a view model. The repository is the only class that depends on multiple other classes; in this example, the repository depends on a persistent data model and a remote backend data source.

This design creates a consistent and pleasant user experience. Regardless of whether the user comes back to the app several minutes after they’ve last closed it or several days later, they instantly see a user’s information that the app persists locally. If this data is stale, the app’s repository module starts updating the data in the background.


Build The User Interface

The UI consists of a fragment, UserProfileFragment, and its corresponding layout file, user_profile_layout.xml.

To drive the UI, our data model needs to hold the following data elements:

  • User ID: The identifier for the user. It’s best to pass this information into the fragment using the fragment arguments. If the Android OS destroys our process, this information is preserved, so the ID is available the next time our app is restarted.
  • User object: A data class that holds details about the user.

We use a UserProfileViewModel, based on the ViewModel architecture component, to keep this information.

ViewModel object provides the data for a specific UI component, such as a fragment or activity, and contains data-handling business logic to communicate with the model. For example, the ViewModel can call other components to load the data, and it can forward user requests to modify the data. The ViewModel doesn’t know about UI components, so it isn’t affected by configuration changes, such as recreating an activity when rotating the device.

We’ve now defined the following files:

  • user_profile.xml: The UI layout definition for the screen.
  • UserProfileFragment: The UI controller that displays the data.
  • UserProfileViewModel: The class that prepares the data for viewing in the UserProfileFragment and reacts to user interactions.

The following code snippets show the starting contents for these files. (The layout file is omitted for simplicity.)

UserProfileViewModel

class UserProfileViewModel : ViewModel() {
   val userId : String = TODO()
   val user : User = TODO()
}

UserProfileFragment

class UserProfileFragment : Fragment() {
   // To use the viewModels() extension function, include
   // "androidx.fragment:fragment-ktx:latest-version" in your app
   // module's build.gradle file.
   private val viewModel: UserProfileViewModel by viewModels()

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

Now that we have these code modules, how do we connect them? After all, when the user field is set in the UserProfileViewModel class, we need a way to inform the UI.

To obtain the user, our ViewModel needs to access the Fragment arguments. We can either pass them from the Fragment, or better, using the SavedState module, we can make our ViewModel read the argument directly:

// UserProfileViewModel
class UserProfileViewModel(
   savedStateHandle: SavedStateHandle
) : ViewModel() {
   val userId : String = savedStateHandle["uid"] ?:
          throw IllegalArgumentException("missing user id")
   val user : User = TODO()
}

// UserProfileFragment
private val viewModel: UserProfileViewModel by viewModels(
   factoryProducer = { SavedStateVMFactory(this) }
   ...
)

Here, SavedStateHandle allows ViewModel to access the saved state and arguments of the associated Fragment or Activity.

Now we need to inform our Fragment when the user object is obtained. This is where the LiveData architecture component comes in.

LiveData is an observable data holder. Other components in our app can monitor changes to objects using this holder without creating explicit and rigid dependency paths between them. The LiveData component also respects the lifecycle state of our app’s components—such as activities, fragments, and services—and includes cleanup logic to prevent object leaking and excessive memory consumption.

If we’re already using a library like RxJava, we can continue using them instead of LiveData. When we use libraries and approaches like these, however, make sure we handle our app’s lifecycle properly. In particular, make sure to pause our data streams when the related LifecycleOwner is stopped and to destroy these streams when the related LifecycleOwner is destroyed. We can also add the android.arch.lifecycle:reactivestreams artifact to use LiveData with another reactive streams library, such as RxJava2.

To incorporate the LiveData component into our app, we change the field type in the UserProfileViewModel to LiveData<User>. Now, the UserProfileFragment is informed when the data is updated. Furthermore, because this LiveData field is lifecycle aware, it automatically cleans up references after they’re no longer needed.

UserProfileViewModel

class UserProfileViewModel(
   savedStateHandle: SavedStateHandle
) : ViewModel() {
   val userId : String = savedStateHandle["uid"] ?:
          throw IllegalArgumentException("missing user id")
   val user : LiveData<User> = TODO()
}

Now we modify UserProfileFragment to observe the data and update the UI:

UserProfileFragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
   super.onViewCreated(view, savedInstanceState)
   viewModel.user.observe(viewLifecycleOwner) {
       // update UI
   }
}

Every time the user profile data is updated, the onChanged() callback is invoked, and the UI is refreshed.

If we’re familiar with other libraries where observable callbacks are used, we might have realized that we didn’t override the fragment’s onStop() method to stop observing the data. This step isn’t necessary with LiveData because it’s lifecycle aware, which means it doesn’t invoke the onChanged() callback unless the fragment is in an active state; that is, it has received onStart() but hasn’t yet received onStop()). LiveData also automatically removes the observer when the fragment’s onDestroy() method is called.

We also didn’t add any logic to handle configuration changes, such as the user rotating the device’s screen. The UserProfileViewModel is automatically restored when the configuration changes, so as soon as the new fragment is created, it receives the same instance of ViewModel, and the callback is invoked immediately using the current data. Given that ViewModel objects are intended to outlast the corresponding View objects that they update, we shouldn’t include direct references to View objects within your implementation of ViewModel.


Fetch Data

Now that we’ve used LiveData to connect the UserProfileViewModel to the UserProfileFragment, how can we fetch the user profile data?

For this example, we assume that our backend provides a REST API. We use the Retrofit library to access our backend, though we are free to use a different library that serves the same purpose.

Here’s our definition of Webservice that communicates with our backend:

Webservice

interface Webservice {
   /**
    * @GET declares an HTTP GET request
    * @Path("user") annotation on the userId parameter marks it as a
    * replacement for the {user} placeholder in the @GET path
    */
   @GET("/users/{user}")
   fun getUser(@Path("user") userId: String): Call<User>
}

A first idea for implementing the ViewModel might involve directly calling the Webservice to fetch the data and assign this data to our LiveData object. This design works, but by using it, our app becomes more and more difficult to maintain as it grows. It gives too much responsibility to the UserProfileViewModel class, which violates the separation of concerns principle. Additionally, the scope of a ViewModel is tied to an Activity or Fragment lifecycle, which means that the data from the Webservice is lost when the associated UI object’s lifecycle ends. This behavior creates an undesirable user experience.

Instead, our ViewModel delegates the data-fetching process to a new module, a repository.

Repository modules handle data operations. They provide a clean API so that the rest of the app can retrieve this data easily. They know where to get the data from and what API calls to make when data is updated. We can consider repositories to be mediators between different data sources, such as persistent models, web services, and caches.

Our UserRepository class, shown in the following code snippet, uses an instance of WebService to fetch a user’s data:

UserRepository

class UserRepository {
   private val webservice: Webservice = TODO()
   // ...
   fun getUser(userId: String): LiveData<User> {
       // This isn't an optimal implementation. We'll fix it later.
       val data = MutableLiveData<User>()
       webservice.getUser(userId).enqueue(object : Callback<User> {
           override fun onResponse(call: Call<User>, response: Response<User>) {
               data.value = response.body()
           }
           // Error case is left out for brevity.
           override fun onFailure(call: Call<User>, t: Throwable) {
               TODO()
           }
       })
       return data
   }
}

Even though the repository module looks unnecessary, it serves an important purpose: it abstracts the data sources from the rest of the app. Now, our UserProfileViewModel doesn’t know how the data is fetched, so we can provide the view model with data obtained from several different data-fetching implementations.

Manage dependencies between components

The UserRepository class above needs an instance of Webservice to fetch the user’s data. It could simply create the instance, but to do that, it also needs to know the dependencies of the Webservice class. Additionally, UserRepository is probably not the only class that needs a Webservice. This situation requires us to duplicate code, as each class that needs a reference to Webservice needs to know how to construct it and its dependencies. If each class creates a new WebService, our app could become very resource heavy.

We can use the following design patterns to address this problem:

  • Dependency injection (DI): Dependency injection allows classes to define their dependencies without constructing them. At runtime, another class is responsible for providing these dependencies. We recommend the Dagger 2 library for implementing dependency injection in Android apps. Dagger 2 automatically constructs objects by walking the dependency tree, and it provides compile-time guarantees on dependencies.
  • Service locator: The service locator pattern provides a registry where classes can obtain their dependencies instead of constructing them.

It’s easier to implement a service registry than use DI, so if we aren’t familiar with DI, use the service locator pattern instead.

These patterns allow us to scale our code because they provide clear patterns for managing dependencies without duplicating code or adding complexity. Furthermore, these patterns allow us to quickly switch between test and production data-fetching implementations.


Connect ViewModel and the repository

Now, we modify our UserProfileViewModel to use the UserRepository object:

UserProfileViewModel

class UserProfileViewModel @Inject constructor(
   savedStateHandle: SavedStateHandle,
   userRepository: UserRepository
) : ViewModel() {
   val userId : String = savedStateHandle["uid"] ?:
          throw IllegalArgumentException("missing user id")
   val user : LiveData<User> = userRepository.getUser(userId)
}


Cache Data

The UserRepository implementation abstracts the call to the Webservice object, but because it relies on only one data source, it’s not very flexible.

The key problem with the UserRepository implementation is that after it fetches data from our backend, it doesn’t store that data anywhere. Therefore, if the user leaves the UserProfileFragment, then returns to it, our app must re-fetch the data, even if it hasn’t changed.

This design is suboptimal for the following reasons:

  • It wastes valuable network bandwidth.
  • It forces the user to wait for the new query to complete.

To address these shortcomings, we add a new data source to our UserRepository, which caches the User objects in memory:

UserRepository

// Informs Dagger that this class should be constructed only once.
@Singleton
class UserRepository @Inject constructor(
   private val webservice: Webservice,
   // Simple in-memory cache. Details omitted for brevity.
   private val userCache: UserCache
) {
   fun getUser(userId: String): LiveData<User> {
       val cached : LiveData<User> = userCache.get(userId)
       if (cached != null) {
           return cached
       }
       val data = MutableLiveData<User>()
       // The LiveData object is currently empty, but it's okay to add it to the
       // cache here because it will pick up the correct data once the query
       // completes.
       userCache.put(userId, data)
       // This implementation is still suboptimal but better than before.
       // A complete implementation also handles error cases.
       webservice.getUser(userId).enqueue(object : Callback<User> {
           override fun onResponse(call: Call<User>, response: Response<User>) {
               data.value = response.body()
           }

           // Error case is left out for brevity.
           override fun onFailure(call: Call<User>, t: Throwable) {
               TODO()
           }
       })
       return data
   }
}


Persist Data

Using our current implementation, if the user rotates the device or leaves and immediately returns to the app, the existing UI becomes visible instantly because the repository retrieves data from our in-memory cache.

However, what happens if the user leaves the app and comes back hours later, after the Android OS has killed the process? By relying on our current implementation in this situation, we need to fetch the data again from the network. This refetching process isn’t just a bad user experience; it’s also wasteful because it consumes valuable mobile data.

We could fix this issue by caching the web requests, but that creates a key new problem: What happens if the same user data shows up from another type of request, such as fetching a list of friends? The app would show inconsistent data, which is confusing at best. For example, our app might show two different versions of the same user’s data if the user made the list-of-friends request and the single-user request at different times. Our app would need to figure out how to merge this inconsistent data.

The proper way to handle this situation is to use a persistent model. This is where the Room persistence library comes to the rescue.

Room is an object-mapping library that provides local data persistence with minimal boilerplate code. At compile time, it validates each query against your data schema, so broken SQL queries result in compile-time errors instead of runtime failures. Room abstracts away some of the underlying implementation details of working with raw SQL tables and queries. It also allows you to observe changes to the database’s data, including collections and join queries, exposing such changes using LiveData objects. It even explicitly defines execution constraints that address common threading issues, such as accessing storage on the main thread.

To use Room, we need to define our local schema. First, we add the @Entity annotation to our User data model class and a @PrimaryKey annotation to the class’s id field. These annotations mark User as a table in our database and id as the table’s primary key:

User

@Entity
data class User(
   @PrimaryKey private val id: String,
   private val name: String,
   private val lastName: String
)

Then, we create a database class by implementing RoomDatabase for our app:

UserDatabase

@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase()

Notice that UserDatabase is abstract. Room automatically provides an implementation of it.

We now need a way to insert user data into the database. For this task, we create a data access object (DAO).

UserDao

@Dao
interface UserDao {
   @Insert(onConflict = REPLACE)
   fun save(user: User)

   @Query("SELECT * FROM user WHERE id = :userId")
   fun load(userId: String): LiveData<User>
}

Notice that the load method returns an object of type LiveData<User>. Room knows when the database is modified and automatically notifies all active observers when the data changes. Because Room uses LiveData, this operation is efficient; it updates the data only when there is at least one active observer.

Room checks invalidations based on table modifications, which means it may dispatch false positive notifications.

With our UserDao class defined, we then reference the DAO from our database class:

UserDatabase

@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase() {
   abstract fun userDao(): UserDao
}

Now we can modify our UserRepository to incorporate the Room data source:

// Informs Dagger that this class should be constructed only once.
@Singleton
class UserRepository @Inject constructor(
   private val webservice: Webservice,
   // Simple in-memory cache. Details omitted for brevity.
   private val executor: Executor,
   private val userDao: UserDao
) {
   fun getUser(userId: String): LiveData<User> {
       refreshUser(userId)
       // Returns a LiveData object directly from the database.
       return userDao.load(userId)
   }

   private fun refreshUser(userId: String) {
       // Runs in a background thread.
       executor.execute {
           // Check if user data was fetched recently.
           val userExists = userDao.hasUser(FRESH_TIMEOUT)
           if (!userExists) {
               // Refreshes the data.
               val response = webservice.getUser(userId).execute()

               // Check for errors here.

               // Updates the database. The LiveData object automatically
               // refreshes, so we don't need to do anything else here.
               userDao.save(response.body()!!)
           }
       }
   }

   companion object {
       val FRESH_TIMEOUT = TimeUnit.DAYS.toMillis(1)
   }
}

Notice that even though we changed where the data comes from in UserRepository, we didn’t need to change our UserProfileViewModel or UserProfileFragment. This small-scoped update demonstrates the flexibility that our app’s architecture provides. It’s also great for testing, because we can provide a fake UserRepository and test our production UserProfileViewModel at the same time.

If users wait a few days before returning to an app that uses this architecture, it’s likely that they’ll see out-of-date information until the repository can fetch updated information. Depending on our use case, we may not want to show this out-of-date information. Instead, we can display placeholder data, which shows example values and indicates that our app is currently fetching and loading up-to-date information.

Single source of truth

It’s common for different REST API endpoints to return the same data. For example, if our backend has another endpoint that returns a list of friends, the same user object could come from two different API endpoints, maybe even using different levels of granularity. If the UserRepository were to return the response from the Webservice request as-is, without checking for consistency, our UIs could show confusing information because the version and format of data from the repository would depend on the endpoint most recently called.

For this reason, our UserRepository implementation saves web service responses into the database. Changes to the database then trigger callbacks on active LiveData objects. Using this model, the database serves as the single source of truth, and other parts of the app access it using our UserRepository. Regardless of whether we use a disk cache, we recommend that our repository designate a data source as the single source of truth for the rest of your app.


Show in-progress operations

In some use cases, such as pull-to-refresh, it’s important for the UI to show the user that there’s currently a network operation in progress. It’s good practice to separate the UI action from the actual data because the data might be updated for various reasons. For example, if we fetched a list of friends, the same user might be fetched again programmatically, triggering a LiveData<User> update. From the UI’s perspective, the fact that there’s a request in flight is just another data point, similar to any other piece of data in the User object itself.

We can use one of the following strategies to display a consistent data-updating status in the UI, regardless of where the request to update the data came from:

  • Change getUser() to return an object of type LiveData. This object would include the status of the network operation.
    For an example, see the NetworkBoundResource implementation in the android-architecture-components GitHub project.
  • Provide another public function in the UserRepository class that can return the refresh status of the User. This option is better if you want to show the network status in your UI only when the data-fetching process originated from an explicit user action, such as pull-to-refresh.


Test Each Component

In the separation of concerns section, we mentioned that one key benefit of following this principle is testability.

The following list shows how to test each code module from our extended example:

  • User interface and interactions: Use an Android UI instrumentation test. The best way to create this test is to use the Espresso library. We can create the fragment and provide it a mock UserProfileViewModel. Because the fragment communicates only with the UserProfileViewModel, mocking this one class is sufficient to fully test your app’s UI.
  • ViewModel: We can test the UserProfileViewModel class using a JUnit test. We only need to mock one class, UserRepository.
  • UserRepository: We can test the UserRepository using a JUnit test, as well. We need to mock the Webserviceand the UserDao. In these tests, verify the following behavior:
    • The repository makes the correct web service calls.
    • It saves results into the database.
    • The repository doesn’t make unnecessary requests if the data is cached and up to date.
  • Because both Webservice and UserDao are interfaces, we can mock them or create fake implementations for more complex test cases.
  • UserDao: Test DAO classes using instrumentation tests. Because these instrumentation tests don’t require any UI components, they run quickly. For each test, create an in-memory database to ensure that the test doesn’t have any side effects, such as changing the database files on disk.
  • Webservice: In these tests, avoid making network calls to our backend. It’s important for all tests, especially web-based ones, to be independent from the outside world. Several libraries, including MockWebServer, can help we create a fake local server for these tests.
  • Testing Artifacts: Architecture Components provides a maven artifact to control its background threads. The androidx.arch.core:core-testing artifact contains the following JUnit rules:
    • InstantTaskExecutorRule: Use this rule to instantly execute any background operation on the calling thread.
    • CountingTaskExecutorRule: Use this rule to wait on background operations of Architecture Components. You can also associate this rule with Espresso as an idling resource.


Best Practices

Programming is a creative field, and building Android apps isn’t an exception. There are many ways to solve a problem, be it communicating data between multiple activities or fragments, retrieving remote data and persisting it locally for offline mode, or any number of other common scenarios that nontrivial apps encounter.

Although the following recommendations aren’t mandatory, it has been our experience that following them makes your code base more robust, testable, and maintainable in the long run:


1. Avoid designating our app’s entry points—such as activities, services, and broadcast receivers—as sources of data.

Instead, they should only coordinate with other components to retrieve the subset of data that is relevant to that entry point. Each app component is rather short-lived, depending on the user’s interaction with their device and the overall current health of the system.


2. Create well-defined boundaries of responsibility between various modules of our app.

For example, don’t spread the code that loads data from the network across multiple classes or packages in your code base. Similarly, don’t define multiple unrelated responsibilities—such as data caching and data binding—into the same class.


3. Expose as little as possible from each module.

Don’t be tempted to create “just that one” shortcut that exposes an internal implementation detail from one module. We might gain a bit of time in the short term, but we then incur technical debt many times over as our codebase evolves.


4. Consider how to make each module testable in isolation.

For example, having a well-defined API for fetching data from the network makes it easier to test the module that persists that data in a local database. If, instead, we mix the logic from these two modules in one place, or distribute our networking code across our entire code base, it becomes much more difficult—if not impossible—to test.


5. Focus on the unique core of our app so it stands out from other apps.

Don’t reinvent the wheel by writing the same boilerplate code again and again. Instead, focus our time and energy on what makes our app unique, and let the Android Architecture Components and other recommended libraries handle the repetitive boilerplate.


6. Persist as much relevant and fresh data as possible.

That way, users can enjoy our app’s functionality even when their device is in offline mode. Remember that not all of our users enjoy constant, high-speed connectivity.


7. Assign one data source to be the single source of truth.

Whenever our app needs to access this piece of data, it should always originate from this single source of truth.


Exposing Network Status

In this section, we demonstrates how to expose network status using a Resource class that encapsulate both the data and its state.

The following code snippet provides a sample implementation of Resource:

Resource

// A generic class that contains data and status about loading this data.
sealed class Resource<T>(
   val data: T? = null,
   val message: String? = null
) {
   class Success<T>(data: T) : Resource<T>(data)
   class Loading<T>(data: T? = null) : Resource<T>(data)
   class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
}

Because it’s common to load data from the network while showing the disk copy of that data, it’s good to create a helper class that we can reuse in multiple places. For this example, we create a class called NetworkBoundResource.

The following diagram shows the decision tree for NetworkBoundResource:

It starts by observing the database for the resource. When the entry is loaded from the database for the first time, NetworkBoundResource checks whether the result is good enough to be dispatched or that it should be re-fetched from the network. Note that both of these situations can happen at the same time, given that we probably want to show cached data while updating it from the network.

If the network call completes successfully, it saves the response into the database and re-initializes the stream. If network request fails, the NetworkBoundResource dispatches a failure directly.

The following code snippet shows the public API provided by NetworkBoundResource class for its subclasses:

NetworkBoundResource.kt

// ResultType: Type for the Resource data.
// RequestType: Type for the API response.
abstract class NetworkBoundResource<ResultType, RequestType> {
   // Called to save the result of the API response into the database
   @WorkerThread
   protected abstract fun saveCallResult(item: RequestType)

   // Called with the data in the database to decide whether to fetch
   // potentially updated data from the network.
   @MainThread
   protected abstract fun shouldFetch(data: ResultType?): Boolean

   // Called to get the cached data from the database.
   @MainThread
   protected abstract fun loadFromDb(): LiveData<ResultType>

   // Called to create the API call.
   @MainThread
   protected abstract fun createCall(): LiveData<ApiResponse<RequestType>>

   // Called when the fetch fails. The child class may want to reset components
   // like rate limiter.
   protected open fun onFetchFailed() {}

   // Returns a LiveData object that represents the resource that's implemented
   // in the base class.
   fun asLiveData(): LiveData<ResultType> = TODO()
}

Note these important details about the class’s definition:

  • It defines two type parameters, ResultType and RequestType, because the data type returned from the API might not match the data type used locally.
  • It uses a class called ApiResponse for network requests. ApiResponse is a simple wrapper around the Retrofit2.Call class that convert responses to instances of LiveData.

After creating the NetworkBoundResource, we can use it to write our disk- and network-bound implementations of User in the UserRepository class:

UserRepository

// Informs Dagger that this class should be constructed only once.
@Singleton
class UserRepository @Inject constructor(
   private val webservice: Webservice,
   private val userDao: UserDao
) {
   fun getUser(userId: String): LiveData<User> {
       return object : NetworkBoundResource<User, User>() {
           override fun saveCallResult(item: User) {
               userDao.save(item)
           }

           override fun shouldFetch(data: User?): Boolean {
               return rateLimiter.canFetch(userId) && (data == null || !isFresh(data))
           }

           override fun loadFromDb(): LiveData<User> {
               return userDao.load(userId)
           }

           override fun createCall(): LiveData<ApiResponse<User>> {
               return webservice.getUser(userId)
           }
       }.asLiveData()
   }
}

That’s all about in this article.


Conclusion

In this article, we learned about best practices and recommended architecture for building robust, production-quality apps in Android.

Thanks for reading ! I hope you enjoyed and learned about App Architecture 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 – 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 !!???

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

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