Tag

androidblog

Browsing

In this blog, We’ll learn coroutines background processing by creating a small project, that simply displays an image to the UI.

Objective

  • Get Image from URL – So first we’re going to get an image from URL and obviously we can not do that on the main thread. So we need a background thread to do that. And we’re going to this with Coroutines.
  • Apply Processing – Next stuff is after downloading the image. We’re going to use another dispatcher to process that image. So here we’re going to apply a filter to that image. So that we transform it in a certain way. We’re going to convert it to black and white. We will do that in a separate coroutine for background processing.
  • Show it on ImageView – Finally, we’re going to show it on ImageView.

So it quite a simple idea, But the way we do it nicely. Because we are going to use coroutine for background processing. So while we are working live project we can understand how we can perform background processing with the bits of help of Coroutines.

Requirements

Importantly I want to mention here that there are some requirements for this example.

  • Android Studio – First of all you have to AndroidStudio installed in your machine.
  • Android Knowledge – You would have some Android development knowledge.

All right just go ahead and jump into the Android Studio.

Let’s create a new project with basic template. So first of all, I’m going to create a basic interface and you not have to worry about it, you can download the source code from bottom of this blog.

Add permission

Obviously, first thing we are going to download an image from server so we have to require Internet permission. So let open the android manifest add it.

    <uses-permission android:name="android.permission.INTERNET" />

I’m going to use this URL here, So this is what we will downloading and this is what we will processing.

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="500dp"
        android:layout_height="500dp"
        android:scaleType="fitCenter"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"
        app:srcCompat="@mipmap/ic_launcher" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Alright moving on we have a layout that has basically two elements. It has ProgressBar that will just show that something is going on in IO dispatcher. and it has an ImageView that is currently hidden. So this will become visible when we actually have something to display. and then we’ll hide the progressbar.

MainActivity

Create Filter

package com.backgroundprocessingexample

import android.graphics.Bitmap
import android.graphics.Color

object Filter {

    fun apply(source: Bitmap): Bitmap {
        val width = source.width
        val height = source.height
        val pixels = IntArray(width * height)
        // get pixel array from source
        source.getPixels(pixels, 0, width, 0, 0, width, height)

        var R: Int
        var G: Int
        var B: Int
        var index: Int
        var threshHold: Int

        for (y in 0 until height) {
            for (x in 0 until width) {
                // get current index in 2D-matrix
                index = y * width + x
                // get color
                R = Color.red(pixels[index])
                G = Color.green(pixels[index])
                B = Color.blue(pixels[index])
                val grey = (R + G + B) / 3
                pixels[index] = Color.rgb(grey, grey, grey)
            }
        }

        // output bitmap
        val bitmapOut = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
        bitmapOut.setPixels(pixels, 0, width, 0, 0, width, height)
        return bitmapOut
    }
}

one things that I added here it is the Filter. Now this filter has a function fun apply(source: Bitmap) on a bitmap and it’s simply converted to a bitmap and It’s simply converted to a bitmap out to a another image.

What this filter does, It converts images to black & white. It does handle some processing because it goes pixel by pixel to convert it into black & white. That will take some processing power. So that a background job, So we can apply our coroutines to it. We can’t do it on the main thread or It should not do this in main thread.

    // Add Coroutines Dependency
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'

So here in the dependency first of all go ahead and copy the above dependencies. After adding these dependencies let go ahead and sync the project.

Now let’s open the MainActivity, So first i want to load image here from URL to local bitmap. then we want to put that bitmap in our ImageView. So I’m creating a kotlin function.

    private fun getImageFromUrl(): Bitmap =
            URL(IMAGE_URL).openStream().use {
                BitmapFactory.decodeStream(it)
            }

    private fun loadImage(bmp: Bitmap) {
        progressBar.visibility = View.GONE
        imageView.setImageBitmap(bmp)
        imageView.visibility = View.VISIBLE
    }

