Hello Readers, CoolMonkTechie heartily welcomes you in this article ( How To Apply Run Time Android Permission In React Native ).
In this article, we will learn how to apply Run Time Android Permission In React Native. Android Permission feature helps a user to know which application is trying to access sensitive data such as files, contacts and SMS, and system-specific features such as camera and internet. This article shows to apply the run time permission with the authentic example.
To understand the Run Time Android Permission In React Native, we cover the below topics as below :
What is Android Permission?
Why we need permission?
How to apply Permission in Android?
How to apply run time Permission in React Native?
Example to apply the run time permission.
A famous quote about learning is :
” The great aim of education is not knowledge, but action. “
So Let’s begin.
What is Android Permission ?
According to Google’s privacy policy, a user should know which application is trying to access sensitive data such as files, contacts and SMS, and system-specific features such as camera and internet. So they introduced the Android Permission feature.
In Android permission modal, every application which is using sensitive data or some certain system features has to ask the permissions from the user.
The purpose of permission is to protect the privacy of an Android user.
Why We need Permission ?
Android applies the permission modal because some applications were tracking the user’s location in the background and accessing the private data like contacts, call history and messages without informing the user which is the violation of googles privacy policy.
So solving this sensitive issue application has to ask for permission to access those sensitive data.
How to Apply Permission in Android ?
Now we get an idea about the Android permission, let’s see how to apply the permission in Android.
To apply permission in Android, we have to follow two steps:
We have to add those permissions requests in project -> app -> src -> AndroidManifest.xml file.
For Example, if we want to ask for the device location Permission, we have to add the following permission request in AndroidManifest.xml :
After adding the permission in AndroidManifest file, this permission will be automatically asked by the play store when they install the app and this permission will be granted to the applications.
On devices before SDK version 23, the permissions are automatically granted if we add those permissions in our AndroidManifest.xml file, but after SDK version 23 we have to ask runtime permission as well.
2. After Google’s new permission modal, they have introduced the run time permission mechanism in Android version 23. According to that, we have to include all the permissions in AndroidManifest.xml and also have to apply some dangerouspermissions in run time.
According to the official docs, dangerouspermissions require a dialog prompt.
So whenever we are working with this dangerouspermissions, we need to check whether the permission is granted by the user, and that can be done with the help of PermissionsAndroid in React Native.
For example, If we need INTERNET permission and CAMERA permission then we have to add both the permission in AndroidManifest.xml file but have to ask only for the CAMERA permission in run time because CAMERA permission comes under the dangerous permission not INTERNET permission.
Run Time Permissions that requires Confirmation from the User. Here is the list of dangerous permissions.
READ_CALENDAR
android.permission.READ_CALENDAR
WRITE_CALENDAR
android.permission.WRITE_CALENDAR
CAMERA
android.permission.CAMERA
READ_CONTACTS
android.permission.READ_CONTACTS
WRITE_CONTACTS
android.permission.WRITE_CONTACTS
GET_ACCOUNTS
android.permission.GET_ACCOUNTS
ACCESS_FINE_LOCATION
android.permission.ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
RECORD_AUDIO
android.permission.RECORD_AUDIO
READ_PHONE_STATE
android.permission.READ_PHONE_STATE
CALL_PHONE
android.permission.CALL_PHONE
READ_CALL_LOG
android.permission.READ_CALL_LOG
WRITE_CALL_LOG
android.permission.WRITE_CALL_LOG
ADD_VOICEMAIL
com.android.voicemail
USE_SIP
android.permission.USE_SIP
PROCESS_OUTGOING_CALLS
android.permission.PROCESS_OUTGOING_CALLS
BODY_SENSORS
android.permission.BODY_SENSORS
SEND_SMS
android.permission.SEND_SMS
RECEIVE_SMS
android.permission.RECEIVE_SMS
READ_SMS
android.permission.READ_SMS
RECEIVE_WAP_PUSH
android.permission.RECEIVE_WAP_PUSH
RECEIVE_MMS
android.permission.RECEIVE_MMS
READ_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
Run Time Permissions
How to apply Run Time Android Permission using React Native PermissionsAndroid ?
In React Native, PermissionsAndroid component provides access to Android M’s (Over API level 23) new permissions model. We always need to check the permission before using native APIs which comes under dangerous permissions.
Above mentioned all the dangerous permissions comes under PermissionsAndroid.PERMISSIONS as constants.
We can check the permission granted or not using PermissionsAndroid.RESULTS.GRANTED.
Permission denied by the user with never ask again.
Result Strings for Requesting Permissions
Now we get an idea about Android permissions and components. Let’s move towards the Example which can help us apply permission in our application.
Example to Apply the Run Time Permission
In this example, we will apply the device location permission, which needs run time permission. We are making a button on the centre of the screen and on a click of button we will apply the run time permission for device location known as ACCESS_FINE_LOCATION. So let’s get started.
Example Project Setup
To demonstration of apply the Run Time Permission, we have to follow the below steps:
Create a new React Native project
Adding device location permission in AndroidManifest.xml
1. Create a new React Native project
Assuming that we have node installed, we can use npm to install the react-native-clicommand line utility. Open the terminal and go to the workspace and run the following commands to create a new React Native project.
npx react-native init ProjectName
This will make a project structure with an index file named App.js in our project directory.
2. Adding device location permission in AndroidManifest.xml
If we are using any features in our app that needs permission, we need to add it in AndroidManifest.xml. For this example, we are adding device location permission in AndroidManifest.xml.
Here PermissionsAndroid.request has two arguments:
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION to generate a dialog to ask for Device Location Permission.
A JSON {‘title’:”,’message’:”}, which will help us to communicate or to show the dialog about the permission if the user denied the permission. They will generate it like this.
To Run the React Native Example Code
Open the terminal again, and jump into our project using
cd ProjectName
To run the project on an Android Virtual Device or on real debugging device
npx react-native run-android
or on the iOS Simulator by running (macOS only)
npx react-native run-ios (macOS only).
The output of example code is as below:
That’s all about in this article.
Conclusion
In this article, we understood how to apply Run Time Android Permission in React Native. This article showed the example code to apply the RunTime Android Permission in React Native.
Thanks for reading! I hope you enjoyed and learned about RunTime Android Permission Concepts in React Native. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find Other articles of CoolmonkTechie as below link :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Select Scope Functions In Kotlin?).
In this article, we will learn about how to select Scope Functions in Kotlin. The Kotlin standard library contains several functions whose sole purpose is to execute a block of code within the context of an object. When we call such a function on an object with a lambda expression provided, it forms a temporary scope. In this scope, we can access the object without its name. Such functions are called Scope Functions. This article provides the detailed descriptions of the differences between scope functions and the conventions on their usage in Kotlin with some authentic examples.
A famous quote about learning is :
” Tell me and I forget, teach me and I may remember, involve me and I learn.”
So Let’s begin.
Scope Functions in Kotlin
The definition of Scope function is
” Scoped functions are functions that execute a block of code within the context of an object.“
These functions provide a way to give temporary scope to the object under consideration where specific operations can be applied to the object within the block of code, thereby, resulting in a clean and concise code. There are five scoped functions in Kotlin: let, run, with, also and apply.
Basically, these functions do the same: execute a block of code on an object. What’s different is how this object becomes available inside the block and what is the result of the whole expression.
If we write the same without let, we’ll have to introduce a new variable and repeat its name whenever we use it.
val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)
The scope functions do not introduce any new technical capabilities, but they can make our code more concise and readable.
Due to the similar nature of scope functions, choosing the right one for our case can be a bit tricky. The choice mainly depends on our intent and the consistency of use in our project.
Common Difference between Scope Functions
Because the scope functions are all quite similar in nature, it’s important to understand the differences between them. There are two main differences between each scope function:
The way to refer to the context object
The return value.
Context object – this or it
Inside the lambda of a scope function, the context object is available by a short reference instead of its actual name. Each scope function uses one of two ways to access the context object: as a lambda receiver (this) or as a lambda argument (it). Both provide the same capabilities, so we’ll describe the pros and cons of each for different cases and provide recommendations on their use.
fun main() {
val str = "Hello"
// this
str.run {
println("The receiver string length: $length")
//println("The receiver string length: ${this.length}") // does the same
}
// it str.let { println("The receiver string's length is ${it.length}") }
}
this
run, with, and apply refer to the context object as a lambda receiver – by keyword this. Hence, in their lambdas, the object is available as it would be in ordinary class functions. In most cases, we can omit this when accessing the members of the receiver object, making the code shorter. On the other hand, if this is omitted, it can be hard to distinguish between the receiver members and external objects or functions. So, having the context object as a receiver (this) is recommended for lambdas that mainly operate on the object members: call its functions or assign properties.
val adam = Person("Adam").apply {
age = 20 // same as this.age = 20 or adam.age = 20
city = "London"
}
println(adam)
it
In turn, let and also have the context object as a lambda argument. If the argument name is not specified, the object is accessed by the implicit default name it. it is shorter than this and expressions with it are usually easier for reading. However, when calling the object functions or properties we don’t have the object available implicitly like this. Hence, having the context object as it is better when the object is mostly used as an argument in function calls. it is also better if we use multiple variables in the code block.
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
Additionally, when we pass the context object as an argument, we can provide a custom name for the context object inside the scope.
fun getRandomInt(): Int {
return Random.nextInt(100).also { value ->
writeToLog("getRandomInt() generated value $value")
}
}
val i = getRandomInt()
Return value
The scope functions differ by the result they return:
apply and also return the context object.
let, run, and with return the lambda result.
These two options let we choose the proper function depending on what we do next in our code.
Context object
The return value of apply and also is the context object itself. Hence, they can be included into call chains as side steps: we can continue chaining function calls on the same object after them.
val numberList = mutableListOf()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()
They also can be used in return statements of functions returning the context object.
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
Lambda result
let, run, and with return the lambda result. So, we can use them when assigning the result to a variable, chaining operations on the result, and so on.
val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run {
add("four")
add("five")
count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")
Additionally, we can ignore the return value and use a scope function to create a temporary scope for variables.
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
val firstItem = first()
val lastItem = last()
println("First item: $firstItem, last item: $lastItem")
}
We can analyze the common scope functions difference summary as below diagram:
Five Scope Functions In Kotlin
To help we choose the right scope function for our case, we’ll describe them in detail and provide usage recommendations. Technically, functions are interchangeable in many cases, so the examples show the conventions that define the common usage style.
There are five scoped functions in Kotlin: let, run, with, also and apply.
Let’s go through them one by one.
Scope Function – let
The context object is available as an argument (it). The return value is the lambda result.
let can be used to invoke one or more functions on results of call chains. For example, the following code prints the results of two operations on a collection:
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
With let, we can rewrite it:
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
// and more function calls if needed
}
If the code block contains a single function with it as an argument, we can use the method reference (::) instead of the lambda:
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let(::println)
letis often used for executing a code block only with non-null values. To perform actions on a non-null object, use the safe call operator ?. on it and call let with the actions in its lambda.
val str: String? = "Hello"
//processNonNullString(str) // compilation error: str can be null
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // OK: 'it' is not null inside '?.let { }'
it.length
}
Another case for using let is introducing local variables with a limited scope for improving code readability. To define a new variable for the context object, provide its name as the lambda argument so that it can be used instead of the default it.
val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let { firstItem ->
println("The first item of the list is '$firstItem'")
if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.toUpperCase()
println("First item after modifications: '$modifiedFirstItem'")
Scope Function – with
A non-extension function: the context object is passed as an argument, but inside the lambda, it’s available as a receiver (this). The return value is the lambda result.
We recommend with for calling functions on the context object without providing the lambda result. In the code, with can be read as “with this object, do the following.”
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
Another use case for with is introducing a helper object whose properties or functions will be used for calculating a value.
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
"The first element is ${first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
It is convenient when we have to call multiple different methods on the same object. Instead of repeating the variable containing this object on each line, we can use with. Thisfunction is used to change instance properties without the need to call dot operator over the reference every time.
Scope Function – run
The context object is available as a receiver (this). The return value is the lambda result.
run does the same as with but invokes as let – as an extension function of the context object.
run is useful when our lambda contains both the object initialization and the computation of the return value.
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// the same code written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
Besides calling run on a receiver object, we can use it as a non-extension function. Non-extension run lets us execute a block of several statements where an expression is required.
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) {
println(match.value)
}
run is actually a combination of with() and let().
Scope Function – apply
The context object is available as a receiver (this). The return value is the object itself.
Use apply for code blocks that don’t return a value and mainly operate on the members of the receiver object. The common case for apply is the object configuration. Such calls can be read as “apply the following assignments to the object.”
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
Having the receiver as the return value, we can easily include apply into call chains for more complex processing.
Scope Function – also
The context object is available as an argument (it). The return value is the object itself.
also is good for performing some actions that take the context object as an argument. Use also for actions that need a reference rather to the object than to its properties and functions, or when we don’t want to shadow this reference from an outer scope.
When we see also in the code, we can read it as “and also do the following with the object.”
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
Scope Functions – run vs let
run is similar to letin terms of accepting any return value , but this is different in the context of the object terms. run function refers to the context of the object as “this” and not “it”. That is the reason we did not use “${this.name}” as it would be redundant here since the block of code understands that “name” is used here concerning the Person object.
Another point here is that since the context is referred to as “this”, it cannot be renamed to a readable lambda parameter. So depending on the use case and requirement , we have to choose between the let and the run operator. The “run” operator also helps in easy null checks similar to the “let” operator.
var name: String? = "Abcd"
private fun performRunOperation() {
val name = Person().name?.run {
"The name of the Person is: $this"
}
print(name)
}
Scope Functions – with vs run
Let’s consider a case where a Person object can be nullable.
we can see that the context of the object referred to as “this” is a nullable type of Person. And hence, to correct this, we need to change the code as:
So performing a null check using a “with” operator is difficult and this is where we can replace it with “run” as follows:
private fun performRunOperation() {
val person: Person? = null
person?.run {
name = "asdf"
contactNumber = "1234"
address = "wasd"
displayInfo()
}
}
This looks a lot cleaner.
Scope Functions – run vs apply
So let’s see the difference between run and apply functions.
We can see that run accepts a return statement whereas apply does not accept a return statement(we can see the error thrown by the IDE in the image) and always returns the same object which it is referring to.
Scope Functions – let vs also
So let’s see the difference between let and also functions.
We can see that let accepts a return statement whereas “also” does not accept a return statement(we can see the error thrown by the IDE in the image) and always returns the same object which it is referring to.
Standard Library Scope Functions – takeIf and takeUnless
In addition to scope functions, the standard library contains the functions takeIf and takeUnless. These functions let us embed checks of the object state in call chains.
When we called on an object with a predicate provided, takeIf returns this object if it matches the predicate. Otherwise, it returns null. So, takeIf is a filtering function for a single object. In turn, takeUnless returns the object if it doesn’t match the predicate and null if it does. The object is available as a lambda argument (it).
val number = Random.nextInt(100)
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("even: $evenOrNull, odd: $oddOrNull")
When we do chaining other functions after takeIf and takeUnless, we don’t forget to perform the null check or the safe call (?.) because their return value is nullable.
takeIf and takeUnless are especially useful together with scope functions. A good case is chaining them with let for running a code block on objects that match the given predicate. To do this, call takeIf on the object and then call let with a safe call (?). For objects that don’t match the predicate, takeIf returns null and let isn’t invoked.
fun displaySubstringPosition(input: String, sub: String) {
input.indexOf(sub).takeIf { it >= 0 }?.let {
println("The substring $sub is found in $input.")
println("Its start position is $it.")
}
}
displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")
This is how the same function looks without the standard library functions:
fun displaySubstringPosition(input: String, sub: String) {
val index = input.indexOf(sub)
if (index >= 0) {
println("The substring $sub is found in $input.")
println("Its start position is $index.")
}
}
displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")
The Selection Of Scope Functions
To help we choose the right scope function for our purpose, we provide the table of key differences between them.
Function
Object reference
Return value
Is extension function
let
it
Lambda result
Yes
run
this
Lambda result
Yes
run
–
Lambda result
No: called without the context object
with
this
Lambda result
No: takes the context object as an argument.
apply
this
Context object
Yes
also
it
Context object
Yes
Key differences between Scope Function
Here is a short guidelines for choosing scope functions depending on the intended purpose:
Executing a lambda on non-null objects: let
Introducing an expression as a variable in local scope: let
Object configuration: apply
Object configuration and computing the result: run
Running statements where an expression is required: non-extension run
Additional effects: also
Grouping function calls on an object: with
The use cases of different scope functions overlap, so that we can choose the functions based on the specific conventions used in our project or team.
In this article, we understood about how to select Scope Functions in Kotlin. Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease our code readability and lead to errors. Avoid nesting scope functions and be careful when chaining them: it’s easy to get confused about the current context object and the value of this or it. This article showed the detailed descriptions of the differences between scope functions and the conventions on their usage in Kotlin with some authentic examples.
Thanks for reading! I hope you enjoyed and learned about Scope Functions concepts in Kotlin. 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 :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Apply Manual Dependency Injection In A Real Android Application ?).
In this article, we will learn about how to apply manual dependency injection in a real android application. Dependency Injection is a good technique for creating scalable and testable Android applications. When our application gets larger, we will start seeing that we write a lot of boilerplate code (such as factories), which can be error-prone. We also have to manage the scope and lifecycle of the containers ourself, optimizing and discarding containers that are no longer needed in order to free up memory. Doing this incorrectly can lead to subtle bugs and memory leaks in our app. This article reviews an iterated approach of how we might start using manual dependency injection in our application.
A famous quote about learning is :
” Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young. “
So Let’s begin.
Manual Dependency Injection
Android’s recommended app architecture encourages dividing our code into classes to benefit from separation of concerns, a principle where each class of the hierarchy has a single defined responsibility. This leads to more, smaller classes that need to be connected together to fulfill each other’s dependencies.
The dependencies between classes can be represented as a graph, in which each class is connected to the classes it depends on. The representation of all our classes and their dependencies makes up the application graph. In figure 1, we can see an abstraction of the application graph. When class A (ViewModel) depends on class B (Repository), there’s a line that points from A to B representing that dependency.
Dependency injection helps make these connections and enables us to swap out implementations for testing. For example, when testing a ViewModel that depends on a repository, we can pass different implementations of Repository with either fakes or mocks to test the different cases.
The approach improves until it reaches a point that is very similar to what Dagger would automatically generate for us.
Example – Login Flow For A Typical Android Application
Consider a flow to be a group of screens in our app that correspond to a feature. Login, registration, and checkout are all examples of flows.
When covering a login flow for a typical Android application, the LoginActivity depends on LoginViewModel, which in turn depends on UserRepository. Then UserRepository depends on a UserLocalDataSource and a UserRemoteDataSource, which in turn depends on a Retrofit service.
LoginActivity is the entry point to the login flow and the user interacts with the activity. Thus, LoginActivity needs to create the LoginViewModel with all its dependencies.
The Repository and DataSource classes of the flow look like this:
class UserRepository(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
class UserLocalDataSource { ... }
class UserRemoteDataSource(
private val loginService: LoginRetrofitService
) { ... }
Here’s what LoginActivity looks like:
class LoginActivity: Activity() {
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// In order to satisfy the dependencies of LoginViewModel, you have to also
// satisfy the dependencies of all of its dependencies recursively.
// First, create retrofit which is the dependency of UserRemoteDataSource
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(LoginService::class.java)
// Then, satisfy the dependencies of UserRepository
val remoteDataSource = UserRemoteDataSource(retrofit)
val localDataSource = UserLocalDataSource()
// Now you can create an instance of UserRepository //that LoginViewModel needs
val userRepository = UserRepository(localDataSource, remoteDataSource)
// Lastly, create an instance of LoginViewModel with //userRepository
loginViewModel = LoginViewModel(userRepository)
}
}
There are issues with this approach:
There’s a lot of boilerplate code. If we wanted to create another instance of LoginViewModel in another part of the code, we’d have code duplication.
Dependencies have to be declared in order. We have to instantiate UserRepository before LoginViewModel in order to create it.
It’s difficult to reuse objects. If we wanted to reuse UserRepository across multiple features, we’d have to make it follow the singleton pattern. The singleton pattern makes testing more difficult because all tests share the same singleton instance.
Example Solutions
Managing Dependencies With A Container
To solve the issue of reusing objects, we can create our own dependencies container class that we use to get dependencies. All instances provided by this container can be public. In the example, because we only need an instance of UserRepository, we can make its dependencies private with the option of making them public in the future if they need to be provided:
// Container of objects shared across the whole app
class AppContainer {
// Since you want to expose userRepository out of the container, you need to satisfy
// its dependencies as you did before
private val retrofit = Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(LoginService::class.java)
private val remoteDataSource = UserRemoteDataSource(retrofit)
private val localDataSource = UserLocalDataSource()
// userRepository is not private; it'll be exposed
val userRepository = UserRepository(localDataSource, remoteDataSource)
}
Because these dependencies are used across the whole application, they need to be placed in a common place all activities can use: the application class. Create a custom application class that contains an AppContainer instance.
// Custom Application class that needs to be specified
// in the AndroidManifest.xml file
class MyApplication : Application() {
// Instance of AppContainer that will be used by all the Activities of the app
val appContainer = AppContainer()
}
We aware that AppContainer is just a regular class with a unique instance shared across the app placed in our application class. However, AppContainer is not following the singleton pattern; in Kotlin, it’s not an object, and in Java, it’s not accessed with the typical Singleton.getInstance() method.
Now we can get the instance of the AppContainer from the application and obtain the shared of UserRepository instance:
class LoginActivity: Activity() {
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Gets userRepository from the instance of AppContainer in Application
val appContainer = (application as MyApplication).appContainer
loginViewModel = LoginViewModel(appContainer.userRepository)
}
}
In this way, we don’t have a singleton UserRepository. Instead, we have an AppContainer shared across all activities that contains objects from the graph and creates instances of those objects that other classes can consume.
If LoginViewModel is needed in more places in the application, having a centralized place where we create instances of LoginViewModel makes sense. We can move the creation of LoginViewModel to the container and provide new objects of that type with a factory. The code for a LoginViewModelFactory looks like this:
// Definition of a Factory interface with a function to create objects of a type
interface Factory<T> {
fun create(): T
}
// Factory for LoginViewModel.
// Since LoginViewModel depends on UserRepository, in order to create instances of
// LoginViewModel, you need an instance of UserRepository that you pass as a parameter.
class LoginViewModelFactory(private val userRepository: UserRepository) : Factory {
override fun create(): LoginViewModel {
return LoginViewModel(userRepository)
}
}
We can include the LoginViewModelFactory in the AppContainer and make the LoginActivity consume it:
// AppContainer can now provide instances of LoginViewModel with LoginViewModelFactory
class AppContainer {
...
val userRepository = UserRepository(localDataSource, remoteDataSource)
val loginViewModelFactory = LoginViewModelFactory(userRepository)
}
class LoginActivity: Activity() {
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Gets LoginViewModelFactory from the application instance of AppContainer
// to create a new LoginViewModel instance
val appContainer = (application as MyApplication).appContainer
loginViewModel = appContainer.loginViewModelFactory.create()
}
}
This approach is better than the previous one, but there are still some challenges to consider:
We have to manage AppContainer ourself, creating instances for all dependencies by hand.
There is still a lot of boilerplate code. We need to create factories or parameters by hand depending on whether we want to reuse an object or not.
Managing Dependencies In Application Flows
AppContainer gets complicated when we want to include more functionality in the project. When our app becomes larger and we start introducing different feature flows, there are even more problems that arise:
When we have different flows, we might want objects to just live in the scope of that flow. For example, when creating LoginUserData (that might consist of the username and password used only in the login flow) we don’t want to persist data from an old login flow from a different user. We want a new instance for every new flow. We can achieve that by creating FlowContainer objects inside the AppContainer as demonstrated in the next code example.
Optimizing the application graph and flow containers can also be difficult. We need to remember to delete instances that we don’t need, depending on the flow we’re in.
Imagine we have a login flow that consists of one activity (LoginActivity) and multiple fragments (LoginUsernameFragment and LoginPasswordFragment). These views want to:
Access the same LoginUserData instance that needs to be shared until the login flow finishes.
Create a new instance of LoginUserData when the flow starts again.
We can achieve that with a login flow container. This container needs to be created when the login flow starts and removed from memory when the flow ends.
Let’s add a LoginContainer to the example code. We want to be able to create multiple instances of LoginContainer in the app, so instead of making it a singleton, make it a class with the dependencies the login flow needs from the AppContainer.
class LoginContainer(val userRepository: UserRepository) {
val loginData = LoginUserData()
val loginViewModelFactory = LoginViewModelFactory(userRepository)
}
// AppContainer contains LoginContainer now
class AppContainer {
...
val userRepository = UserRepository(localDataSource, remoteDataSource)
// LoginContainer will be null when the user is NOT in the login flow
var loginContainer: LoginContainer? = null
}
Once we have a container specific to a flow, we have to decide when to create and delete the container instance. Because our login flow is self-contained in an activity (LoginActivity), the activity is the one managing the lifecycle of that container. LoginActivity can create the instance in onCreate() and delete it in onDestroy().
class LoginActivity: Activity() {
private lateinit var loginViewModel: LoginViewModel
private lateinit var loginData: LoginUserData
private lateinit var appContainer: AppContainer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appContainer = (application as MyApplication).appContainer
// Login flow has started. Populate loginContainer in AppContainer
appContainer.loginContainer = LoginContainer(appContainer.userRepository)
loginViewModel = appContainer.loginContainer.loginViewModelFactory.create()
loginData = appContainer.loginContainer.loginData
}
override fun onDestroy() {
// Login flow is finishing
// Removing the instance of loginContainer in the AppContainer
appContainer.loginContainer = null
super.onDestroy()
}
}
Like LoginActivity, login fragments can access the LoginContainer from AppContainer and use the shared LoginUserData instance.
Because in this case we’re dealing with view lifecycle logic, using lifecycle observation makes sense.
In this article, we understood about how to apply manual dependency injection in a real android application. Dependency injection is a good technique for creating scalable and testable Android applications. Use containers as a way to share instances of classes in different parts of our application and as a centralized place to create instances of classes using factories. This article reviewed an iterated approach of how we might start using manual dependency injection in our android application.
Thanks for reading! I hope you enjoyed and learned about ManualDependency Injection (DI) concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find other articles of CoolMonkTechie as below link :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (An Overview Of Dependency Injection In Android).
In this article, we will learn about Dependency Injection in android. Dependency Injection (DI) is a technique widely used in programming and well suited to Android development. By following the principles of DI, we lay the groundwork for good app architecture. This article explains an overview of how Dependency Injection (DI) works in Android.
A famous quote about learning is :
” Wisdom is not a product of schooling but of the lifelong attempt to acquire it.”
So let’s begin.
An Overview Of Dependency Injection
Dependency injection is based on the Inversion of Control principle in which generic code controls the execution of specific code.
Classes often require references to other classes. For example, a Car class might need a reference to an Engine class. These required classes are called dependencies, and in this example the Car class is dependent on having an instance of the Engine class to run.
There are three ways for a class to get an object it needs:
The class constructs the dependency it needs. In the example above, Car would create and initialize its own instance of Engine.
Grab it from somewhere else. Some Android APIs, such as Context getters and getSystemService(), work this way.
Have it supplied as a parameter. The app can provide these dependencies when the class is constructed or pass them in to the functions that need each dependency. In the example above, the Car constructor would receive Engine as a parameter.
With this Dependency Injection (DI) approach, we take the dependencies of a class and provide them rather than having the class instance obtain them itself.
Example Without Dependency Injection
This example is representing a Car that creates its own Engine dependency in code looks like this:
class Car {
private val engine = Engine()
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.start()
}
This is not an example of dependency injection because the Car class is constructing its own Engine.
This can be problematic because:
Car and Engine are tightly coupled – an instance of Car uses one type of Engine, and no subclasses or alternative implementations can easily be used. If the Car were to construct its own Engine, we would have to create two types of Car instead of just reusing the same Car for engines of type Gas and Electric.
The hard dependency on Engine makes testing more difficult. Car uses a real instance of Engine, thus preventing us from using a test double to modify Engine for different test cases.
Example With Dependency Injection
With this example, instead of each instance of Car constructing its own Engine object on initialization, it receives an Engine object as a parameter in its constructor. The code looks like this.
class Car(private val engine: Engine) {
fun start() {
engine.start()
}
}
fun main(args: Array) {
val engine = Engine()
val car = Car(engine)
car.start()
}
The main function uses Car. Because Car depends on Engine, the application creates an instance of Engine and then uses it to construct an instance of Car.
The benefits of this DI-based approach are:
Reusability of Car: We can pass in different implementations of Engine to Car. For example, we might define a new subclass of Engine called ElectricEngine that we want Car to use. If we use DI, all we need to do is pass in an instance of the updated ElectricEngine subclass, and Car still works without any further changes.
Easy testing of Car: We can pass in test doubles to test our different scenarios. For example, we might create a test double of Engine called FakeEngine and configure it for different tests.
Major Ways to do Dependency Injection
There are two major ways to do dependency injection in Android:
Constructor Injection. This is the way described above. We pass the dependencies of a class to its constructor.
Field Injection (or Setter Injection). Certain Android framework classes such as activities and fragments are instantiated by the system, so constructor injection is not possible. With field injection, dependencies are instantiated after the class is created. The code would look like this:
class Car {
lateinit var engine: Engine
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.engine = Engine()
car.start()
}
Automated Dependency Injection
In the previous example, we created, provided, and managed the dependencies of the different classes ourself, without relying on a library. This is called dependency injection by hand, or manual dependency injection. In the Car example, there was only one dependency, but more dependencies and classes can make manual injection of dependencies more tedious. Manual dependency injection also presents several problems:
For big apps, taking all the dependencies and connecting them correctly can require a large amount of boilerplate code. In a multi-layered architecture, in order to create an object for a top layer, we have to provide all the dependencies of the layers below it. As a concrete example, to build a real car we might need an engine, a transmission, a chassis, and other parts; and an engine in turn needs cylinders and spark plugs.
When we’re not able to construct dependencies before passing them in — for example when using lazy initializations or scoping objects to flows of our app — we need to write and maintain a custom container (or graph of dependencies) that manages the lifetimes of our dependencies in memory.
Dependency Injection Libraries
There are libraries that solve this problem by automating the process of creating and providing dependencies. They fit into two categories:
Reflection-based solutions that connect dependencies at runtime.
Static solutions that generate the code to connect dependencies at compile time.
Dagger is a popular dependency injection library for Java, Kotlin, and Android that is maintained by Google. It facilitates using DI in our app by creating and managing the graph of dependencies for us. It provides fully static and compile-time dependencies addressing many of the development and performance issues of reflection-based solutions such as Guice.
Alternatives To Dependency Injection
An alternative to dependency injection is using a service locator. The service locator design pattern also improves decoupling of classes from concrete dependencies. We create a class known as the service locator that creates and stores dependencies and then provides those dependencies on demand.
object ServiceLocator {
fun getEngine(): Engine = Engine()
}
class Car {
private val engine = ServiceLocator.getEngine()
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.start()
}
Dependency Injection Vs Service Locator Pattern
The service locator pattern is different from dependency injection in the way the elements are consumed. With the service locator pattern, classes have control and ask for objects to be injected; with dependency injection, the app has control and proactively injects the required objects.
The Comparisons between dependency injection and Service Locator Pattern are:
The collection of dependencies required by a service locator makes code harder to test because all the tests have to interact with the same global service locator.
Dependencies are encoded in the class implementation, not in the API surface. As a result, it’s harder to know what a class needs from the outside. As a result, changes to Car or the dependencies available in the service locator might result in runtime or test failures by causing references to fail.
Managing lifetimes of objects is more difficult if we want to scope to anything other than the lifetime of the entire app.
Use Hilt in Android Application
Hilt is Jetpack’s recommended library for dependency injection in Android. It defines a standard way to do DI in our application by providing containers for every Android class in our project and managing their lifecycles automatically for us.
Hilt is built on top of the popular DI library Dagger to benefit from the compile time correctness, runtime performance, scalability, and Android Studio support that Dagger provides.
Benefits Of Dependency Injection
Dependency injection provides our app with the following advantages:
Reusability of classes and decoupling of dependencies: It’s easier to swap out implementations of a dependency. Code reuse is improved because of inversion of control, and classes no longer control how their dependencies are created, but instead work with any configuration.
Ease of refactoring: The dependencies become a verifiable part of the API surface, so they can be checked at object-creation time or at compile time rather than being hidden as implementation details.
Ease of testing: A class doesn’t manage its dependencies, so when you’re testing it, you can pass in different implementations to test all of your different cases.
In this article, we understood about Dependency Injection fundamental in android. This article reviewed an overview of how Dependency Injection (DI) works in Android.
Thanks for reading! I hope you enjoyed and learned about DI concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find other articles of CoolMonkTechie as below link :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Manage Core App Quality In Android ?).
In this article, we will learn about how to manage Core App Quality in Android application. Android users expect high-quality apps. App quality directly influences the long-term success of our app – in terms of installs, user rating and reviews, engagement, and user retention. This article reviews about the core aspects of quality (Core App Quality Guidelines) in our android application, through a compact set of quality criteria and associated tests. All Android apps should meet these criteria.
A famous quote about learning is :
“One learns from books and example only that certain things can be done. Actual learning requires that you do those things.”
So let’s begin.
Overview
Android users expect high-quality apps that should meet the core aspects of quality, through a compact set of quality criteria and associated tests criteria which provides in Core App Quality Guidelines.
Before publishing our apps, we need to test them against these criteria to ensure that application function works well on many devices and meets Android standards for navigation and design, and are prepared for promotional opportunities in the Google Play store.
Core App Quality Guidelines
Handle Visual Design and User Interaction
These criteria ensure that our application provides standard Android visual design and interaction patterns from core app quality guidelines where appropriate, for a consistent and intuitive user experience.
Standard Design
The application follows Android Design guidelines and uses common UI patterns and icons:
This does not redefine the expected function of a system icon (such as the Back button).
The application does not replace a system icon with a completely different icon if it triggers the standard UI behavior.
If the application provides a customized version of a standard system icon, the icon strongly resembles the system icon and triggers the standard system behavior.
The application does not redefine or misuse Android UI patterns, such that icons or behaviors could be misleading or confusing to users.
Navigation
The application supports standard system Back button navigation and does not make use of any custom, on-screen “Back button” prompts.
All dialogs are dismissible using the Back button.
Pressing the Home button at any point navigates to the Home screen of the device.
Notifications
Notifications follow Android Design guidelines. In particular:
Multiple notifications are stacked into a single notification object, where possible.
Notifications are persistent only if related to ongoing events (such as music playback or a phone call).
Notifications do not contain advertising or content unrelated to the core function of the app, unless the user has opted in.
The application uses notifications only to:
Indicate a change in context relating to the user personally (such as an incoming message), or
Expose information/controls relating to an ongoing event (such as music playback or a phone call).
Manage Functionality
These criteria ensure that our application provides the expected functional behavior, with the appropriate level of permissions.
Permissions
The application requests only the absolute minimum permissions that it needs to support core functionality.
The application does not request permissions to access sensitive data (such as Contacts or the System Log) or services that can cost the user money (such as the Dialer or SMS), unless related to a core capability of the application.
Install Location
The application functions normally when installed on SD card (if supported by application). Supporting installation to SD card is recommended for most large apps (10MB+).
Audio
Audio does not play when the screen is off, unless this is a core feature (for example, the app is a music player).
It does not play behind the lock screen, unless this is a core feature.
Audio does not play on the home screen or over another application, unless this is a core feature.
Audio resumes when the application returns to the foreground, or indicates to the user that playback is in a paused state.
UI and Graphics
The application supports both landscape and portrait orientations (if possible). Orientations expose largely the same features and actions and preserve functional parity. Minor changes in content or views are acceptable.
The application uses the whole screen in both orientations and does not letterbox to account for orientation changes. Minor letterboxing to compensate for small variations in screen geometry is acceptable.
The application correctly handles rapid transitions between display orientations without rendering problems.
User/App State
The application should not leave any services running when the app is in the background, unless related to a core capability of the application. For example, the application should not leave services running to maintain a network connection for notifications, to maintain a Bluetooth connection, or to keep the GPS powered-on.
The application correctly preserves and restores user or application state. The application preserves user or application state when leaving the foreground and prevents accidental data loss due to back-navigation and other state changes. This must restore the preserved state and any significant stateful transaction that was pending, such as changes to editable fields, game progress, menus, videos, and other sections of the application or game, when returning to the foreground.
The application returns the user to the exact state in which it was last used, when the application is resumed from the Recents application switcher.
When this is resumed after the device wakes from sleep (locked) state, the application returns the user to the exact state in which it was last used.
When the application is relaunched from Home or All Applications, the application restores the application state as closely as possible to the previous state.
On Back keypresses, the application gives the user the option of saving any application or user state that would otherwise be lost on back-navigation.
Manage Compatibility, Performance and Stability
These criteria ensure that applications provide the compatibility, performance, stability, and responsiveness expected by users.
Stability
The application does not crash, force close, freeze, or otherwise function abnormally on any targeted device.
Performance
The application loads quickly or provides onscreen feedback to the user (a progress indicator or similar cue) if the application takes longer than two seconds to load.
With StrictMode enabled, no red flashes (performance warnings from StrictMode) are visible when exercising the application, including during game play, animations and UI transitions, and any other part of the application.
Software Development Kit (SDK)
The application runs on the latest public version of the Android platform without crashing or loss of core function.
This targets the latest SDK by setting the targetSdk value to minimize the use of any platform-provided compatibility fallbacks.
The application is built with the latest SDK by setting the compileSdk value.
Battery
The Android application supports power management features in Android 6.0+ (Doze and App Standby) properly. In the case where core functionality is disrupted by power management, only qualified apps may request an exemption.
Media
Music and video playback is smooth, without crackle, stutter, or other artifacts, during normal app use and load.
Visual Quality
The application displays graphics, text, images, and other UI elements without noticeable distortion, blurring, or pixelation.
The application provides high-quality graphics for all targeted screen sizes and form factors.
No aliasing at the edges of menus, buttons, and other UI elements is visible.
The application displays text and text blocks in an acceptable manner.
Composition is acceptable in all supported form factors.
No cut-off letters or words are visible.
No improper word wraps within buttons or icons are visible.
Sufficient spacing between text and surrounding elements.
Handle Application Security
These criteria ensure that apps handle user data and personal information safely. In addition to this checklist, applications published on the Google Play Store must also follow the User Data policies to protect users’ privacy.
Data
All private data is stored in the application’s internal storage.
External storage data is verified before being accessed.
All intents and broadcasts follow secure best practices:
Intents are explicit if the destination application is known.
These intents enforce and use appropriate permissions.
Intents that contain data and payload are verified before use.
No personal or sensitive user data is logged to the system or app-specific log.
App Components
Only application components that share data with other applications, or components that should be invoked by other applications, are exported. This includes activities, services, broadcast receivers, and especially content providers. Always set the android:exported attribute explicitly, regardless of whether or not we export any of your application’s components.
All application components that share content with other applications define (and enforce) appropriate permissions. This includes activities, services, broadcast receivers, and especially content providers.
All content providers that share content between our applications use android:protectionLevel="signature".
Networking
All network traffic is sent over SSL.
Application declares a network security configuration.
If the application uses Google Play services, the security provider is initialized at application startup.
Libraries
All libraries, SDKs, and dependencies are up to date.
WebViews
JavaScript is disabled in all WebViews (unless required).
WebViews only load allow listed content if possible.
WebViews do not use addJavaScriptInterface() with untrusted content. On Android M and above, HTML message channels can be used instead.
Execution
The app does not dynamically load code from outside the app’s APK.
Cryptography
The application uses strong, platform-provided cryptographic algorithms and does not implement custom algorithms.
The application uses a properly secure random number generator, in particular to initialize cryptographic keys.
Google Play
These criteria ensure that our applications are ready to publish on Google Play.
Policies
The application strictly adheres to the terms of the Google Play Developer Content Policy and does not offer inappropriate content, does not use the intellectual property or brand of others, and so on.
The maturity level of the application is set appropriately, based on the Content Rating Guidelines.
The application supports power management features in Android 6.0+ (Doze and App Standby) properly. In the case where core functionality is disrupted by power management, only qualified applications may request an exemption.
App Details Page
The application’s feature graphic follows the guidelines outlined. Make sure that:
The application listing includes a high-quality feature graphic.
The feature graphic does not contain device images, screenshots, or small text that will be illegible when scaled down and displayed on the smallest screen size that our application is targeting.
The feature graphic does not resemble an advertisement.
The application’s screenshots and videos do not show or reference non-Android devices.
The application’s screenshots or videos do not represent the content and experience of our application in a misleading way.
User Support
Common user-reported bugs in the Reviews tab of the Google Play page are addressed if they are reproducible and occur on many different devices. If a bug occurs on only a few devices, we should still address it if those devices are particularly popular or new.
Setting Up A Test Environment
To assess the quality of our application, we need to set up a suitable hardware or emulator environment for testing. The ideal test environment would include a small number of actual hardware devices that represent key form factors and hardware/software combinations currently available to consumers. It’s not necessary to test on every device that’s on the market — rather, we should focus on a small number of representative devices, even using one or two devices per form factor.
If we are not able to obtain actual hardware devices for testing, we should set up emulated devices (AVDs) to represent the most common form factors and hardware/software combinations.
To go beyond basic testing, we can add more devices, more form factors, or new hardware/software combinations to our test environment. We can also increase the number or complexity of tests and quality criteria.
Test Procedures
These test procedures help us discover various types of quality issues in our app. We can combine the tests or integrate groups of tests together in our own test plans.
Core Suite
Navigate to all parts of the application — all screens, dialogs, settings, and all user flows.
If the application allows for editing or content creation, game play, or media playback, make sure to enter those flows to create or modify content.
While exercising the application, we introduce transient changes in network connectivity, battery function, GPS availability, system load, and so on.
From each application screen, we press the device’s Home key, then re-launch the application from the All Applications screen.
We switch to another running application and then return to the application under test using the Recents application switcher from each application screen.
From each application screen (and dialogs), we press the Back button.
From each application screen, we rotate the device between landscape and portrait orientation at least three times.
Switch to another application to send the test application into the background. Go to Settings and check whether the test application has any services running while in the background. In Android 4.0 and higher, we go to the Applications screen and find the application in the “Running” tab.
We press the power button to put the device to sleep, then press the power button again to wake the screen.
We set the device to lock when the power button is pressed. Press the power button to put the device to sleep, then press the power button again to wake the screen, then unlock the device.
For devices that have slide-out keyboards, we slide the keyboard in and out at least once. For devices that have keyboard docks, we attach the device to the keyboard dock.
We plug-in the external display for those devices that have an external display port.
Trigger and observe in the notifications drawer all types of notifications that the application can display. Expand notifications where applicable (Android 4.1 and higher), and tap all actions offered.
Install On SD Card
Repeat Core Suite with the application installed to a device’s SD card (if supported by application). To move the application to SD card, we can use Settings > App Info > Move to SD Card.
Hardware Acceleration
Repeat Core Suite with hardware acceleration enabled. To force-enable hardware acceleration (where supported by device), add hardware-accelerated="true"to the <application> in the application manifest and recompile.
Performance and Stability
Review the Android manifest file and build configuration to ensure that the application is built against the latest available SDK (targetSdkand compileSdk).
Performance Monitoring
Repeat Core Suite with StrictMode profiling enabled. Pay close attention to garbage collection and its impact on the user experience.
Battery
Repeat Core Suite across Doze and App Standby cycles. Pay close attention to alarms, timers, notifications, syncs, and so on.
Security
Review all data stored in external storage.
We review how data loaded from external storage is handled and processed.
Review all content providers defined in the Android manifest file for appropriate protectionLevel.
All permissions review that our application requires, in the manifest file, at runtime and in the application settings (Settings > App Info) on the device.
Review all application components defined in the Android manifest file for the appropriate export state. The export property must be set explicitly for all components.
Review the application’s Network Security configuration, ensuring that no lint checks on the configuration fail.
For each WebView, navigate to a page that requires JavaScript.
For each WebView, attempt to navigate to sites and content that are outside of our control.
Declare a Network Security Configuration that disables cleartext traffic then execute the application.
Run the application and exercise all core functionality, while observing the device log. No private user information should be logged.
Google Play
Sign into the Google Play Developer Console to review developer profile, app description, screenshots, feature graphic, content rating and user feedback.
Download feature graphic and screenshots and scale them down to match the display sizes on the devices and form factors we are targeting.
Review all graphical assets, media, text, code libraries, and other content packaged in the application or expansion file download.
Review Support for other use cases in Doze and App Standby.
Payments
Navigate to all screens of our app and enter all in-app purchase flows.
Testing with StrictMode
For performance testing, we recommend enabling StrictMode in our app and using it to catch operations on the main thread and other threads that could affect performance, network accesses, file reads/writes, and so on.
We can set up a monitoring policy per thread using StrictMode.ThreadPolicy.Builder and enable all supported monitoring in the ThreadPolicy using detectAll().
Make sure to enable visual notification of policy violations for the ThreadPolicy using penaltyFlashScreen().
That’s all about in this article.
Conclusion
In this article, we understood about how to manage Core App Quality in Android application. This article reviewed about the core aspects of quality (Core App Quality) in our android application, through a compact set of quality criteria and associated tests.
Thanks for reading! I hope you enjoyed and learned about Core App Quality Guidelines concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find Other articles of CoolMonkTechie as below link :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (An Overview Of Property Animation In Android).
In this article, we will learn about Property Animation Overview in Android. The property animation system is a robust framework that allows us to animate almost anything. We can define an animation to change any object property over time, regardless of whether it draws to the screen. A property animation changes a property’s (a field in an object) value over a specified length of time. To animate something, we specify the object property that we want to animate, such as an object’s position on the screen, how long we want to animate it for, and what values we want to animate between. This article shows the Property Animation related concepts in Android.
A famous quote about learning is :
” Develop a passion for learning. If you do, you will never cease to grow. “
So let’s begin.
Characteristics Of Property Animation
The property animation system lets us define the following characteristics of an animation:
Duration: We can specify the duration of an animation. The default length is 300 ms.
Time interpolation: This specify how the values for the property calculate as a function of the animation’s current elapsed time.
Repeat count and behavior: This specify whether to have an animation repeat when it reaches the end of a duration and how many times to repeat the animation. We can also specify whether we want the animation to play back in reverse. Setting it to reverse plays the animation forwards then, backwards repeatedly, until it reaches the number of repeats.
Animator sets: We can group animations into logical sets that play together or sequentially or after specified delays.
Frame refresh delay: This specify how often to refresh frames of our animation. The default is set to refresh every 10 ms, but the speed in which our application can refresh frames is ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.
The Work Flow of Property Animation
Linear Animation
First, we see how an animation works with a simple example.
In this above linear animation figure, it depicts a hypothetical object animates with its x property, which represents its horizontal location on a screen. It sets the duration of the animation to 40 ms and the distance to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at horizontal position 40. This is an example of an animation with linear interpolation, meaning the object moves at a constant speed.
Non-Linear Animation
We can also specify animations to have a non-linear interpolation.
In this above non-linear animation figure, it illustrates a hypothetical object that speeds up at the beginning of the animation and decelerates at the end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the beginning, this animation accelerates up to the halfway point, then decelerates from the halfway point until the end of the animation. As this non-linear animation figure shows the distance traveled at the beginning and end of the animation is less than in the middle.
Animations Calculation Using Property Animation
We understood how the important components of the property animation system would calculate animations like the ones. Now we will discuss how the main classes work with one another.
In this figure, The ValueAnimator object keeps track of our animation’s timing, such as how long the animation has been running, and the current value of the property that it is animating.
The ValueAnimatorencapsulates
a TimeInterpolator, which defines animation interpolation, and
a TypeEvaluator, which defines how to calculate values for the property being animated.
For example, in non-linear animation figure, the TimeInterpolator used would be AccelerateDecelerateInterpolator and the TypeEvaluator would be IntEvaluator.
To start an animation, we create a ValueAnimator and give it the starting and ending values for the property that we want to animate, along with the duration of the animation. When we call start(), the animation begins. During the whole animation, the ValueAnimator calculates an elapsed fraction between 0 and 1, based on the duration of the animation and how much time has elapsed. The elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0% and 1 meaning 100%. For example, in linear animation figure , the elapsed fraction at t = 10 ms would be .25 because the total duration is t = 40 ms.
When the ValueAnimator calculates an elapsed fraction, it calls the TimeInterpolator that is currently set to calculate an interpolated fraction. An interpolated fraction maps the elapsed fraction to a new fraction that takes into account the time interpolation that is set. For example, in non-linear animation figure, because the animation slowly accelerates, the interpolated fraction, about .15, is less than the elapsed fraction, .25, at t = 10 ms. In linear animation figure, the interpolated fraction is always the same as the elapsed fraction.
When the interpolated fraction calculates, the ValueAnimator calls the TypeEvaluator to calculate the value of the property that you are animating, based on the interpolated fraction, the starting value, and the ending value of the animation. For example, in non-linear figure, the interpolated fraction was .15 at t = 10 ms, so the value for the property would be .15 × (40 – 0), or 6.
Property Animation Vs View Animation
View Animation
” The View Animation system provides the capability to only animate View objects.”
So if we wanted to animate non-View objects, we have to implement our own code to do so.
“The View Animation system constrains because it only exposes a few aspects of a View object to animate, such as the scaling and rotation of a View but not the background color, for instance.”
“Another disadvantage of the View Animation system is that it only modified where the View drew, and not the actual View itself.”
For instance, if we animated a button to move across the screen, the button draws correctly, but the actual location where we can click the button does not change, so we have to implement our own logic to handle this.
Property Animation
“With the property animation system, these constraints removed completely, and we can animate any property of any object (Views and non-Views) and the object they modify it.”
The property animation system is also more robust in the way it carries out animation. At a high level, we assign animators to the properties that we want to animate, such as color, position, or size and can define aspects of the animation such as interpolation and synchronization of multiple animators.
The View Animation system, however, takes less time to set up and requires less code to write. If View Animation accomplishes everything that we need to do, or if our existing code already works the way we want, there is no need to use the Property Animation system. It also might make sense to use both animation systems for different situations if the use case arises.
The Main Components of Property Animation System
We can find most of the property animation system’s APIs in android.animation. Because the View Animation system already defines many interpolators in android.view.animation, we can use those interpolators in the property animation system.
The Main Components of Property Animation System are :
Animators
Evaluators
Interpolators
Animators
The Animator class provides the basic structure for creating animations. We normally do not use this class directly, as it only provides minimal functionality that must be extended to fully support animating values. The following subclasses extend Animator:
ValueAnimator
ObjectAnimator
AnimatorSet
ValueAnimator
The main timing engine for property animation that also computes the values for the property to be animated. It has all the core functionality that calculates animation values and contains the timing details of each animation, information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate.
There are two pieces to animating properties:
calculating the animated values and
setting those values on the object and property that is being animated.
ValueAnimator does not carry out the second piece, so we must listen for updates to values calculated by the ValueAnimator and modify the objects that want to animate with our own logic.
ObjectAnimator
A subclass of ValueAnimator that allows us to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation.
We want to use ObjectAnimator most of the time, because it makes animating values on target objects much easier. However, we sometimes want to use ValueAnimator directly because ObjectAnimator has a few more restrictions, such as requiring specific accessor methods to be present on the target object.
AnimatorSet
This provides a mechanism to group animations together so they run in relation to one another. We can set animations to play together, sequentially, or after a specified delay.
Evaluators
Evaluators tell the property animation system how to calculate values for a property. They take the timing data that an Animator class provides, the animation’s start and end value, and calculate the animated values of the property based on this data.
The property animation system provides the following evaluators:
IntEvaluator: The default evaluator to calculate values for int properties.
FloatEvaluator: The default evaluator to calculate values for float properties.
ArgbEvaluator: The default evaluator to calculate values for color properties that represents as hexadecimal values.
TypeEvaluator: An interface that allows us to create our own evaluator. If we are animating an object property that is not an int, float, or color, we must implement the TypeEvaluator interface to specify how to compute the object property’s animated values. We can also specify a custom TypeEvaluator for int, float, and color values, if we want to process those types differently than the default behavior.
Interpolators
A time interpolator defines how specific values in an animation are calculated as a function of time.
For example, we can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or we can specify animations to use non-linear time, for example, accelerating at the beginning and decelerating at the end of the animation.
The property animation system provides the following interpolators:
AccelerateDecelerateInterpolator: An interpolator whose rate of change starts and ends slowly but accelerates through the middle.
AccelerateInterpolator: An interpolator whose rate of change starts out slowly and then accelerates.
AnticipateInterpolator: An interpolator whose change starts backward, then flings forward.
AnticipateOvershootInterpolator: An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value.
BounceInterpolator: An interpolator whose change bounces at the end.
CycleInterpolator: An interpolator whose animation repeats for a specified number of cycles.
DecelerateInterpolator: An interpolator whose rate of change starts out quickly and then decelerates.
LinearInterpolator: An interpolator whose rate of change is constant.
OvershootInterpolator: An interpolator whose change flings forward and overshoots the last value then comes back.
TimeInterpolator: An interface that allows you to implement your own interpolator.
Animate Using ValueAnimator
The ValueAnimator class lets us animate values of some type for the duration of an animation by specifying a set of int, float, or color values to animate through. We get a ValueAnimator by calling one of its factory methods: ofInt(), ofFloat(), or ofObject(). For example:
In this code, the ValueAnimator calculates the values of the animation, between startPropertyValue and endPropertyValue using the logic supplied by MyTypeEvaluator for a duration of 1000 ms, when the start() method runs.
We can use the values of the animation by adding an AnimatorUpdateListener to the ValueAnimator object, as shown in the following code:
ValueAnimator.ofObject(...).apply {
...
addUpdateListener { updatedAnimation ->
// You can use the animated value in a property that uses the
// same type as the animation. In this case, you can use the
// float value in the translationX property.
textView.translationX = updatedAnimation.animatedValue as Float
}
...
}
In the onAnimationUpdate() method, we can access the updated animation value and use it in a property of one of our views.
Animate Using ObjectAnimator
The ObjectAnimator is a subclass of the ValueAnimator and combines the timing engine and value computation of ValueAnimator with the ability to animate a named property of a target object. This makes animating any object much easier, as we no longer need to implement the ValueAnimator.AnimatorUpdateListener, because the animated property updates automatically.
Instantiating an ObjectAnimator is like a ValueAnimator, but we also specify the object and the name of that object’s property (as a String) along with the values to animate between:
To have the ObjectAnimator update properties correctly, we must do the following steps:
The object property that we are animating must have a setter function (in camel case) in the form ofset<PropertyName>(). Because the ObjectAnimator automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo, we need to have a setFoo() method. If this setter method does not exist, we have three options:
Add the setter method to the class if we have the rights to do so.
Use a wrapper class that we have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.
Use ValueAnimator instead.
If we specify only one value for the values... parameter in one of the ObjectAnimator factory methods, we assume it to be the ending value of the animation. Therefore, the object property that we are animating must have a getter function that is used to get the starting value of the animation. The getter function must be in the form of get<PropertyName>(). For example, if the property name is foo, we need to have a getFoo() method.
The getter and setter methods of the property that we are animating must operate on the same type as the starting and ending values that specify to ObjectAnimator. For example, we must have targetObject.setPropName(float) and targetObject.getPropName() if we construct the following ObjectAnimator:
Depending on what property or object we are animating, we might need to call the invalidate() method on a View to force the screen to redraw itself with the updated animated values. We do this in the onAnimationUpdate() callback.
Animate Using AnimatorSet
In many cases, we want to play an animation that depends on when another animation starts or finishes. The Android system lets us bundle animations together into an AnimatorSet, so that we can specify whether to start animations simultaneously, sequentially, or after a specified delay. We can also nest AnimatorSet objects within each other.
In this code snippet, the following Animator objects in the following manner :
Plays bounceAnim.
Plays squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2 at the same time.
Plays bounceBackAnim.
Plays fadeAnim.
Animation Listeners
We can listen for important events during an animation’s duration with the listeners described below.
Animator.AnimatorListener
ValueAnimator.AnimatorUpdateListener
Animator.AnimatorListener
onAnimationStart() : This method called when the animation starts.
onAnimationEnd(): This called when the animation ends.
onAnimationRepeat(): This method called when the animation repeats itself.
onAnimationCancel(): Called when the animation is canceled. A cancelled animation also calls onAnimationEnd(), regardless of how they were ended.
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate(): This method called on every frame of the animation. Listen to this event to use the calculated values generated by ValueAnimator during an animation.
Animate Layout Changes to ViewGroup Objects
The property animation system provides the capability to animate changes to ViewGroup objects as well as provide an easy way to animate View objects themselves.
We can animate layout changes within a ViewGroup with the LayoutTransition class. Views inside a ViewGroup can go through an appearing and disappearing animation when we add them to or remove them from a ViewGroup or when we call a View’s setVisibility() method with VISIBLE, INVISIBLE, or GONE. The remaining Views in the ViewGroup can also animate into their new positions when we add or remove Views.
We can define the following animations in a LayoutTransition object by calling setAnimator() and passing in an Animator object with one of the following LayoutTransition constants:
APPEARING : This flag indicating the animation that runs on items that are appearing in the container.
CHANGE_APPEARING : A flag indicating the animation that runs on items that are changing due to a new item appearing in the container.
DISAPPEARING : This flag indicating the animation that runs on items that are disappearing from the container.
CHANGE_DISAPPEARING : A flag indicating the animation that runs on items that are changing due to an item disappearing from the container.
Animate View State Changes Using StateListAnimator
The StateListAnimator class lets us define animators that run when the state of a view changes. This object behaves as a wrapper for an Animator object, calling that animation whenever the specified view state (such as “pressed” or “focused”) changes.
The StateListAnimator can be defined in an XML resource with a root <selector> element and child <item> elements that each specify a different view state defined by the StateListAnimator class. Each <item> contains the definition for a property animation set.
For example, the following file creates a state list animator that changes the x and y scale of the view when it’s pressed:
res/xml/animate_scale.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- the pressed state; increase x and y size to 150% -->
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="scaleX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1.5"
android:valueType="floatType"/>
<objectAnimator android:propertyName="scaleY"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1.5"
android:valueType="floatType"/>
</set>
</item>
<!-- the default, non-pressed state; set x and y size to 100% -->
<item android:state_pressed="false">
<set>
<objectAnimator android:propertyName="scaleX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1"
android:valueType="floatType"/>
<objectAnimator android:propertyName="scaleY"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1"
android:valueType="floatType"/>
</set>
</item>
</selector>
To attach the state list animator to a view, add the android:stateListAnimator attribute as follows:
Now the animations defined in animate_scale.xml are used when this button’s state changes.
Or, to instead assign a state list animator to a view in our code, use the AnimatorInflater.loadStateListAnimator() method, and assign the animator to our view with the View.setStateListAnimator() method.
Use a TypeEvaluator
If we want to animate a type unknown to the Android system, we can create our own evaluator by implementing the TypeEvaluator interface. The types that are known by the Android system are int, float, or a color, which are supported by the IntEvaluator, FloatEvaluator, and ArgbEvaluator type evaluators.
There is only one method to implement in the TypeEvaluator interface, the evaluate() method. This allows the animator that we are using to return an appropriate value for our animated property at the current point of the animation. The FloatEvaluator class shows how to do this:
private class FloatEvaluator : TypeEvaluator<Any> {
override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
return (startValue as Number).toFloat().let { startFloat ->
startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
}
}
}
Use Interpolators
An interpolator define how calculate specific values in an animation as a function of time. For example, we can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or we can specify animations to use non-linear time, for example, using acceleration or deceleration at the beginning or end of the animation.
Interpolators in the animation system receive a fraction from Animators that represent the elapsed time of the animation. Interpolators modify this fraction to coincide with the type of animation that it aims to provide. The Android system provides a set of common interpolators in the android.view.animation package. If none of these suit our needs, we can implement the TimeInterpolator interface and create our own.
For example, we will see that how to compare the default interpolator AccelerateDecelerateInterpolator and the LinearInterpolator that calculates interpolated fractions. The LinearInterpolator has no effect on the elapsed fraction. The AccelerateDecelerateInterpolator accelerates into the animation and decelerates out of it.
override fun getInterpolation(input: Float): Float = input
Specify Keyframes
A Keyframe object consists of a time/value pair that lets us define a specific state at a specific time of an animation. Each keyframe can also have its own interpolator to control the behavior of the animation in the interval between the previous keyframe’s time and the time of this keyframe.
To instantiate a Keyframe object, we must use one of the factory methods, ofInt(), ofFloat(), or ofObject() to get the appropriate type of Keyframe. We then call the ofKeyframe() factory method to get a PropertyValuesHolder object. Once we have the object, we can get an animator by passing in the PropertyValuesHolder object and the object to animate.
val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
duration = 5000
}
Animate Views
The Property Animation system allows streamlined animation of View objects and offers a few advantages over the view animation system. The View Animation system transformed View objects by changing the way that they were drawn. This was handled in the container of each View, because the View itself had no properties to manipulate. This resulted in the View being animated, but caused no change in the View object itself. This led to behavior such as an object still existing in its original location, even though it was drawn on a different location on the screen.
The property animation system can animate Views on the screen by changing the actual properties in the View objects. In addition, Views also automatically calls the invalidate() method to refresh the screen whenever its properties are changed. The new properties in the View class that facilitate property animations are:
translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates, which are set by its layout container.
rotation, rotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
xand y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).
To animate a property of a View object, such as its color or rotation value, all we need to do is create a property animator and specify the View property that we want to animate. For example:
The ViewPropertyAnimator provides a simple way to animate several properties of a View in parallel, using a single underlying Animator object. It behaves much like an ObjectAnimator, because it modifies the actual values of the view’s properties, but is more efficient when animating many properties at once. In addition, the code for using the ViewPropertyAnimator is much more concise and easier to read.
The following code snippets show the differences in using multiple ObjectAnimator objects, a single ObjectAnimator, and the ViewPropertyAnimator when simultaneously animating the x and y property of a view.
Multiple ObjectAnimator objects
val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
playTogether(animX, animY)
start()
}
One ObjectAnimator
val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()
ViewPropertyAnimator
myView.animate().x(50f).y(100f)
Declare Animations in XML
The property animation system lets us declare property animations with XML instead of doing it programmatically. By defining our animations in XML, we can easily reuse our animations in multiple activities and more easily edit the animation sequence.
The following property animation classes have XML declaration support with the following XML tags:
In order to run this animation, we must inflate the XML resources in our code to an AnimatorSet object, and then set the target objects for all of the animations before starting the animation set. Calling setTarget() sets a single target object for all children of the AnimatorSet as a convenience.
(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
setTarget(myObject)
start()
}
We can also declare a ValueAnimator in XML, as shown in the following example:
To use the previous ValueAnimator in our code, we must inflate the object, add an AnimatorUpdateListener, get the updated animation value, and use it in a property of one of our views, as shown in the following code:
(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
addUpdateListener { updatedAnimation ->
textView.translationX = updatedAnimation.animatedValue as Float
}
start()
}
In this article, we understood about Property Animation Overview in Android. This article described about Property Animation related concepts like Workflow, benefits, Animates View using ValueAnimator, ObjectAnimator and AnimatorSet in Android.
Thanks for reading! I hope you enjoyed and learned about Property Animation concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find Other articles of CoolMonkTechie as below link :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (Is Awesome Design Patterns Valuable In Kotlin?).
In this article, we will learn about why design patterns are valuable and frequently used in Kotlin. When we are new in programming languages, we don’t know which design patterns we should use with it and how to implement them. Design Patterns determine certain factors to differentiate between a good code and a bad code in Kotlin. This may be the code structure or the comments used or the variable names or something else. Being able to use a relevant design pattern is a prerequisite to creating functional, high-quality, and secure applications in Android with use of Kotlin.
So every developer should follow Design Patterns while writing the Kotlin code of an Android application.
A famous quote about learning is :
” One learns from books and example only that certain things can be done. Actual learning requires that you do those things. “
So Let’s begin.
Design Patterns: What they are and why know them ?
A software design pattern is a solution to a particular problem we might face when designing an app’s architecture. But unlike out-of-the-box services or open-source libraries, we can’t paste a design pattern into our application because it isn’t a piece of code. Rather, it’s a general concept for how to solve a problem. A design pattern is a template that tells us how to write code, but it’s up to us to fit our code to this template.
Design patterns bring several benefits:
Tested solutions. We don’t need to waste time and reinvent the wheel trying to solve a particular software development problem, as design patterns already provide the best solution and tell us how to implement it.
Code unification. Design patterns provide us with typical solutions that have tested for drawbacks and bugs, helping us make fewer mistakes when designing our app architecture.
Common vocabulary. Instead of providing in-depth explanations of how to solve this or that software development problem, we can say what design pattern we used and other developers will immediately understand what solutions we implemented.
Design Patterns: What is it ?
” A Design Pattern is a general, reusable solution to a commonly occurring problem within a given context. “
So, Design Patterns are a pattern solution that follows to solve a particular feature. These are the best practices that any programmer can use to build an application.
We use Design Patterns that makes our code easier to understand and more reusable in Android.
Design Patterns: Patterns Types In Kotlin
Before we describe the most common architecture patterns in Android Kotlin, we should first learn the three types of software design patterns and how they differ:
Creational Design Patterns
Structural Design Patterns
Behavioral Design Patterns
1. Creational Design Patterns
Creational software design patterns deal with object creation mechanisms, which increase flexibility and reuse of existing code. They try to instantiate objects in a manner suitable for the particular situation.
This Pattern is used to create some object without showing the logic or the steps that involves in creating the object. So, every time we want an object, we need not instantiate the object by using the new operator. So, this makes creating an object easier and can be easily created again and again.
Here are several creational design patterns:
Builder Pattern
Singleton Pattern
Factory Method Pattern
Abstract Factory
2. Structural Design Patterns
Structural design patterns aim to simplify the design by finding a simple way of realizing relationships between classes and objects. These patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
In this Design Pattern, we concern about the structure of the code. Here, we follow some particular structural pattern that will help in understanding the code and the working of code just by looking at the structure of the code. These are some structural architecture patterns:
Adapter Pattern
Facade Pattern
Decorator Pattern
Composite Pattern
Protection Proxy Pattern
3. Behavioral Design Patterns
Behaviour design patterns identify common communication patterns between entities and implement these patterns. This Patterns mainly tells how the objects of the classes will communicate with each other. These patterns help us in understanding the code in a better way because by viewing the code we can identify the pattern and then we can understand the code in a better way.
Observer / Listener Pattern
Command Pattern
Strategy Pattern
State Pattern
Chain of Responsibility Pattern
Visitor Pattern
Mediator Pattern
Memento Pattern
Most Frequently Used Design Patterns In Kotlin
We’re going to provide only the essential information about each software design pattern–namely, how it works from the technical point of view and when it should be applied. We’ll also give an illustrative example in the Kotlin programming language.
1. Creational: Builder Pattern
The builder pattern is used to create complex objects with constituent parts that must be created in the same order or using a specific algorithm. An external class controls the construction algorithm.
Example
For example, Let’s assume that external library provides Dialog class, and we have only accessed to Dialog Public interface, which can not be changed.
class Dialog {
fun showTitle() = println("showing title")
fun setTitle(text: String) = println("setting title text $text")
fun setTitleColor(color: String) = println("setting title color $color")
fun showMessage() = println("showing message")
fun setMessage(text: String) = println("setting message $text")
fun setMessageColor(color: String) = println("setting message color $color")
fun showImage(bitmapBytes: ByteArray) = println("showing image with size ${bitmapBytes.size}")
fun show() = println("showing dialog $this")
}
//Builder:
class DialogBuilder() {
constructor(init: DialogBuilder.() -> Unit) : this() {
init()
}
private var titleHolder: TextView? = null
private var messageHolder: TextView? = null
private var imageHolder: File? = null
fun title(init: TextView.() -> Unit) {
titleHolder = TextView().apply { init() }
}
fun message(init: TextView.() -> Unit) {
messageHolder = TextView().apply { init() }
}
fun image(init: () -> File) {
imageHolder = init()
}
fun build(): Dialog {
val dialog = Dialog()
titleHolder?.apply {
dialog.setTitle(text)
dialog.setTitleColor(color)
dialog.showTitle()
}
messageHolder?.apply {
dialog.setMessage(text)
dialog.setMessageColor(color)
dialog.showMessage()
}
imageHolder?.apply {
dialog.showImage(readBytes())
}
return dialog
}
class TextView {
var text: String = ""
var color: String = "#00000"
}
}
Usage
//Function that creates dialog builder and builds Dialog
fun dialog(init: DialogBuilder.() -> Unit): Dialog {
return DialogBuilder(init).build()
}
val dialog: Dialog = dialog {
title {
text = "Dialog Title"
}
message {
text = "Dialog Message"
color = "#333333"
}
image {
File.createTempFile("image", "jpg")
}
}
dialog.show()
Output
setting title text Dialog Title
setting title color #00000
showing title
setting message Dialog Message
setting message color #333333
showing message
showing image with size 0
showing dialog Dialog@5f184fc6
AlertDialogExample
One of the common examples of Builder pattern that we all use in our daily life is that of AlertDialog. In AleartDialog, we call only the required methods like:
AlertDialog.Builder(this)
.setTitle("This is a title")
.setMessage("This is some message")
.show()
2. Creational: Singleton Pattern
The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance. There are very few applications, do not overuse this pattern!
Example
object PrinterDriver {
init {
println("Initializing with object: $this")
}
fun print() = println("Printing with object: $this")
}
Start
Initializing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5
3. Creational: Factory Method Pattern
The factory pattern is used to replace class constructors, abstracting the process of object generation so that the type of the object instantiated can be determined at run-time.
Example
sealed class Country {
object USA : Country()
}
object Spain : Country()
class Greece(val someProperty: String) : Country()
data class Canada(val someProperty: String) : Country()
class Currency(
val code: String
)
object CurrencyFactory {
fun currencyForCountry(country: Country): Currency =
when (country) {
is Greece -> Currency("EUR")
is Spain -> Currency("EUR")
is Country.USA -> Currency("USD")
is Canada -> Currency("CAD")
}
}
Usage
val greeceCurrency = CurrencyFactory.currencyForCountry(Greece("")).code
println("Greece currency: $greeceCurrency")
val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA).code
println("USA currency: $usaCurrency")
assertThat(greeceCurrency).isEqualTo("EUR")
assertThat(usaCurrency).isEqualTo("USD")
Output
Greece currency: EUR
US currency: USD
4. Creational: Abstract Factory Pattern
The abstract factory pattern is used to provide a client with a set of related or dependant objects. The “family” of objects created by the factory are determined at run-time.
Example
interface Plant
class OrangePlant : Plant
class ApplePlant : Plant
abstract class PlantFactory {
abstract fun makePlant(): Plant
companion object {
inline fun <reified T : Plant> createFactory(): PlantFactory = when (T::class) {
OrangePlant::class -> OrangeFactory()
ApplePlant::class -> AppleFactory()
else -> throw IllegalArgumentException()
}
}
}
class AppleFactory : PlantFactory() {
override fun makePlant(): Plant = ApplePlant()
}
class OrangeFactory : PlantFactory() {
override fun makePlant(): Plant = OrangePlant()
}
Usage
val plantFactory = PlantFactory.createFactory<OrangePlant>()
val plant = plantFactory.makePlant()
println("Created plant: $plant")
Output
Created plant: OrangePlant@4f023edb
5. Structural: Adapter Pattern
The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the “adaptee” with a class that supports the interface required by the client.
Example
interface Temperature {
var temperature: Double
}
class CelsiusTemperature(override var temperature: Double) : Temperature
class FahrenheitTemperature(var celsiusTemperature: CelsiusTemperature) : Temperature {
override var temperature: Double
get() = convertCelsiusToFahrenheit(celsiusTemperature.temperature)
set(temperatureInF) {
celsiusTemperature.temperature = convertFahrenheitToCelsius(temperatureInF)
}
private fun convertFahrenheitToCelsius(f: Double): Double = (f - 32) * 5 / 9
private fun convertCelsiusToFahrenheit(c: Double): Double = (c * 9 / 5) + 32
}
Usage
val celsiusTemperature = CelsiusTemperature(0.0)
val fahrenheitTemperature = FahrenheitTemperature(celsiusTemperature)
celsiusTemperature.temperature = 36.6
println("${celsiusTemperature.temperature} C -> ${fahrenheitTemperature.temperature} F")
fahrenheitTemperature.temperature = 100.0
println("${fahrenheitTemperature.temperature} F -> ${celsiusTemperature.temperature} C")
Output
36.6 C -> 97.88000000000001 F
100.0 F -> 37.77777777777778 C
6. Structural: Facade Pattern
The facade pattern is used to define a simplified interface to a more complex subsystem.
Example
class ComplexSystemStore(val filePath: String) {
init {
println("Reading data from file: $filePath")
}
val store = HashMap<String, String>()
fun store(key: String, payload: String) {
store.put(key, payload)
}
fun read(key: String): String = store[key] ?: ""
fun commit() = println("Storing cached data: $store to file: $filePath")
}
data class User(val login: String)
//Facade:
class UserRepository {
val systemPreferences = ComplexSystemStore("/data/default.prefs")
fun save(user: User) {
systemPreferences.store("USER_KEY", user.login)
systemPreferences.commit()
}
fun findFirst(): User = User(systemPreferences.read("USER_KEY"))
}
Usage
val userRepository = UserRepository()
val user = User("coolmonktechie")
userRepository.save(user)
val resultUser = userRepository.findFirst()
println("Found stored user: $resultUser")
Output
Reading data from file: /data/default.prefs
Storing cached data: {USER_KEY=coolmonktechie} to file: /data/default.prefs
Found stored user: User(login=coolmonktechie)
7. Structural: Decorator Pattern
The decorator pattern is used to extend or alter the functionality of objects at run-time by wrapping them in an object of a decorator class. This provides a flexible alternative to using inheritance to change behaviour.
Example
interface CoffeeMachine {
fun makeSmallCoffee()
fun makeLargeCoffee()
}
class NormalCoffeeMachine : CoffeeMachine {
override fun makeSmallCoffee() = println("Normal: Making small coffee")
override fun makeLargeCoffee() = println("Normal: Making large coffee")
}
//Decorator:
class EnhancedCoffeeMachine(val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {
// overriding behaviour
override fun makeLargeCoffee() {
println("Enhanced: Making large coffee")
coffeeMachine.makeLargeCoffee()
}
// extended behaviour
fun makeCoffeeWithMilk() {
println("Enhanced: Making coffee with milk")
coffeeMachine.makeSmallCoffee()
println("Enhanced: Adding milk")
}
}
Usage
val normalMachine = NormalCoffeeMachine()
val enhancedMachine = EnhancedCoffeeMachine(normalMachine)
// non-overridden behaviour
enhancedMachine.makeSmallCoffee()
// overriding behaviour
enhancedMachine.makeLargeCoffee()
// extended behaviour
enhancedMachine.makeCoffeeWithMilk()
Output
Normal: Making small coffee
Enhanced: Making large coffee
Normal: Making large coffee
Enhanced: Making coffee with milk
Normal: Making small coffee
Enhanced: Adding milk
8. Structural: Composite Pattern
The composite pattern is used to compose zero-or-more similar objects so it can manipulate them as one object.
Example
open class Equipment(private var price: Int, private var name: String) {
open fun getPrice(): Int = price
}
/*
[composite]
*/
open class Composite(name: String) : Equipment(0, name) {
val equipments = ArrayList<Equipment>()
fun add(equipment: Equipment) {
this.equipments.add(equipment)
}
override fun getPrice(): Int {
return equipments.map { it.getPrice() }.sum()
}
}
/*
leafs
*/
class Cabbinet : Composite("cabbinet")
class FloppyDisk : Equipment(80, "Floppy Disk")
class HardDrive : Equipment(250, "Hard Drive")
class Memory : Equipment(280, "Memory")
Usage
var cabbinet = Cabbinet()
cabbinet.add(FloppyDisk())
cabbinet.add(HardDrive())
cabbinet.add(Memory())
println(cabbinet.getPrice())
Output
610
9. Structural: Protection Proxy Pattern
The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. Protection proxy is restricting access.
Example
interface File {
fun read(name: String)
}
class NormalFile : File {
override fun read(name: String) = println("Reading file: $name")
}
//Proxy:
class SecuredFile : File {
val normalFile = NormalFile()
var password: String = ""
override fun read(name: String) {
if (password == "secret") {
println("Password is correct: $password")
normalFile.read(name)
} else {
println("Incorrect password. Access denied!")
}
}
}
Usage
val securedFile = SecuredFile()
securedFile.read("readme.md")
securedFile.password = "secret"
securedFile.read("readme.md")
The observer pattern is used to allow an object to publish changes to its state. Other objects subscribe to be immediately notified of any changes.
Example
interface TextChangedListener {
fun onTextChanged(oldText: String, newText: String)
}
class PrintingTextChangedListener : TextChangedListener {
private var text = ""
override fun onTextChanged(oldText: String, newText: String) {
text = "Text is changed: $oldText -> $newText"
}
}
class TextView {
val listeners = mutableListOf<TextChangedListener>()
var text: String by Delegates.observable("<empty>") { _, old, new ->
listeners.forEach { it.onTextChanged(old, new) }
}
}
Usage
val textView = TextView().apply {
listener = PrintingTextChangedListener()
}
with(textView) {
text = "old name"
text = "new name"
}
Output
Text is changed <empty> -> old name
Text is changed old name -> new name
11. Behavioral: Command Pattern
The command pattern is used to express a request, including the call to be made and all of its required parameters, in a command object. The command may then be executed immediately or held for later use.
Example
interface OrderCommand {
fun execute()
}
class OrderAddCommand(val id: Long) : OrderCommand {
override fun execute() = println("Adding order with id: $id")
}
class OrderPayCommand(val id: Long) : OrderCommand {
override fun execute() = println("Paying for order with id: $id")
}
class CommandProcessor {
private val queue = ArrayList<OrderCommand>()
fun addToQueue(orderCommand: OrderCommand): CommandProcessor =
apply {
queue.add(orderCommand)
}
fun processCommands(): CommandProcessor =
apply {
queue.forEach { it.execute() }
queue.clear()
}
}
Adding order with id: 1
Adding order with id: 2
Paying for order with id: 2
Paying for order with id: 1
12. Behavioral: Strategy Pattern
The strategy pattern is used to create an interchangeable family of algorithms from which the required process is chosen at run-time.
Example
class Printer(private val stringFormatterStrategy: (String) -> String) {
fun printString(string: String) {
println(stringFormatterStrategy(string))
}
}
val lowerCaseFormatter: (String) -> String = { it.toLowerCase() }
val upperCaseFormatter = { it: String -> it.toUpperCase() }
Usage
val inputString = "OLD name NEW name "
val lowerCasePrinter = Printer(lowerCaseFormatter)
lowerCasePrinter.printString(inputString)
val upperCasePrinter = Printer(upperCaseFormatter)
upperCasePrinter.printString(inputString)
val prefixPrinter = Printer { "Prefix: $it" }
prefixPrinter.printString(inputString)
Output
old name new name
OLD NAME NEW NAME
Prefix: OLD name NEW name
13. Behavioral: State Pattern
The state pattern is used to alter the behaviour of an object as its internal state changes. The pattern allows the class for an object to apparently change at run-time.
Example
sealed class AuthorizationState
object Unauthorized : AuthorizationState()
class Authorized(val userName: String) : AuthorizationState()
class AuthorizationPresenter {
private var state: AuthorizationState = Unauthorized
val isAuthorized: Boolean
get() = when (state) {
is Authorized -> true
is Unauthorized -> false
}
val userName: String
get() {
val state = this.state //val enables smart casting of state
return when (state) {
is Authorized -> state.userName
is Unauthorized -> "Unknown"
}
}
fun loginUser(userName: String) {
state = Authorized(userName)
}
fun logoutUser() {
state = Unauthorized
}
override fun toString() = "User '$userName' is logged in: $isAuthorized"
}
Usage
val authorizationPresenter = AuthorizationPresenter()
authorizationPresenter.loginUser("admin")
println(authorizationPresenter)
authorizationPresenter.logoutUser()
println(authorizationPresenter)
Output
User 'admin' is logged in: true
User 'Unknown' is logged in: false
14. Behavioral: Chain of Responsibility Pattern
The chain of responsibility pattern is used to process varied requests, each of which may be dealt with by a different handler.
Example
interface HeadersChain {
fun addHeader(inputHeader: String): String
}
class AuthenticationHeader(val token: String?, var next: HeadersChain? = null) : HeadersChain {
override fun addHeader(inputHeader: String): String {
token ?: throw IllegalStateException("Token should be not null")
return inputHeader + "Authorization: Bearer $token\n"
.let { next?.addHeader(it) ?: it }
}
}
class ContentTypeHeader(val contentType: String, var next: HeadersChain? = null) : HeadersChain {
override fun addHeader(inputHeader: String): String =
inputHeader + "ContentType: $contentType\n"
.let { next?.addHeader(it) ?: it }
}
class BodyPayload(val body: String, var next: HeadersChain? = null) : HeadersChain {
override fun addHeader(inputHeader: String): String =
inputHeader + "$body"
.let { next?.addHeader(it) ?: it }
}
Usage
//create chain elements
val authenticationHeader = AuthenticationHeader("123456")
val contentTypeHeader = ContentTypeHeader("json")
val messageBody = BodyPayload("Body:\n{\n\"username\"=\"coolmonktechie\"\n}")
//construct chain
authenticationHeader.next = contentTypeHeader
contentTypeHeader.next = messageBody
//execute chain
val messageWithAuthentication =
authenticationHeader.addHeader("Headers with Authentication:\n")
println(messageWithAuthentication)
val messageWithoutAuth =
contentTypeHeader.addHeader("Headers:\n")
println(messageWithoutAuth)
The visitor pattern is used to separate a relatively complex set of structured data classes from the functionality that may be performed upon the data that they hold.
Example
interface ReportVisitable {
fun <R> accept(visitor: ReportVisitor<R>): R
}
class FixedPriceContract(val costPerYear: Long) : ReportVisitable {
override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}
class TimeAndMaterialsContract(val costPerHour: Long, val hours: Long) : ReportVisitable {
override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}
class SupportContract(val costPerMonth: Long) : ReportVisitable {
override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}
interface ReportVisitor<out R> {
fun visit(contract: FixedPriceContract): R
fun visit(contract: TimeAndMaterialsContract): R
fun visit(contract: SupportContract): R
}
class MonthlyCostReportVisitor : ReportVisitor<Long> {
override fun visit(contract: FixedPriceContract): Long =
contract.costPerYear / 12
override fun visit(contract: TimeAndMaterialsContract): Long =
contract.costPerHour * contract.hours
override fun visit(contract: SupportContract): Long =
contract.costPerMonth
}
class YearlyReportVisitor : ReportVisitor<Long> {
override fun visit(contract: FixedPriceContract): Long =
contract.costPerYear
override fun visit(contract: TimeAndMaterialsContract): Long =
contract.costPerHour * contract.hours
override fun visit(contract: SupportContract): Long =
contract.costPerMonth * 12
}
Usage
val projectAlpha = FixedPriceContract(costPerYear = 10000)
val projectGamma = TimeAndMaterialsContract(hours = 150, costPerHour = 10)
val projectBeta = SupportContract(costPerMonth = 500)
val projectKappa = TimeAndMaterialsContract(hours = 50, costPerHour = 50)
val projects = arrayOf(projectAlpha, projectBeta, projectGamma, projectKappa)
val monthlyCostReportVisitor = MonthlyCostReportVisitor()
val monthlyCost = projects.map { it.accept(monthlyCostReportVisitor) }.sum()
println("Monthly cost: $monthlyCost")
assertThat(monthlyCost).isEqualTo(5333)
val yearlyReportVisitor = YearlyReportVisitor()
val yearlyCost = projects.map { it.accept(yearlyReportVisitor) }.sum()
println("Yearly cost: $yearlyCost")
assertThat(yearlyCost).isEqualTo(20000)
Output
Monthly cost: 5333
Yearly cost: 20000
16. Behavioral: Mediator Pattern
Mediator design pattern is used to provide a centralized communication medium between different objects in a system. This pattern is very helpful in an enterprise application where multiple objects are interacting with each other.
Example
class ChatUser(private val mediator: ChatMediator, val name: String) {
fun send(msg: String) {
println("$name: Sending Message= $msg")
mediator.sendMessage(msg, this)
}
fun receive(msg: String) {
println("$name: Message received: $msg")
}
}
class ChatMediator {
private val users: MutableList<ChatUser> = ArrayList()
fun sendMessage(msg: String, user: ChatUser) {
users
.filter { it != user }
.forEach {
it.receive(msg)
}
}
fun addUser(user: ChatUser): ChatMediator =
apply { users.add(user) }
}
Usage
val mediator = ChatMediator()
val user1 = ChatUser(mediator, "User1")
mediator
.addUser(ChatUser(mediator, "User2"))
.addUser(ChatUser(mediator, "User3"))
.addUser(user1)
user1.send("Hello everyone!")
The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
Example
data class Memento(val state: String)
class Originator(var state: String) {
fun createMemento(): Memento {
return Memento(state)
}
fun restore(memento: Memento) {
state = memento.state
}
}
class CareTaker {
private val mementoList = ArrayList<Memento>()
fun saveState(state: Memento) {
mementoList.add(state)
}
fun restore(index: Int): Memento {
return mementoList[index]
}
}
Usage
val originator = Originator("initial state")
val careTaker = CareTaker()
careTaker.saveState(originator.createMemento())
originator.state = "State #1"
originator.state = "State #2"
careTaker.saveState(originator.createMemento())
originator.state = "State #3"
println("Current State: " + originator.state)
assertThat(originator.state).isEqualTo("State #3")
originator.restore(careTaker.restore(1))
println("Second saved state: " + originator.state)
assertThat(originator.state).isEqualTo("State #2")
originator.restore(careTaker.restore(0))
println("First saved state: " + originator.state)
Output
Current State: State #3
Second saved state: State #2
First saved state: initial state
In this article, we understood about why design patterns are valuable and most frequently used in Kotlin. This article shows the most frequently used design patterns in Kotlin with an authentic example.
Thanks for reading! I hope you enjoyed and learned about ValuableDesign Patterns concepts in Kotlin. 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 :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Animate Drawable Graphics In Android ?).
In this article, we will learn about how to animate drawable graphics in Android. In some situations, images need to be animated on screen. This is useful if :
we want to display a custom loading animation comprising several images, or
if one icon to morph into another after a user’s action.
Android provides a couple options for animating drawable as below :
Animation Drawable – This allows us to specify several static drawable files that will display one at a time to create an animation.
Animated Vector Drawable – This animates the properties of a vector drawable.
This article shows the animated images on screen in android using Drawable and Vector Drawable.
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.
1. Animation Drawable
One way to animate Drawables is to load a series of Drawable resources one after another to create an animation. This is a traditional animation in the sense that it creates with a sequence of different images. It played in order, like a roll of film. The AnimationDrawable class is the basis for Drawable animations.
While we can define the frames of an animation in our code, using the AnimationDrawable class API, it’s more accomplished with a single XML file that lists the frames that compose the animation. The XML file for this kind of animation belongs in the res/drawable/directory of our Android project. Here, the instructions are the order and duration for each frame of the animation.
The XML file comprises an <animation-list> element as the root node and a series of child <item> nodes that each define a frame: a drawable resource for the frame and the frame duration. Here’s an example XML file for a Drawable animation:
This animation runs for just three frames. By setting the android:oneshot attribute of the list to true, it will cycle just once, then stop and hold on the last frame. The animation will loop if set false. With this XML saved as rocket_thrust.xml in the res/drawable/ directory of the project, it can be added as the background image to a View and then called to play.
Here’s an example Activity, in which the animation is added to an ImageView and then animated when the screen is touched:
private lateinit var rocketAnimation: AnimationDrawable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
val rocketImage = findViewById<ImageView>(R.id.rocket_image).apply {
setBackgroundResource(R.drawable.rocket_thrust)
rocketAnimation = background as AnimationDrawable
}
rocketImage.setOnClickListener({ rocketAnimation.start() })
}
It’s important to note that the start() method called on the AnimationDrawable cannot be called during the onCreate() method of our Activity, because the AnimationDrawable does not fully attach to the window. If we want to play the animation immediately, without requiring interaction, then we might want to call it from the onStart()method in our Activity, which will get called when Android makes the view visible on screen.
2. Animated Vector Drawable
A vector drawable is a drawable that is scalable without getting Pixelated or blurry. The AnimatedVectorDrawable class (and AnimatedVectorDrawableCompat for backward-compatibility) lets us animate the properties of a vector drawable, such as rotating it or changing the path data to morph it into a different image.
We normally define animated vector drawables in three XML files:
A vector drawable with the <vector> element in res/drawable/.
An animated vector drawable with the <animated-vector> element in res/drawable/.
One or more object animators with the <objectAnimator> element in res/animator/.
Animated vector drawables can animate the attributes of the <group> and <path> elements. The <group> elements defines a set of paths or subgroups, and the <path> element defines paths to be drawn.
When we define a vector drawable that we want to animate, use the android:name attribute to assign a unique name to groups and paths, so we can refer to them from our animator definitions.
For instance, the second animator morphs the vector drawable’s path from one shape to another. Both paths are compatible for morphing, because they must have the same number of commands and the same number of parameters for each command.
In this article, we understood about how to animate drawable graphics in Android. This article shows the animated images on screen in android using Drawable and Vector Drawable.
Thanks for reading! I hope you enjoyed and learned about drawable graphics concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find Other articles of CoolMonkTechie as below link :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (The most popular best ways to Add Motion To UI).
In this article, we will learn about different best ways to Add Motion To UI using Android Animation.
When our UI changes in response to user action, we should animate the layout transitions. These animations give users feedback on their actions and help keep them oriented to the UI. Android includes the transitions framework, which enables us to easily animate changes between two view hierarchies. The framework animates the views at runtime by changing some of their property values. The framework includes built-in animations for common effects and lets us create custom animations and transition life cycle callbacks. This article shows the overview of different ways we can Add Motion to UI using Android Animation.
A famous quote about learning is :
” I am always ready to learn although I do not always like being taught. “
So Let’s begin.
Animation Overview
Animations can add visual cues that notify users about what’s going on in our app. They are especially useful when the UI changes state, such as when new content loads or new actions become available. Animations also add a polished look to your app, which gives it a higher quality look and feel. Android includes different animation APIs depending on what type of animation you want.
Different Ways To Add Motion To UI
Android provides the different ways to add motion to UI using Android animation:
1. Animate bitmaps
When we want to animate a bitmap graphic such as an icon or illustration, then we should use the drawable animation APIs. Usually, these animations define statically with a drawable resource, but we can also define the animation behavior at runtime.
For example, animating a play button transforming into a pause button when tapped is a pleasant way to communicate to the user that related with the two actions, and that pressing one makes the other visible.
2. Animate UI visibility and motion
When we need to change the visibility or position of views in our layout, we include subtle animations to help the user understand how the UI is changing.
To move, reveal, or hide views within the current layout, we can use the property animation system provided by the android.animation package, available in Android 3.0 (API level 11) and higher. These APIs update the properties of our View objects over time, continuously redrawing the view as the properties change.
For example, when we change the position properties, the view moves across the screen, or when we change the alpha property, the view fades in or out.
To create these animations with the least amount of effort, we can enable animations on our layout so that when we change the visibility of a view, an animation applies automatically.
Physics-based motion
Our animations are natural-looking if it apply real-world physics. For example, they should maintain momentum when their target changes, and make smooth transitions during any changes.
The Android Support library includes physics-based animation APIs to provide these behaviors that rely on the laws of physics to control how our animations occur.
There are Two common physics-based animations as :
Spring Animation – Physics-based motion drives by force. Spring force is one such force that guides interactivity and motion. A spring force has the following properties: damping and stiffness. In a spring-based animation, the value and the velocity calculate based on the spring force that applies on each frame.
Fling Animation – Fling-based animation uses a friction force proportional to an object’s velocity. Use it to animate a property of an object and to end the animation gradually. It has an initial momentum, which receives from the gesture velocity, and gradually slows down. The animation ends when the velocity of the animation is low enough that it makes no visible change on the device screen.
Animations are not based on physics — such as those built with ObjectAnimator APIs—are fairly static and have a fixed duration. If the target value changes, we need to cancel the animation at the time of target value change, re-configure the animation with a new value as the new start value, and add the new target value. Visually, this process creates an abrupt stop in the animation, and a disjointed movement afterwards, as shown in below figure:
Whereas, animations built by with physics-based animation APIs such as DynamicAnimation are driven by force. The change in the target value results in a change in force. The new force applies on the existing velocity, which makes a continuous transition to the new target. This process results in a more natural-looking animation, as shown in figure .
3. Animate layout changes
On Android 4.4 (API level 19) and higher, We can use the transition framework to create animations when we require swapping the layout within the current activity or fragment.
All we need to do is specify the starting and ending layout, and what type of animation we want to use. Then the system figures out and executes an animation between the two layouts. We can use this to swap out the entire UI or to move/replace just some views.
For example, when the user taps an item to see more information, we can replace the layout with the item details, applying a transition like the one shown in figure.
The starting and ending layout are each stored in a Scene, though the starting scene determines automatically from the current layout. We then create a Transition to tell the system what type of animation we want and then call TransitionManager.go() and the system runs the animation to swap the layouts.
4. Animate between activities
On Android 5.0 (API level 21) and higher, we can also create animations that transition between our activities. They base this on the same transition framework, but it allows us to create animations between layouts in separate activities.
We can apply simple animations such as sliding the new activity in from the side or fading it in, but we can also create animations that transition between shared views in each activity. For example, when the user taps an item to see more information, we can transition into a new activity with an animation that seamlessly grows that item to fill the screen.
As usual, we call startActivity(), but pass it a bundle of options provided by ActivityOptions.makeSceneTransitionAnimation(). This bundle of options may include which views are shared between the activities so the transition framework can connect them during the animation.
In this article, we understood about different best ways to add motions to UI using Android Animation. This article showed the overview of different ways to handle motion in Android UI.
Thanks for reading! I hope you enjoyed and learned about UI Motion 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 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 :
Hello Readers, CoolMonkTechie heartily welcomes you in this article (The most popular best ways to make REST API Calls in React Native ).
In this article, we will learn about different best ways to make REST API Calls in React Native. Many mobile apps need to load resources from a remote URL. We may want to make a POST request to a REST API, or we may need to fetch a chunk of static content from another server.
Applications on the web and mobile devices often cannot store all the information that they need. Therefore, they have to reach out to the web to update data, retrieve data, or request a service from a third-party. The transaction that takes place between two devices (client and server) is called an API call. An API (application programming interface) typically will use the REST design paradigm to manage all how it can be accessed (called).
This article shows the fetch function and the axios library. Both methods achieve the same task, making an HTTP call, but they go about it in slightly different ways.
A famous quote about learning is :
” The expert in anything was once a beginner. “
So Let’s begin.
Using Fetch
React Native provides the Fetch API for our networking needs. Fetch will seem familiar if we have used XMLHttpRequest or other networking APIs before. The Fetch API is a built-in Javascript function. We can use fetch by providing the URL for the location of our content. This will send a GET request to that URL.
Making Requests
In order to fetch content from an arbitrary URL, we can pass the URL to fetch:
fetch('https://mywebsite.com/mydata.json');
Fetch also takes an optional second argument that allows us to customize the HTTP request. We may want to specify additional headers, or make a POST request:
The above examples show how we can make a request. Most times, we will want to do something with the response.
Networking is an inherently asynchronous operation. Fetch methods will return a Promise that makes it straightforward to write code that works asynchronously:
By default, iOS will block any request that’s not encrypted using SSL. If we need to fetch from a cleartext URL (one that begins with http), then App Transport Security exception will first need to add.
It is more secure to add exceptions only for those domains, If we know ahead of time to need to access the domains; if we do not know the domains until runtime, ATS can completely disable.
This is a simple way to send HTTP requests as soon as possible. However, another popular method for sending HTTP requests is with third-party libraries.
Using XMLHttpRequest API
The XMLHttpRequest API is built into React Native. This means that we can use third-party libraries such as frisbee or axios that depend on it, or we can use the XMLHttpRequest API directly if we prefer.
var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
if (request.readyState !== 4) {
return;
}
if (request.status === 200) {
console.log('success', request.responseText);
} else {
console.warn('error');
}
};
request.open('GET', 'https://mywebsite.com/endpoint/');
request.send();
The security model for XMLHttpRequest differs from on web, as there is no concept of CORS in native apps.
Using WebSocket Protocal
React Native also supports WebSockets , a protocol which provides full-duplex communication channels over a single TCP connection.
var ws = new WebSocket('ws://host.com/path');
ws.onopen = () => {
// connection opened
ws.send('something'); // send a message
};
ws.onmessage = (e) => {
// a message was received
console.log(e.data);
};
ws.onerror = (e) => {
// an error occurred
console.log(e.message);
};
ws.onclose = (e) => {
// connection closed
console.log(e.code, e.reason);
};
Using Axios Library
The Axios is a Javascript library used to make HTTP requests, and it supports the Promise API that is native to JS ES6. The Axios library has grown in popularity alongside the increase in apps that exchange information using JSON. Three things that make the Axios library so useful are the ability to;
Intercept requests and responses
Transform request and response data
Automatically transform data for JSON
Axios Library Example
import Axios from 'axios'
Axios({
url: '/data',
method: 'get',
baseURL: 'https://example.com',
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data
return data;
}],
transformResponse: [function (data) {
// Do whatever you want to transform the data
return data;
}],
headers: {'X-Requested-With': 'XMLHttpRequest'},
data: {
name: 'Some Name'
},
})
In the above example, we have passed a configuration object that calls an example URL with a GET request. We’ll notice that some parameter names are common between fetch and Axios.
We are calling the same URL as we did with the fetch API, but we don’t have to manually transform the response object into JSON. Axios has more features that can be useful with bigger applications.
That’s all about in this article.
Conclusion
In this article, we understood about different best ways to make REST API Calls in React Native. This article showed the fetch and axios library usage with code snippets in React Native .
Thanks for reading! I hope you enjoyed and learned about REST API Calls concepts in React Native. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
You can find Other articles of CoolmonkTechie as below link :