Categories
Programming

Setting Up Jetpack Navigation Component and Modal Navigation Drawer

On small screens like on mobile (setting aside how humongous smartphones tend to be nowadays), a very common pattern is to use a modal navigation drawer to allow users to navigate between one top-level destinations in the app. You’ve seen them: it’s usually the three horizontal lines on top left of an app, that can be tapped to show and hide a side drawer with a list of menu items inside of it.

Despite being a common design pattern, when I tried to learn how implement it in conjunction with the Jetpack Navigation Component, the available resources can be quite difficult to understand. They’re out there, but in my experience they’re a bit scattered and I had a hard time creating the right mental model in my mind on how to put things together.

This article is my attempt to sum things up. For things to make sense, you will need some familiarity with basic Android development and the Navigation Component itself.

Categories
Programming

General Mechanism for Scheduling Notification in Android

This article is more of a quick note about what’s involved when your Android app needs to create a notification and schedule it to appear at some time in the future. There’s a lot of articles out there that goes in details about the implementation. Think of this article as more of a summary.

Categories
Programming

Kotlin: Using MediatorLiveData to Use Data from Two Tables at Once

With my current app, the main data is Subjects, where each Subject have multiple Reminders tied to it. For this purpose, I have a couple of tables in the database, which in its simplified form can be seen as below:

Subject table with:

  1. Subject ID
  2. Subject title

Reminder table with:

  1. Reminder ID
  2. Reminder timestamp
  3. Subject ID

I’m using Room for database and LiveData to pull data from the tables. The data then gets supplied to a RecyclerView, where each RecyclerView item will display a Subject with its Reminders.

RecyclerView where each row is a Subject and its four Reminder dates.

The tricky part is that, since there are two tables, I have two separate methods returning LiveData object from each table. Most tutorials out there assume the use of only one LiveData to be observed by the RecyclerView, so it can get confusing when there’s two LiveData at once.

I found a smart solution on Stack Overflow that makes use of MediatorLiveData and Kotlin’s Pair to make the whole thing work. This article is an attempt to expand on that solution by writing down my understanding of it.

This article assumes familiarity with Android concepts for RecyclerView, Room, ViewModel, and LiveData.



Create a “combination” MediatorLiveData class

The first thing to do is to create a class that extends MediatorLiveData. The interesting part is that we need to use Pair<List<Subject>, List<Reminder>> as MediatorLiveData’s type.

After that, we simply construct the class by sending it LiveData<List<Subject>> and LiveData<List<Reminder>> as constructor parameters, then put those two inside a Pair, and finally set the Pair as the MediatorLiveData object’s value.

Example of a MediatorLiveData class to handle two LiveDatas at once.

That is pretty much where the magic lies. The potentially tricky part is the addSource method and the value variable, both of which are built-in features of the MediatorLiveData.

Inside ViewModel

Inside the ViewModel, we call the repository’s methods for pulling LiveData from each table, then we return a “combination” object as we describe in the class above, and put in the LiveData as its parameters.

Methods inside ViewModel to grab two LiveData from repository and put them into one MediatorLiveData

Inside RecyclerViewAdapter

Inside the adapter, we modify its constructor to accept two Lists as parameters, the List<Subject> and the List<Reminder>.

Put in the two List<> as constructor parameters in RecyclerViewAdapter

Observe the MediatorLiveData

Finally, inside the fragment or activity that has the RecyclerView, we observe the returned MediatorLiveData from the ViewModel. When there’s any change, the observer gives a Pair containing a List<Subject> and a List<Reminder>. We can then put those two as the parameter of the adapter.

Observe the MediatorLiveData given by ViewModel, and update RecyclerViewAdapter accordingly.

Note how the Observer pattern uses a lambda with subjectsAndRemindersPair as the parameter. That is essentially the type of the MediatorLiveData in question, and by default Kotlin call that parameter it. I renamed it to subjectsAndRemindersPair to make it easier to understand.


Categories
Programming

Strategies for Creating A Single RecyclerView with Multiple ViewHolders

This article assumes familiarity with creating a RecyclerView using a single ViewHolder for each item, which is the most common use case.

Here are the possible cases where a RecyclerView might use an adapter with multiple ViewHolders instead:

A RecyclerView that uses two different layouts for each item based on odd/even position.

Case One