So this function simply returns a bitmap of whatever is returned from decode stream. Now Important thing is this is a network call. So we can not do that in main thread. So what we’ll like to do is we would like to open up a coroutines scope and do that call inside the scope on the IO dispatcher.

 coroutineScope.launch {
            val remoteImageDeferred = coroutineScope.async(Dispatchers.IO) {
                getImageFromUrl()
            }
            val imageBitmap = remoteImageDeferred.await()
            //loadImage(imageBitmap)
            launch(Dispatchers.Default) {
             val filterBitmap =   Filter.apply(imageBitmap)
                withContext(Dispatchers.Main) {
                    loadImage(filterBitmap)
                }

               // Log.i(MainActivity.TAG, "Default. Thread: ${Thread.currentThread().name}")
            }
        }

So let create a coroutines scope, here I’m going to open the main scope. Because we have to update UI from IO. Otherwise, we can’t update UI from another thread, It has to from main thread.

So the final output is

package com.backgroundprocessingexample

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import java.net.URL

class MainActivity : AppCompatActivity() {

    private val IMAGE_URL = "http://androidwave.com/wp-content/uploads/2018/12/useful-tools-for-logging-debugging-in-android.png.webp"
    private val coroutineScope = CoroutineScope(Dispatchers.Main)


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        coroutineScope.launch {
            val remoteImageDeferred = coroutineScope.async(Dispatchers.IO) {
                getImageFromUrl()
            }
            val imageBitmap = remoteImageDeferred.await()
            //loadImage(imageBitmap)
            launch(Dispatchers.Default) {
             val filterBitmap =   Filter.apply(imageBitmap)
                withContext(Dispatchers.Main) {
                    loadImage(filterBitmap)
                }

               // Log.i(MainActivity.TAG, "Default. Thread: ${Thread.currentThread().name}")
            }
        }

    }

    private fun getImageFromUrl(): Bitmap =
            URL(IMAGE_URL).openStream().use {
                BitmapFactory.decodeStream(it)
            }

    private fun loadImage(bmp: Bitmap) {
        progressBar.visibility = View.GONE
        imageView.setImageBitmap(bmp)
        imageView.visibility = View.VISIBLE
    }
}

Conclusion

That all, this way you can perform background jobs using coroutines in Kotlin. Keep in touch for more coroutines tutorials

In this article, I will show, how to create a CardView in React Native, Which will work perfectly in android and iOS both. So let’s started.

Objectives

Basically, the purpose of the example is to design and CardView component in React Native and use it whenever we need it. Why it should component? The reason is we can use this CardView in other parts of the application. We might have a different item that we want to present in CardView.

Let suppose you’re building an app, how have to show products inside the card on the main screen. Now avoid repeating cardview style definition agin and again. We can create a separate component. Let create a new react native project.

Create a react native application

let move to IDE, create a new react native project and create separate package named is components. Here I’m creating a new file here name it Card.js. The name is up to you

import React from 'react';
import { View, StyleSheet } from 'react-native';

const Card = props => {};

export default Card;
 

So here I simply created a function component named is Card.js and export as default. Now it’s all about presentation here, So let style this card view

import React from 'react';
import { View, StyleSheet } from 'react-native';

const Card = props => {
  return (
    <View style={{ ...styles.card, ...props.style }}>{props.children}</View>
  );
};

const styles = StyleSheet.create({
  card: {
    shadowColor: 'black',
    shadowOffset: { width: 0, height: 2 },
    shadowRadius: 6,
    shadowOpacity: 0.26,
    elevation: 8,
    backgroundColor: 'white',
    padding: 20,
    borderRadius: 10
  }
});

export default Card;
  

That all, our components is ready for use. Let import this component in main app

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';

import {Colors} from 'react-native/Libraries/NewAppScreen';
import Card from './src/components/Card';

const App: () => React$Node = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={styles.container}>
        <Card style={styles.card}>
          <Text style={styles.sectionTitle}>Basic CardView Example</Text>
        </Card>
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    margin: 16,
    alignItems: 'center', // Centered horizontally
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: Colors.white,
  },
  card: {
    height: 200,
    width: '100%',
    backgroundColor: '#f18484',
    justifyContent: 'center', //Centered vertically
    alignItems: 'center', // Centered horizontally
  },
});

export default App;

In this blog, we will learn how to use retrofit with coroutines Kotlin in android application, with the help of an example. So we are actually building an application in android using Coroutines, Retrofit and MVVM, and so on. So lets started.

