Jetpack is an Android library collection that incorporates best practices and provides backward compatibility in your Android apps.
The Jetpack app architecture guide gives you an overview of best practices and recommended architecture to think about when you design your Android app.
Create a new Kotlin Android project and call it Post App. Open the project’s build.gradle file and add the Google() repository as shown below:
1
allprojects {
repositories {
google()jcenter()
Project overview
In this article, we are going to build an Android application that fetches a list of posts from
jsonplaceholder API
. The application will make use of;
Kotlin as the programming language
Retrofit to connect to the post API
Kotlin coroutine for background processes
Data binding to bind views to a data source
LiveData and ViewModel
Navigation component
By the end of this article, we should have an Android application that looks like this:
Project setup
We need to add the following dependency to our project. Open the project’s build.gradle file and add the following inside the build script.
Since the app will need internet connection, don’t forget to add the internet permission in the manifest.
<uses-permission android:
name
=
"android.permission.INTERNET"
/>
The network layer
Create a “network” package inside the app folder. Create a Kotlin data class for the post object to look like this:
1
dataclassPost(
val userId: Int,
val id: Int,
val title: String,
val body: String)
Take note that the post class properties map to the ones from the
jsonplaceholder API
. Also note that we use data class here to take advantage of all the functionalities Kotlin data class provides.
Now create a PostApiService.kt file that will contain the mechanism by which the app will connect to the server using retrofit. The file content should look like this:
1
privateconstval BASE_POST_URL = "https://jsonplaceholder.typicode.com"privateval moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
privateval retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_POST_URL)
.build()
// Interface that defines how Retrofit talks to the web server using HTTP requestsinterfacePostApiService{
@GET("posts")suspendfungetPosts(): List < Post >
object PostApi {
val retrofitService: PostApiService by lazy {
retrofit.create(PostApiService::class.java)
We first create the instance of moshi with the builder pattern, which is used in converting the JSON string into Kotlin objects. Then, create a retrofit instance with the builder pattern and add a converterFactory and our base url. Then, finally, we call the build method.
Next, we create an interface that defines how retrofit talks to the web server using HTTP requests and returns a suspend function from the getPosts() method. Note that returning a suspend function is only possible from retrofit 2.6.0 and above.
Then finally we use the Kotlin object class to provide a single instance of the PostApiService and lazily initialize it (i.e initializing on-demand).
We are now done with the network layer and will move to the overview screen.
Databinding, ViewModel, and LiveData
Before we start working with the overview screen, let’s first enable data binding. Open the app-level build.gradle and enable the data-binding under the Android section as demonstrated below.
1
android {
dataBinding {
enabled = true
Create a package called “post_overview” and create a fragment called “PostsOverviewFragment” also create a view model for the fragment and call it “PostsOverviewViewModel”. Then edit your “posts_overview_fragment.xml” to look like this.
The adapter has field data which holds a list of posts and we override the setter to call notifyDataSetChanged() whenever this list is assigned. As a contract for extending the RecyclerView.Adapter, we need to override the:
OnCreateViewHolder
getItemCount
onBindViewHolder
Next, we implement our PostsOverviewViewModel class as follows:
1
classPostsOverviewViewModel: ViewModel() {
privateval _posts = MutableLiveData < List < Post >> ()
val post: LiveData < List < Post >>
get() = _posts
init {
getPosts()
privatefungetPosts() {
viewModelScope.launch {
val getDeferredPosts = PostApi.retrofitService.getPosts()
try {
val listResult = getDeferredPosts
if (listResult.isNotEmpty()) {
_posts.value = listResult
} catch (e: Exception) {
_posts.value = ArrayList()
PostsOverviewViewModel extends the ViewModel class and has a private field _posts which holds a MutableLiveData of
List
<
Post
>
. In order to provide the list of posts to outside classes, we declare other public property posts with exposes _posts as a non mutable LiveData. We then call the
getPosts
()
method in the
init
{}
block. This method is responsible for launching the coroutine within the viewmodelScope, calling the suspend function from the
PostApi
.
retrofitService.get
Posts()
and assigning the listResult to the _post.value.
Now it’s about time we moved to the PostsOverviewFragment class. The class should look like this.
We create a lateinit var binding to hold the PostsOverviewFragmentBinding class which was automatically created for us by the data binding. We also create a postViewModel by lazy from ViewModelProviders, then we set the lifecycle owner to the fragment with the call
binding.setLifecycleOwner(
this
)
, we create a PostListAdapter and assign it to the binding.postsList.adapter.
We finally set an observer to observe the
LiveData<
List
<
Post
>>
from the view model and assign emitted lists of posts to the postListAdapter.data
Navigation component
Up to this point, if you run the app it should still display the “Hello world” template. This is because we have not set up the navigation.
Create a navigation resource file inside the navigation folder in the res folder as shown below.
The navigation.xml will probably display an error because it needs a NavHostFragment to be associated with. We will resolve this error now.
Open your activity_main.xml, delete the Hello world TextView and add the
<fragment/>
layout as shown below.
Note that the Android:name attribute of the fragment is set to androidx.navigation.fragment.NavHostFragment and the app:navGraph = “@navigation/navigation” then the defaultNavHost is set to true.
Go back to the naviagtion.xml file and notice that the error is gone. Click on “New Destination ‘’ to add a new destination to your app, search for postsOverviewFragment and click ok. Since this is your only destination, it is automatically selected as your startDestination. When you are done your navigation file should look like this.
Although the navigation component seems very trivial in this scenario, it is very powerful when it comes to the concept of
Single Activity applications.
Congratulations, you are done building the post-app and learning the basics of Android architecture components and how they work together.