Have one RecyclerView to display different layouts for different items at the same time, based on certain conditions. Just as an example, here I have a RecyclerView that uses two different layouts for each item based on odd/even position.

A RecyclerView that uses identical layout for each item.

Case Two

Have one RecyclerView to display the same layout for each item. However, based on user action on certain conditions (such as during sorting or filtering action), the same RecyclerView will display the same data using a different layout. As an example, here’s a RecyclerView that uses just one layout for each item. Use your own imagination for an example where the RecyclerView at a different time uses a different layout instead.


Case 1

Here’s the general method. I’m only listing areas that differ from the usual case of RecyclerView with just one ViewHolder:

  1. We now have to create the multiple ViewHolder classes first. I prefer them to be inner classes of the custom RecyclerView.Adapter class. Pretty self-explanatory, the only possible gotcha/oddity here is that inside the class’s constructor, we create variables that directly access specific views inside a certain layout, without the class knowing about which layout it is related to.
  2. The RecyclerView adapter now must extend the more generic RecyclerView.Adapter<RecyclerView.ViewHolder>class instead of using the single custom ViewHolder class as the generic type.
  3. The RecyclerView now must override getItemViewType(), which is used to decide which ViewHolder class to be used on a certain item position in the RecyclerView.
  4. The method onCreateViewHolder() must now return the more generic RecyclerView.ViewHolder
  5. The method onBindViewHolder() must now use the more generic RecyclerView.ViewHolder object as its first parameter.

Case 2

  1. Have a variable to determine which ViewHolder should be used by the RecylerView. This will usually be inside the fragment or activity.
  2. Pass that variable as one of the parameters for constructing the RecyclerView’s adapter.
  3. Inside the adapter, create the multiple ViewHolder classes first. I prefer them to be inner classes of the custom RecyclerView.Adapter class. Pretty self-explanatory, the only possible gotcha/oddity here is that inside the class’s constructor, we create variables that directly access specific views inside a certain layout, without the class knowing about which layout it is related to.
  4. The RecyclerView adapter now must extend the more generic RecyclerView.Adapter<RecyclerView.ViewHolder>class instead of using the single custom ViewHolder class as the generic type.
  5. The RecyclerView adapter now must override getItemViewType(), and check the variable from step 2 to determine which ViewHolder to use.
  6. The method onCreateViewHolder() must now return the more generic RecyclerView.ViewHolder
  7. The method onBindViewHolder() must now use the more generic RecyclerView.ViewHolder object as its first parameter.
Categories
Programming

Data Binding with Kotlin; Why?

I came across the Data Binding resources page for Android, and reading through it got me questioning. Why is such setup necessary, when Kotlin extensions allows for super easy view binding already (example below)?

Example of view binding using Kotlin Extensions (source)

As it turns out, there’s quite a difference as explained on a Reddit comment that I pasted below:

Hey! Developer Advocate for Android at Google here!

I wanted to add a bit of background here. Kotlin Extensions with synthetic views was never intentionally “recommended” though that shouldn’t be taken as a recommendation to not use them. If they’re working for you please feel free to continue using them in your app!

We’ve been shifting away from them (e.g. we don’t teach them in the Udacity course) because they expose a global namespace of ids that’s unrelated to the layout that’s actually inflated with no checks against invalid lookups, are Kotlin only, and don’t expose nullability when views are only present in some configuration. All together, these issues cause the API to increase number of crashes for Android apps.

On the other hand, they do offer a lightweight API that can help simplify view lookups. In this space it’s also worth taking a look at Data Binding which also does automatic view lookups – as well as integrates with LiveData to automatically update your views as data changes.

Today, there’s a few options in this space that work:

  • Data Binding is the recommendation for view lookup as well as binding, but it does add a bit of overhead when compared to Android Kotlin Extensions. It’s worth taking a look to see if this is a good fit for your app. Data Binding also allows you to observe LiveData to bind views automatically when data changes. Compared to Kotlin Extensions, it adds compile time checking of view lookups and type safety.
  • Android Kotlin Extensions is not officially recommended (which is not the same as recommendation against). It does come with the issues mentioned above, so for our code we’re not using them.
  • Butter Knife is another solution that is extremely popular and works for both Kotlin and the Java Programming Language.

Reading through the comments here there’s a lot of developers that are having great luck with Kotlin Extensions. That’s great – and something we’ll keep in mind as we look at ways to continue improving our APIs. If you haven’t taken a look at Data Binding, definitely give it a shot.