Objectives

Basically, the purpose of the project is to get the list of users information from the endpoints and display it to users in RecyclerView. I’m listing them below of these are objectives of this Retrofit coroutines example blog.

  • Get a list of user’s info from an endpoint using REST API. Basically
  • Use coroutines with Retrofit and MVVM
  • Display the user’s info in a RecyclerView

So the main idea is to show you, how you can build a professional application using a proper architecture with Retrofit and using coroutines to get some information from a webserver. so that is what we are building.

Requirements

Now, there are some requirements for this retrofit coroutines example app. These are listed below

  • Android knowledge
  • Retrofit
  • MVVM

So idea is, I’m not going to details of these, you have to basic idea of these items. You should know how retrofit works with MVVM. I’m going to focus, this example on coroutines that work with Retrofit and MVVM.

All right guys, so lets started quickly with this example app. Open AndroidStudio and create a new android project with some template.

Setting up the project

Let wait for sync of project, that recently you created. And first of all let’s add internet permission in AndroidManifest that is obvious, Because we are communicating with backend APIs. So we do need that

    <uses-permission android:name="android.permission.INTERNET"/>

All right then next step is we have to add below dependency in app build.gradle.

  • RecyclerView
  • Glide
  • Retrofit
  • Lifecycle extensions
  • Coroutines
    // RecyclerView dependency
    implementation "androidx.recyclerview:recyclerview:1.1.0"
    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
    // Glide dependency
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    // Retrofit dependency
    implementation 'com.squareup.retrofit2:retrofit:2.7.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
    // Lifecycle dependency
    implementation 'android.arch.lifecycle:extensions:1.1.1'
    // Coroutines dependency
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

Backend API Endpoint and JSON

So first of all, we require a REST API, that we have to consume in this project. You can use whatever you required. For now, I’m consuming this URL and json response is

{
  "page": 1,
  "per_page": 6,
  "total": 12,
  "total_pages": 2,
  "data": [
    {
      "id": 1,
      "email": "george.bluth@reqres.in",
      "first_name": "George",
      "last_name": "Bluth",
      "avatar": "https://reqres.in/img/faces/1-image.jpg"
    },
    {
      "id": 2,
      "email": "janet.weaver@reqres.in",
      "first_name": "Janet",
      "last_name": "Weaver",
      "avatar": "https://reqres.in/img/faces/2-image.jpg"
    },
    {
      "id": 3,
      "email": "emma.wong@reqres.in",
      "first_name": "Emma",
      "last_name": "Wong",
      "avatar": "https://reqres.in/img/faces/3-image.jpg"
    }
    .
    .
    .
  ]
}

Create user Model

Now, we are following the MVVM design pattern in this coroutines example. So I’m creating a data class for parsing above JSON resonse. Let create a data class named is UserList.kt

package com.retrofitcoroutines.example.model

import com.google.gson.annotations.SerializedName

data class UserList(
    @SerializedName("page") val page: Int,
    @SerializedName("per_page") val per_page: Int,
    @SerializedName("total") val total: Int,
    @SerializedName("total_pages") val total_pages: Int,
    @SerializedName("data") val data: List<User>
)
 

Let’s create another data class named User.kt

package com.retrofitcoroutines.example.model

import com.google.gson.annotations.SerializedName

data class User(
    @SerializedName("id") val id : Int,
    @SerializedName("email") val email : String,
    @SerializedName("first_name") val first_name : String,
    @SerializedName("last_name") val last_name : String,
    @SerializedName("avatar") val avatar : String
)

Update the activity_main.xml

Now in main_activity has below layout, So we have a RecyclerView, then TextView in case of error, we can display a message. And we have a ProgressBar, so user know that is something is going in background. So its quite simple you know. So there are 3 element and we’re going to hide to show these element.

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

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/usersList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/listError"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:gravity="center"
        tools:text="Error"
        android:textColor="#FA1302"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/loadingView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout> 

Create a ViewModel

Let create a ViewModel named is ListViewModel. Here we simply fetching user list from backend server and passes that data to our MutableLiveData. Apart from this, we have a load error which generates an error message in case of API failure. and we have a loading MutableLiveData(), That let us know there is something happens in background.

package com.retrofitcoroutines.example.viewmodel

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.retrofitcoroutines.example.model.User
import com.retrofitcoroutines.example.remote.UserService
import kotlinx.coroutines.*

class ListViewModel : ViewModel() {

    val userService = UserService().getUsersService()
    var job: Job? = null
    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        onError("Exception handled: ${throwable.localizedMessage}")
    }

    val users = MutableLiveData<List<User>>()
    val usersLoadError = MutableLiveData<String?>()
    val loading = MutableLiveData<Boolean>()

    fun refresh() {
        fetchUsers()
    }

    private fun fetchUsers() {
        loading.value = true
        job = CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
            val response = userService.getUsers()
            withContext(Dispatchers.Main) {
                if (response.isSuccessful) {
                    users.value = response.body()?.data
                    usersLoadError.value = null
                    loading.value = false
                } else {
                    onError("Error : ${response.message()} ")
                }
            }
        }
        usersLoadError.value = ""
        loading.value = false
    }

    private fun onError(message: String) {
        usersLoadError.value = message
        loading.value = false
    }

    override fun onCleared() {
        super.onCleared()
        job?.cancel()
    }

} 

Connect ListViewModel to MainActivity

In the MainActivity we are instantiating the ListViewModel. Then we simply set up our RecycleView, Using a UserAdapter, That we’ll create below of the code, and then we are observing the ViewModel, which simply connecting Livedata from the ViewModel and proceeding to update the interface based on the information that we received. So all is that we need to do.

package com.retrofitcoroutines.example.view

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.retrofitcoroutines.example.R
import com.retrofitcoroutines.example.viewmodel.ListViewModel
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    lateinit var viewModel: ListViewModel
    private val userListAdapter = UserListAdapter(arrayListOf())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java)
        viewModel.refresh()

        usersList.apply {
            layoutManager = LinearLayoutManager(context)
            adapter = userListAdapter
        }

        observeViewModel()
    }
    private fun observeViewModel() {
        viewModel.users.observe(this, Observer {countries ->
            countries?.let {
                usersList.visibility = View.VISIBLE
                userListAdapter.updateCountries(it) }
        })

        viewModel.usersLoadError.observe(this, Observer { isError ->
            listError.visibility = if(isError == "") View.GONE else View.VISIBLE
        })

        viewModel.loading.observe(this, Observer { isLoading ->
            isLoading?.let {
                loadingView.visibility = if(it) View.VISIBLE else View.GONE
                if(it) {
                    listError.visibility = View.GONE
                    usersList.visibility = View.GONE
                }
            }
        })
    }
}

Now create UserListAdapter

Simply create a RecyclerView Adapter and attached the user_item view to recycler as usual. The only different thing here is the extension function for ImageView to load the image which allows us to loan an image on ImageView based on the URL we have.

package com.retrofitcoroutines.example.view

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.retrofitcoroutines.example.R
import com.retrofitcoroutines.example.model.User
import com.retrofitcoroutines.example.utils.loadImage
import kotlinx.android.synthetic.main.item_user.view.*

class UserListAdapter(var users: ArrayList<User>): RecyclerView.Adapter<UserListAdapter.UserViewHolder>() {

    fun updateCountries(newUsers: List<User>) {
        users.clear()
        users.addAll(newUsers)
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, p1: Int) = UserViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false)
    )

    override fun getItemCount() = users.size

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        holder.bind(users[position])
    }

    class UserViewHolder(view: View): RecyclerView.ViewHolder(view) {

        private val imageView = view.userAvatar
        private val userName = view.userFullName
        private val userEmail = view.userEmail


        fun bind(user: User) {
            userName.text = user.first_name + " "+ user.last_name
            userEmail.text = user.email
            imageView.loadImage(user.avatar)
        }
    }
}

Here is item_user.xml

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

    <ImageView
        android:id="@+id/userAvatar"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher_round" />

    <TextView
        android:id="@+id/userFullName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:textColor="@color/colorPrimaryDark"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/userAvatar"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Morris" />

    <TextView
        android:id="@+id/userEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        tools:text="morris@androidwave.com"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/userAvatar"
        app:layout_constraintTop_toBottomOf="@+id/userFullName" />