As an aside, our internal code style guide is not intended to be directly applied outside of our codebase. For example, we use mPrefixVariables, but there’s no reason that every app should follow that style.

On ButterKnife

The quote mentions ButterKnife, so it’s probably worth mentioning also that the Github page for ButterKnife mentions that development for it is being winded down, and it recommends to use View Binding instead.

Data Binding vs View Binding

Now that these two concepts are being mentioned, what are the differences? From the View Binding documentation page:

View binding and the data binding library both generate binding classes that you can use to reference views directly. However, there are notable differences:

  • The data binding library processes only data binding layouts created using the <layout> tag.
  • View binding doesn’t support layout variables or layout expressions, so it can’t be used to bind layouts with data in XML.

Stack Overflow: Android : Difference between DataBinding and ViewBinding

Categories
Programming

Digging into Coroutines

With Kotlin comes one of its most famous features: coroutines.

Javanese is my first language. No, not the Java programming language. Javanese. So whenever I hear a foreign word like coroutines, nothing shows up in my mind. It has no meaning. This post is an attempt to dig into the word to give my brain some understanding of it.

Coroutines, Threads, Processes

First, here’s what the official documentation says about it (emphasis mine):

One can think of a coroutine as a light-weight thread. Like threads, coroutines can run in parallel, wait for each other and communicate. The biggest difference is that coroutines are very cheap, almost free: we can create thousands of them, and pay very little in terms of performance. True threads, on the other hand, are expensive to start and keep around. A thousand threads can be a serious challenge for a modern machine.

“Your first coroutine with Kotlin”

I know that coroutines is a way to do asynchronous programming, and from the information above, it seems to have these three major points:

  1. Analogous to a light-weight thread,
  2. Multiple coroutines can run together at the same time, they can wait for each other, and also communicate to each other,
  3. Having a lot of them does not hurt performance.

I’m currently using Kotlin for programming Android apps, so the next step seems to be to figure out what exactly a thread is in this context. Here’s what the documentation says (emphasis mine):

When an application component starts and the application does not have any other components running, the Android system starts a new Linux process for the application with a single thread of execution. By default, all components of the same application run in the same process and thread (called the “main” thread). If an application component starts and there already exists a process for that application (because another component from the application exists), then the component is started within that process and uses the same thread of execution.

Android Developers — Processes and Threads

So from here, it seems like within Linux there is a concept of “process”, and also “thread of execution”. According to Wikipedia:

In computing, a process is the instance of a computer program that is being executed by one or many threads. It contains the program code and its activity. Depending on the operating system (OS), a process may be made up of multiple threads of execution that execute instructions concurrently

Wikipedia — Process (computing)

This helps understanding what a “process” is. One of the definition from Merriam-Webster is: “something going on”. So in this context, a process is the idea of an app that’s currently running within Android. That’s simple enough.

Also, it just blew my mind right now that this is why a processor is called that —again, English is not my first language—it’s basically the thing that runs the apps.

Let’s have a few seconds of visual break:

Photo by Tran Mau Tri Tam on Unsplash

Wikipedia has this to say about thread of executions:

In computer science, a thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system.[1] The implementation of threads and processes differs between operating systems, but in most cases a thread is a component of a process. Multiple threads can exist within one process, executing concurrently and sharing resources such as memory, while different processes do not share these resources.

Wikipedia — Thread (computing)

Thread, according to Merriam-Webster:

3 : something continuous or drawn out: such as

a : a line of reasoning or train of thought that connects the parts in a sequence (as of ideas or events)

So from the two explanations above, a process seems to be the whole existence of a running app, while a thread is the smaller element within the process.

For example in Instagram, a thread might be the start from finish when someone uploads a new picture (tap plus icon, find the image, add filter, add description, click upload, wait for it to be sent, have the image displayed). I don’t know if that’s technically accurate, but probably a good approximation.

The Wikipedia about thread also has this useful section about the difference between processes and threads:

Threads differ from traditional multitasking operating-system processes in several ways:

  • processes are typically independent, while threads exist as subsets of a process
  • processes carry considerably more state information than threads, whereas multiple threads within a process share process state as well as memory and other resources
  • processes have separate address spaces, whereas threads share their address space
  • processes interact only through system-provided inter-process communication mechanisms
  • context switching between threads in the same process typically occurs faster than context switching between processes