</androidx.constraintlayout.widget.ConstraintLayout>

Write a extension function for ImageView

Here I have created a new package named is utils and writes below extension function.

package com.retrofitcoroutines.example.utils

import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.retrofitcoroutines.example.R

fun ImageView.loadImage(uri: String?) {
    val options = RequestOptions()
        .error(R.mipmap.ic_launcher_round)
    Glide.with(this.context)
        .setDefaultRequestOptions(options)
        .load(uri)
        .into(this)
}
  

That all about UI let setup Retrofit and connect it with project.

Setting up Retrofit

Let create a retrofit interface. Here I’m gonna set up a GET function for Retrofit. Inside this interface create a suspending function like below. It simply returns us a Response.

package com.retrofitcoroutines.example.remote

import com.retrofitcoroutines.example.model.UserList
import retrofit2.Response
import retrofit2.http.GET

interface UserApi {

    @GET("users")
    suspend fun getUsers(): Response<UserList>

} 

Create a UsersService

In this class, first of I have created a variable here is BASE_URL. After that I have created a function named is getUsersService() that simply returns UserApi that we just defined earlier.

package com.retrofitcoroutines.example.remote

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class UserService {
    
    private val BASE_URL = "https://reqres.in/api/";
    fun getUsersService(): UserApi{
       return Retrofit.Builder()
           .baseUrl(BASE_URL)
           .addConverterFactory(GsonConverterFactory.create())
           .build()
           .create(UserApi::class.java)
    }
}

That all done, let’s go ahead and run the code and see if it works. It’s working so we have all users that return from APIs in the context of coroutines. Basically, in this example, we have two coroutines because first, we are calling with Dispatchers.IO and finally we are calling Dispatchers.Main. So way we can update UI in main. thread. That code I already added in ViewModel, for reference I adding again

CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
            val response = userService.getUsers()
            withContext(Dispatchers.Main) {
                if (response.isSuccessful) {
                    users.value = response.body()?.data
                    usersLoadError.value = null
                    loading.value = false
                } else {
                    onError("Error : ${response.message()} ")
                }
            }
        } 

Conclusion

So that is it, So we learned how to use Retrofit with coroutines in Kotlin| Android.  I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.

Download Source Code

Retrofit coroutines error handling

will publish a complete article on that, For now you can follow this article

Retrofit with coroutines

This is a sample app thats created. Please follow this article only

Hello folks! In this article, We’ll learn the best practices of SharedPreferences in Android(Kotlin). I’ll show you, how we can store and retrieve values in SharedPreferences. For better understanding, I’ll create an android SharedPreferences example (Sample app) and take an example of Writing and Reading Values in SharedPreferences. So let started

What are SharePreferences?

SharedPreferences is an android API that stores app data using key-value pairs and provides simple methods to read and write them.

Android system provides several options for you to save your data. These options are App-specific storage, Shared storage, Preferences, Databases. SharePreferences comes in Preference. It offers a framework to save private, primitive data in key-value pairs.

Why SharePreferences?

They’re mostly used to store user states when it comes to user settings or keeps a piece of small information(User details etc.) without needing storage permission. As per my opinion, you should store small primitive value in Preferences such as booleans, ints, longs, floats, and strings.

Modes in SharePreferences

SharedPreferences have different MODE these are below

1. Create a android application

Let take an example, open android studio, and create a new project. Now create a interface named is IPreferenceHelper. In this Interface we are defining some getter setter for storing or retrieving preference value. such as ApiKey and UserId etc.

package com.sharedpreferencesexample

interface IPreferenceHelper {

    fun setApiKey(apiKey: String)

    fun getApiKey(): String

    fun setUserId(userId: String)

    fun getUserId(): String

    fun clearPrefs()

}

2. Create a singleton class for managing Preferences

Ideally SharedPreferences store the app level value, So the SharedPreferences instance should be single threw out the app. It should be a singleton. Let create a Singletone class named is PreferenceManager and implement IPreferenceHelper. Just like below

open class PreferenceManager constructor(context: Context) : IPreferenceHelper {
    private val PREFS_NAME = "SharedPreferenceDemo"
    private var preferences: SharedPreferences

    init {
        preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
    }

    override fun setApiKey(apiKey: String) {
        preferences[API_KEY] = apiKey
    }

    override fun getApiKey(): String {
        return preferences[API_KEY] ?: ""
    }

    override fun setUserId(userId: String) {
        preferences[USER_ID] = userId
    }

    override fun getUserId(): String {
        return preferences[USER_ID] ?: ""
    }

    override fun clearPrefs() {
        preferences.edit().clear().apply()
    }

    companion object {
        const val API_KEY = "api_key"
        const val USER_ID = "user_id"
    }
}

3. Write into your SharedPreferences

Normally, writing into SharedPreferences is simple. But I’m going to write a Kotlin extension, This extension is make writing very simple and reduce boilerplate code.

/**
 * SharedPreferences extension function, to listen the edit() and apply() fun calls
 * on every SharedPreferences operation.
 */
private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
    val editor = this.edit()
    operation(editor)
    editor.apply()
}

/**
 * puts a key value pair in shared prefs if doesn't exists, otherwise updates value on given [key]
 */
private operator fun SharedPreferences.set(key: String, value: Any?) {
    when (value) {
        is String? -> edit { it.putString(key, value) }
        is Int -> edit { it.putInt(key, value) }
        is Boolean -> edit { it.putBoolean(key, value) }
        is Float -> edit { it.putFloat(key, value) }
        is Long -> edit { it.putLong(key, value) }
        else -> throw UnsupportedOperationException("Not yet implemented")
    }
}

4.  Read from SharedPreferences

Reading a value from SharedPreferences is also straightforward. I’m going to write another useful extension that provides more control over retrieving a value from SharedPreferences. Let check below code

/**
 * finds value on given key.
 * [T] is the type of value
 * @param defaultValue optional default value - will take null for strings, false for bool and -1 for numeric values if [defaultValue] is not specified
 */
private inline operator fun <reified T : Any> SharedPreferences.get(
    key: String,
    defaultValue: T? = null
): T? {
    return when (T::class) {
        String::class -> getString(key, defaultValue as? String) as T?
        Int::class -> getInt(key, defaultValue as? Int ?: -1) as T?
        Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
        Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
        Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
        else -> throw UnsupportedOperationException("Not yet implemented")
    }
}

5. Finally your PreferenceManager class look like below

package com.sharedpreferencesexample

import android.content.Context
import android.content.SharedPreferences

open class PreferenceManager constructor(context: Context) : IPreferenceHelper {
    private val PREFS_NAME = "SharedPreferenceDemo"
    private var preferences: SharedPreferences

    init {
        preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
    }

    override fun setApiKey(apiKey: String) {
        preferences[API_KEY] = apiKey
    }

    override fun getApiKey(): String {
        return preferences[API_KEY] ?: ""
    }

    override fun setUserId(userId: String) {
        preferences[USER_ID] = userId
    }

    override fun getUserId(): String {
        return preferences[USER_ID] ?: ""
    }

    override fun clearPrefs() {
        preferences.edit().clear().apply()
    }

    companion object {
        const val API_KEY = "api_key"
        const val USER_ID = "user_id"
    }
}


/**
 * SharedPreferences extension function, to listen the edit() and apply() fun calls
 * on every SharedPreferences operation.
 */
private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
    val editor = this.edit()
    operation(editor)
    editor.apply()
}

/**
 * puts a key value pair in shared prefs if doesn't exists, otherwise updates value on given [key]
 */
private operator fun SharedPreferences.set(key: String, value: Any?) {
    when (value) {
        is String? -> edit { it.putString(key, value) }
        is Int -> edit { it.putInt(key, value) }
        is Boolean -> edit { it.putBoolean(key, value) }
        is Float -> edit { it.putFloat(key, value) }
        is Long -> edit { it.putLong(key, value) }
        else -> throw UnsupportedOperationException("Not yet implemented")
    }
}