It also has this useful image:

A process with two threads of execution, running on one processor

So far we’ve learned about what a process and a thread is. And, specifically within Android, we have learned that a running app is given one process and one main thread.

So a coroutine is like a thread, and has similar behaviors, except that it’s light-weight and can perform better.

That’s a good start. Now let’s see if we can figure out what it means with multiple coroutines running in parallel, communicating and coordinating with each other.

Photo by Gaelle Marcel on Unsplash

Here is another explanation of what coroutine is:

coroutine A program component that allows structuring of a program in an unusual way. A coroutine resembles a subroutine, with one important difference. A subroutine has a subordinate position relative to the main routine: it is called and then returns. Coroutines, however, have a symmetric relation: each can call the other. Thus a coroutine is resumed at a point immediately following its call of another coroutine; it never returns, but terminates its operation by calling (resuming) another coroutine.

Encyclopedia.com — coroutine

What even is a subroutine?

In computer programming, a subroutine is a sequence of program instructions that performs a specific task, packaged as a unit. This unit can then be used in programs wherever that particular task should be performed.

Subroutines may be defined within programs, or separately in libraries that can be used by many programs. In different programming languages, a subroutine may be called a procedure, a function, a routine, a method, or a subprogram. The generic term callable unit is sometimes used.

Wikipedia — Subroutine
Photo by Mousssss Liu on Unsplash

So subroutine is basically a function. I know functions! I don’t think it’s practically possible to learn programming without learning about functions anyway.

Co+routine


“co-” prefix, from Wiktionary:

  1. together; mutually; jointly
  2. partner or subordinate in an activity
  3. to the same degree

So linguistically, perhaps coroutines mean functions that can run and work together as an equal.

I’m happy with that conclusion. It’s something I can picture in my mind. It does not seem to explain technically all coroutines can do in the context of Kotlin programming, in fact it sounds a little too bland to me. However, it is concise and memorable. And that perhaps is all a good name needs to do.

Categories
Programming

Simple Way to Add a Global Constants File with Kotlin

One of the most common things I’m adding when making an Android app is a file that contains various constant values to be used in various places. Here’s how I’d set it up.

First, create the .kt file (usually I’d name it Constants.kt). Add the package name at the top, then add the constant variables without creating any class for them. In this example I assume that the file is on the root folder of the app. For example:

Gist containing the Constants.kt file example

Finally, to use the constants themselves, we can just write the name without any fuss (on line 2 on the example below):

Example of how to use a constant variable

Categories
Programming

What Exactly is a “Repository”?

The Repository pattern is one of the concepts frequently used and mentioned within Android Architecture Components. As English is not my first language, I’m drawing a blank in my head whenever I read or hear the term “repository” itself. I have some abstract idea about what it is, mostly from the concept of Github repositories: it seems to be a central place for something.

In my experience, not understanding an English term is a common barrier to having a better intuition of a programming concept. I would read about something new and would have a hard time connecting a term that drew a blank in my mind, to the larger understanding of the concept. So today I figured I’ll geek out a little bit and see what the “repository” word really means.

According to Merriam-Webster, the word means:

: a place, room, or container where something is deposited or stored

This definition matches my understanding of a Git repository: it’s a place where the code is stored.

What about when the term is used in Android Architecture Components? From this Codelab:

A repository class abstracts access to multiple data sources. The repository is not part of the Architecture Components libraries, but is a suggested best practice for code separation and architecture. A Repository class provides a clean API for data access to the rest of the application.

and

A Repository manages queries and allows you to use multiple backends. In the most common example, the Repository implements the logic for deciding whether to fetch data from a network or use results cached in a local database.

Uh, what? That sounds like a lot of things it’s doing other than just being a storage.

The Codelab also provides this graphic:

Repository concept in Android Architecture Components
Repository concept in Android Architecture Components

I found a clearer explanation from an article about the Repository pattern:

[The repository] will persist your objects sans the need of having to know how those objects would be actually persisted in the underlying database, i.e., without having to be bothered about how the data persistence happens underneath. The knowledge of this persistence, i.e., the persistence logic, is encapsulated inside the Repository.

In essence, the Repository design pattern facilitates de-coupling of the business logic and the data access layers in your application with the former not having to have any knowledge on how data persistence would actually take place.