/**
 * finds value on given key.
 * [T] is the type of value
 * @param defaultValue optional default value - will take null for strings, false for bool and -1 for numeric values if [defaultValue] is not specified
 */
private inline operator fun <reified T : Any> SharedPreferences.get(
    key: String,
    defaultValue: T? = null
): T? {
    return when (T::class) {
        String::class -> getString(key, defaultValue as? String) as T?
        Int::class -> getInt(key, defaultValue as? Int ?: -1) as T?
        Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
        Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
        Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
        else -> throw UnsupportedOperationException("Not yet implemented")
    }
}

6. Access PreferenceManager into your presentation layers

Yes, Now your PreferenceManager class is ready for use. You can initialize PreferenceManager class into your ViewModel and Activity/Fragment, Make sure the context should be applicationContext.

7. Now open the activity_main.xml and paste below code.

For making interesting example I have added two edit text and one button in this layout

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

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Hello World!" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="User Id"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="API Key"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Show Preference Data"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText2" />

</androidx.constraintlayout.widget.ConstraintLayout>

8. Let access PreferenceManager class in your source file

Let’s check the below code This way you can easily read and write the value in SharePreferences

package com.sharedpreferencesexample

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val preferenceHelper: IPreferenceHelper by lazy { PreferenceManager(applicationContext) }

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // display saved data
        textView.text =
            " API Key - > ${preferenceHelper.getApiKey()} \n User Id -> ${preferenceHelper.getUserId()}"
        button.setOnClickListener {
            // Update data in SharedPreference
            preferenceHelper.setApiKey(editText.text.toString())
            preferenceHelper.setUserId(editText2.text.toString())
            // display saved data
            textView.text =
                " API Key - > ${preferenceHelper.getApiKey()} \n User Id -> ${preferenceHelper.getUserId()}"
        }

    }
}

7. Build & Test

Let run the app, in a movement your app will up and run, Now enter few value these EditText and click button. The value will display on TextView. This way you can read and write small amount of data in key-value pairs!

Conclusion

In this android SharedPreferences example, we’ll learn how we can save and retrieve value in SharedPreferences. I try to follow best practices for android development. Still, you want to improve… Most Welcome

Follow this article to detect network change in Android!

In this article, I’ll show you how you adding firebase to android app. We’ll discuss the possible options and recommended ways of it. Apart from this, I will tell you preliminary information for Firebase integration. For doing that, we’ll create a sample app and will add the firebase into it. So let’s started

Prerequisite

The following preliminary information that required for Firebase

  • One Google account
  • One real device or emulator for running the sample app
  • The latest Android Studio version (1.5 or later)
  • The app must use gradle 4.1 or later
There are two options for adding firebase into your project.
  • Firebase Assistant in Android Studio
    • Prebuild tool available in Android Studio
    • Using Assistant you can connect existing project or create new ones for you.
    • Assistant is automatically added and install all necessary dependencies
  • Add Firebase to your app via a firebase console manually.
    • This way you have to do all operations manually

Note – Google recommended manual way to add firebase in your app.

Create a project in Firebase console

Let’s open web browse and open firebase console. Click on add project -> enter the project name -> If you need analytics then check this option otherwise you can uncheck it. Finally, click on create a project.

Go to add firebase into android platform

Open this project and click on android icon, Then below window pop up. The big thing here is the package name. This package name has to match android app package name.

The next step is to download google-services.json file

You have to download this file and put into your app

Put this json file into app folder in your app

It’s a really important step. So let’s open your app folder -> navigate app folder – paste inside the app google-services.json. After adding it click on next.

Create a project in Android Studio with the same package name.

Let’s move to Android Studio create a new project with package name com.sampleapp. Now paste the google-services.json file in your app folder. just like below screenshot.

Add firebase SDK
Modify project-level build.gradle in Android Studio and add SDK classpath
  dependencies {
    // Add this line
    classpath 'com.google.gms:google-services:4.3.2'
  }
Open the app-level.gradle file. (<project>/<app-module>/build.gradle)
// Add this line
apply plugin: 'com.google.gms.google-services'

After modifying these files sync the project. Now your firebase setup is used. Firebase provides so many libraries that we’ll learn in the coming tutorial.

Learn our firebase android tutorial series