In using the Repository design pattern, you can hide the details of how the data is eventually stored or retrieved to and from the data store. This data store can be a database, an xml file, etc. You can apply this design pattern to even hide how data that is exposed by a web service or an ORM is accessed. Martin Fowler states: “Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.”

So from the above, it sounds like the Repository pattern is more of a way to allow access to various data sources, without the accessor having to know about how the data sources are created. It’s not just a dumb place to store things, instead it’s a smart storage that allows someone to add or remove things as needed.

That mental model still makes sense for a Git repository as well: I add and remove code to my repository without really knowing how Github stores it: is it in a database? If yes, what database? I don’t need to know.

Picture of a library

From now on I figure I’d imagine the Repository pattern as a sort of librarian who I can talk to to help me find a book: I mention a title and they’d go ahead and fetch it for me. From my perspective it’s not important where the book is stored within the library (shelves? Fridge? Vault?), as long as I can borrow and return it properly. After all, a library is indeed a repository of books, yeah?

Categories
Bahasa Indonesia Programming

Dagger 2 untuk Dummies Menggunakan Kotlin (dalam 20 baris kode)

🇮🇩

Artikel kali ini diterjemahkan dari artikel asli dalam bahasa Inggris, “Dagger 2 for Dummies in Kotlin (with 20 lines of code)” oleh Elye, link di bawah:

🇬🇧

Below is my Indonesian translation from the original article “Dagger 2 for Dummies in Kotlin (with 20 lines of code)” by Elye, link below:


Saya seorang dummy, dan belajar Dagger 2 membikin otak saya melar!! Semua tutorial yang saya baca membahas terlalu banyak hal, seperti Dependency Injection, Provider, Component, Module, Scope… membuat otak meledak!

Selain itu, kebanyakan tutorial memakai Java. Kotlin adalah pilihan yang lebih baik saat ini. Saya perlu berbulan-bulan untuk memahami Dagger 2 di Kotlin. Jadi saya berjanji pada diri sendiri bahwa nanti begitu saya paham, saya harus membuat tutorial yang super sederhana, sesimpel ABC, untuk orang-orang seperti saya yang mungkin membutuhkan.

Karena alasan itu, saya membuat aplikasi Android paling sederhana yang memakai Dagger 2… Hanya 2 halaman, kurang dari 20 baris 😎 . (tolong jangan membuat yang lebih simpel lagi, biarkan saya memegang satu rekor dunia 😝).

Banyak tutorial menampilkan kode berhalaman-halaman, dan tautan hubungan antar classes. Otak saya capek, karena stack saya dangkal 😝

(Untuk para ahli, saya minta maaf untuk cara saya yang tidak umum untuk memperkenalkan Dagger 2, karena ini benar-benar untuk dummies, dan hanya meminta pengetahuan yang minim bagi pembacanya)

Ayo mulai dari pemrograman (sangat) sederhana (… di Android)

Oke, saya akui, saya lumayan punya pengetahuan pemrograman. Saya tahu soal Function, Class, Member Variables. Saya tahu cara membuat aplikasi Hello World dengan satu MainActivity (hey! … Anda bisa membuat aplikasi itu tanpa mengetik satu baris kode… Cukup memakai template Empty Activity di Android Studio 😁)

Untuk membuatnya lebih keren, saya bisa mengubah aplikasi Hello World tadi menjadi aplikasi Hello Dagger 2. Silahkan lihat kode di bawah (bagian yang ditebalkan adalah kode yang saya tambahkan kepada template yang sudah ada).

class MainActivity : AppCompatActivity() {

    val info = Info()

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

        text_view.text = info.text
    }
}

class Info  {
    val text = "Hello Dagger 2"
}

Maaf kalau saya membuat hal sederhana jadi lebih rumit dengan menggunakan class untuk menyimpan teks. Dan saya membuat member variable bernama infountuk menyimpannya. Nanti dia akan berperan penting untuk menjelaskan penggunaan Dagger 2.

Saya harap semua cukup simpel sejauh ini.

Memindah info keluar dari Activity…

Dalam aplikasi beneran, biasanya saya tidak hanya punya satu member variable info. Bisa jadi akan ada banyak lainnya. Atau barangkali saya ingin membagi info dalam banyak activities.

Untuk mempermudah, alih-alih membuat info di dalam class MainActivity, saya ingin membuat info (dan semua member variables lain) dibuat di tempat lain, lalu dikirim masuk. Hey, kamu tahu? Ada istilah untuk hal tersebut… Dependencies Injection 😉.

Gambar perbedaan membuat variable biasa dengan menyuntikkan variable menggunakan dependency injection.

Kedengaran sederhana… mungkin saya bisa membuat seperti ini?

class MainActivity(val info:Info) : AppCompatActivity()

Ups! Maaf, Android tidak mengizinkan saya menambah argumen kepada constructor Activity. Kalau saya mau, ada jalan yang lebih susah. Cek di Stack Overflow. Tapi meskipun saya mau pakai jalan susah tersebut, dia masih tetap mustahil dilakukan, karena kita melakukannya di Activity pertama di dalam aplikasi. Siapa yang akan mengirim parameternya?

Kalau Anda punya lebih banyak pengalaman di Android development, mungkin sekarang Anda sudah berpikir untuk menggunakan Application untuk membantu Anda… Tepat sekali, Anda benar! … Tetapi saya yang sedang bercerita di sini. Saya sedang bercerita soal Dagger 2, jadi mari kembali ke topik…

Berdasarkan kisah nyata…

Dengan adanya kebutuhan seperti kita bahas sebelumnya, beberapa orang mulai membayangkan, betapa enaknya kalau kita bisa secara ajaib membuat semua dependencies agar bisa di-inject (dalam kasus saya, dependency yang saya punya adalah info). Untuk Android, beberapa orang tersebut akhirnya membuat Dagger, yang dibuat oleh para ahli dari Square. Google menyukai karya mereka, dan membuat versi keduanya, Dagger 2.

Dagger 2 menjadi sangat populer… sampai suatu hari seorang dummy ingin belajar tentangnya… lalu memutuskan membuat blog post… Okay, cukup ngobrol sejarahnya…

Membuat info muncul secara ajaib?

Dengan adanya Dagger 2, kita sekarang bisa membuat info muncul secara ajaib. Mungkin akan lebih ajaib lagi kalau kita bisa menghapus baris val info = Info(), tapi hal ini TERLALU ajaib. Sudah di luar kemampuan Dagger 2, mungkin butuh David Copperfield.

Yang lebih realistis, kita masih harus memberitahu MainActivity untuk memiliki member variable bernama info, tetapi dia TIDAK PERLU membuat/menginisialisasinya (hal ini biarkan terjadi secara ajaib).

Dengan Kotlin, karena kita ingin info sebagai non-nullable item, maka info harus diinisialisasi dengan sesuatu. Tetapi karena kita tidak tahu dia mau diisi apa, dan kita menunggu kerja dari Dagger 2, maka kita perlu memberitahu Kotlin agar lebih santai dan tidak kuatir soal inisialisasi, dengan cara menambah modifier bernama lateinit.

Jadi singkatnya, cukup tambahkan lateinit var info: Info untuk mengganti kode kita sebelumnya.

class MainActivity : AppCompatActivity() {

lateinit var info: Info override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

text_view.text = info.text}
}

class Info {
val text = "Hello Dagger 2"
}

Hey, jangan di-compile dulu kode di atas, nanti akan CRASH 💥!!! Penyebabnya karena info belum diinisialisasi.

Maaf karena belum ada keajaiban di sini. Jangan marah ke saya… Saya janji kita nanti tidak perlu inisialisasi di MainActivity, cukup Dagger 2 yang melakukannya.

Sekarang, menuju dunia Dagger 2…

Menambahkan libraries Dagger 2

Sebelum terlalu jauh, kita lakukan hal-hal membosankan dulu. Semua yang kita perlu pakai di luar hal-hal Android paling dasar, kita perlu tambahkan libraries-nya ke project kita. Dagger 2 juga begitu. Cukup ikuti langkah berikut.

Di file build.gradle aplikasi Anda (pilih untuk app, bukan project), tambahkan baris yang ditebalkan berikut:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

dan

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// ... other dependencies ...
implementation 'com.google.dagger:dagger:2.13'
kapt 'com.google.dagger:dagger-compiler:2.13'
}

Lalu lakukan sync di project Anda. Sekarang Anda bisa pakai Dagger 2.

Kembali ke kode… Memperkenalkan @Component

Sekarang kita kembali ke kode. Saya kenalkan Anda pada yang namanya @Component. Dia adalah satu cara untuk memberitahu Dagger 2, “Hey! Saya ingin satu kotak ajaib yang bisa melakukan keajaiban membuat member variable saya.”

Saya mendeklarasikan kotak ajaib tadi seperti ini, sebagai interface:

@Component
interface MagicBox

Saya menyebutnya MagicBox agar berbeda dengan tutorial lain, agar Anda tahu kalau dia bisa dikasih nama apa saja. Tutorial lain akan memberinya nama <Something>Component. Dalam proyek bersama-sama dengan orang lain, saya sarankan memakai nama seperti itu, jadi orang lain bisa paham kode anda.

Saya juga perlu memberi tahu kotak ajaib tadi, bahwa dia dibuat untuk melakukan keajaiban pada MainActivity. Untuk melakukan itu, saya membuat function bernama poke , yang menerima MainActivity sebagai parameter di dalam MagicBox

@Component
interface MagicBox {
fun poke(app: MainActivity)
}

Catat bahwa saya memakai nama poke (suntik) agar berbeda dengan tutorial lain, jadi Anda tahu dia bisa diberi nama apa saja. Hampir semua tutorial yang saya baca, memakai nama inject. Sekali lagi saya sarankan memakai nama itu untuk proyek bersama, agar mudah dipahami.

Untuk membuat semuanya lebih jelas, bayangkan bahwa MagicBox bisa dipakai untuk menyuntik MainActivity, seperti di diagram di bawah, agar bisa memasukkan info ke dalam MainActivity. Kita cuma perlu memasukkan info ke dalam MagicBox, dan dia langsung bekerja! …Akan saya tunjukkan bagaimana info dimasukkan, sebentar lagi…

Contoh memasukkan variable ke dalam activity menggunakan Dagger 2

Mari kita bayangkan sebentar. Terutama jika saat ini Anda bertanya-tanya, untuk apa membuat MagicBox kalau kita bisa membuat info langsung di dalam MainActivity?

Sekarang bayangkan kita punya banyak members variable selain info, dan kita juga punya banyak activities dan classes yang membutuhkan mereka. Jadi diagramnya jadi seperti di bawah, dan MagicBox bisa mengisi semuanya sekaligus, tanpa satupun activities atau classes harus repot-repot membuat members variable-nya sendiri.

Contoh diagram memasukkan variable ke dalam banyak activity dan class menggunakan Dagger 2

👏👏👏 MagicBox mengurus semuanya untuk kita!! Baik sekali dia…

Oke, sekarang kembali ke kenyataan. Kita hanya butuh satu info di satu MainActivity, karena kita sedang membuat aplikasi Dagger 2 paling simpel di dunia.

Memasukkan info ke @Component… memakai @Inject

Oke, sekarang kita masukkan info ke @Component

Sekarang bisa jadi Anda berpikir, kalau kita harus secara manual memasukkan info ke @Component, terus apa spesialnya? Bukannya kita bisa membuat class untuk membuat yang seperti itu? Di mana ajaibnya?!!

Tunggu… keajaiban Dagger 2 segera muncul… sekarang!

Keajaibannya adalah, saya tidak perlu memasukkan info ke MagicBox. Saya cukup tambahkan keyword spesial (disebut juga annotation) ke dalam class Info, dan Dagger 2 langsung tahu ke mana dia harus mengambil Info.

Perkenalkan… @Inject!!

Ubah dulu class Info tadi dari

class Info  {
val text = "Hello Dagger 2"
}

menjadi

class Info @Inject constructor() {
val text = "Hello Dagger 2"
}

Maaf kalau Anda harus mengetik lebih banyak dari @Inject, karena sebelumnya kita tidak membikin constructor. Karena @Inject harus ditambahkan di constructor, maka kita perlu menampilkan contructor di Kotlin memakai function constructor() di atas.

Setelah annotation @Inject ditambahkan, Dagger 2 sekarang sudah tahu caranya mengambil class Info lewat MagicBox. Gampang sekali caranya, sekarang Anda bisa tambahkan @Inject ke class manapun yang sudah ada, dan class object langsung terkonek dengan MagicBox secara ajaib saat dibuat.

Jika Anda penasaran bagaimana caranya… Itulah keajaiban Dagger 2. Tidak usah pusing-pusing memikirkannya, karena kita cuma dummies 😝.

Membangun MagicBox…Keajaiban Dagger 2 yang lain

Semua belum selesai. Kita sudah mendefinisikan MagicBox, dan menyambungkan Info ke MagicBox. Kita sudah punya function poke untuk menyuntik MainActivity.

Tapi sejauh ini, kita belum menyuntiknya!

Sebelum kita bisa melakukan itu, kita harus membangun MagicBoxnya dulu…

Tapi MagicBox adalah sebuah interface. Siapa yang harus membuat implementasinya?

Jangan takut!!! Inilah keajaiban Dagger 2 berikutnya. Karena kita sudah memberinya nama MagicBox, kita cukup menjadikan Dagger 2 sebagai penguasa dari MagicBox, dengan cara memanggil Static Class yang secara ajaib sudah dibuatkan, bernama DaggerMagicBox.

Kelas ini akan memberi anda sebuah Builder untuk membangun MagicBox. Cukup panggil seperti berikut:

DaggerMagicBox.create()

Jangan kuatir kalau Android Studio protes karena dia tidak mengenali DaggerMagicBox, karena sampai pada titik itu dia memang belum diciptakan. Cukup percayakan pada Lord Dagger 2, dan dia akan membereskan semua. Compile/Run... dan seperti dijanjikan, DaggerMagicBox diciptakan saat proses kompilasi.

Sekarang Anda bisa main suntik

Oke, menyuntik MainActivity bisa dilakukan seperti berikut (intinya: buat MagicBox, panggil function poke. Masukkan MainActivity sebagai parameter memakai this):

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerMagicBox.create().poke(this)
text_view.text = info.text
}

Idealnya, sampai di sini aplikasi langsung jalan.

Compile… run… CRASH 💥 !!! Android Studio protes lagi, info belum diinisialisasi!!!

Tolong, tolong jangan tembak saya… saya tidak bohong soal keajaiban Dagger 2. Ijinkan saya tambahkan satu step lagi…

@Inject lagi!!!

dalam MainActivity, kita sudah definisikan info seperti ini

lateinit var info: Info

Dagger 2 tidak ingin membajak member variable manapun dengan membuat tanpa ijin saya. Ini sebenarnya bagus, karena kadang bisa jadi saya ingin bikin info saya sendiri.

Jadi untuk memberitahu Dagger 2, member variable mana yang perlu dia ciptakan, kita pakai kata kunci @Inject di depan namanya:

@Inject lateinit var info: Info

SELESAI!!! Sudah cukup. Compile, semua jalan!!!

Android app dengan Dagger 2 tersimpel di dunia

Dengan kode tadi, selesai sudah dalam satu file (kurang dari 20 baris, dan hanya 4 baris Dagger 2):

class MainActivity : AppCompatActivity() {

    @Inject lateinit var info: Info

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerMagicBox.create().poke(this)
        text_view.text = info.text
    }
}

class Info @Inject constructor() {
    val text = "Hello Dagger 2"
}

@Component
interface MagicBox {
    fun poke(app: MainActivity)
}

Anda bisa ambil contoh project-nya di

https://github.com/elye/demo_android_simplest_dagger2

Apakah semua selesai?

Oh tentu tidak. Ini hanya ujung dari gunung es Dagger 2. Tapi untuk seorang dummy seperti saya, mencerna hal sederhana begini adalah awal yang bagus, dan otak saya bisa istirahat sejenak.

Saya tahu kalau saya hanya membagi hal paling dasar (dan saya dengar sebagian orang memprotes, karena saya tidak adil kepada Dagger 2. Maaf… saya akan tambahkan lagi detailnya kalau pembaca tertarik), dan saya memang sengaja tidak membahas hal-hal penting seperti @Module, @Singleton, @Scope, dan lainnya. Mereka tidak dibahas supaya saya bisa membuat semuanya lebih mudah dipahami para newbies. Tapi mereka memang penting untuk membuat Dagger 2 semakin berguna.

Kalau Anda tertarik, Anda bisa belajar lebih lanjut di sini.

Atau, Anda juga bisa mencari tutorial lain, karena saya merasa tutorial saya sudah memberi Anda pemahaman dasar bagaimana hal-hal di Dagger 2 saling menyambung. Anda bisa jadi sudah siap untuk membaca tutorial lain yang lebih padat.

Dapatkan Artikel-artikel Seperti ini Di Email Anda