0% menganggap dokumen ini bermanfaat (0 suara)
5 tayangan342 halaman

Proses Latar Belakang dan Jaringan Android

Hak Cipta
© All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
5 tayangan342 halaman

Proses Latar Belakang dan Jaringan Android

Hak Cipta
© All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd

Pengenalan Background Process

dan Networking
Selanjutnya kita akan mempelajari background process dan networking.
Berikut ini adalah preview dari kedua modul tersebut:

1. Background Process
Pada modul ini, Anda akan belajar menjalankan proses di
background. Ini merupakan hal yang sangat penting agar aplikasi
yang Anda buat dapat menjalankan proses yang berat dan lama
tanpa memengaruhi UI. Sehingga, pengguna tidak akan merasakan
lagging atau freeze ketika menjalankan aplikasi. Beberapa
komponen yang bisa Anda gunakan untuk menjalankan proses di
background secara asynchronous adalah Handler dan
Executors, sedangkan di dalam Kotlin terdapat Coroutine.

2. Networking
Pada modul ini, Anda akan mempelajari apa itu Web API dan format
JSON. Kemudian, kita belajar cara untuk mengambil data dari Web
API dengan berbagai macam library dan mem-parsing datanya
supaya bisa dibaca oleh aplikasi. Pada latihan pertama, Anda akan
belajar menggunakan library LoopJ untuk mengetahui fundamental
parsing JSON secara manual. Lalu, pada latihan berikutnya Anda
dapat menggunakan library Retrofit untuk mem-parsing JSON
dengan lebih efektif. Materi ini sangat penting karena sebagian
besar tugas dari Android Developer adalah menampilkan dan
mengirimkan data ke server.

Beberapa poin di atas hanyalah pengenalan terhadap komponen-


komponen yang akan kita pelajari di dalam modul Background Process
dan Networking. Penting untuk memahami komponen-komponen dasar
Android ini sebelum melangkah ke materi berikutnya yang lebih dalam.
Selamat belajar!
Teori Background Thread

Aplikasi dijalankan menggunakan proses dalam linux. Ketika sebuah


komponen aplikasi dijalankan, maka sistem android akan menjalankan
sebuah proses linux baru pada aplikasi dan dengan menggunakan suatu
thread tunggal.

Secara umum semua komponen aplikasi di Android berjalan pada proses


dan thread yang sama. Ini disebut main thread atau ui thread. Secara
default ini berarti sistem Android tidak secara otomatis menciptakan
thread lain untuk menjalankan proses secara spesifik.

Ingat! activity, fragment, service, ui toolkit, broadcast receiver dijalankan


di main thread atau ui thread

Dalam pengembangan aplikasi, terkadang kita membutuhkan proses


komputasi yang intensif. Misalnya proses memanipulasi bitmap dan
proses menghubungi server.

Jika proses-proses tersebut dilakukan pada ui thread atau main thread,


maka proses tersebut akan menghambat rendering tampilan aplikasi. Hal
ini ditandai dengan komponen ui widget tidak bisa diklik. Mirip dengan
kondisi hang atau freeze.

Sistem Android akan menghitung selama 5 detik apakah aplikasi yang


sedang berjalan itu responsif atau tidak. Jika tidak, maka sistem Android
akan menampilkan dialog Application Not Responding (ANR). Jelas ini akan
memberi dampak yang negatif ke pengguna. Kesalahan ini dapat
berujung ke pengguna mencopot aplikasi kita (uninstall).

Oleh karena itu kita membutuhkan thread lain. Thread lain ini bernama
background thread atau async task yang bersifat asynchronous. Ia akan
berjalan pada thread yang terpisah dengan main thread. Yang dimaksud
dengan asynchronous di sini yaitu proses tidak berjalan berurutan,
sehingga proses lain bisa masuk dan layar Android masih bisa kita
jalankan. Ini membuat pengalaman pengguna menjadi lebih nyaman
karena aplikasi tetap terasa responsif dan tidak mengalami freeze.
Berbeda dengan kebalikannya yaitu synchronous, proses berjalan secara
berurutan, sehingga kita harus menunggu proses tersebut selesai terlebih
dahulu sebelum melakukan proses lainnya. Hal ini membuat layar Android
tidak bisa kita jalankan (freeze).
Ada dua aturan yang harus kita perhatikan agar tercipta pengalaman
pengguna yang baik ketika menerapkan proses komputasi intensif yang
memakan waktu.

 Jangan memblok ui thread atau main thread. Ini berarti kita harus
menciptakan background thread atau async task yang bersifat
asynchronous. Ini akan menjaga aplikasi tetap responsif.
 Jangan melakukan pemanggilan komponen ui widget (seperti TextView,
Button, ImageView dsb.) di dalam background thread atau thread yang
sedang berjalan secara asynchronous. Ini karena Android UI Toolkit
merupakan komponen yang hanya berjalan pada ui thread atau
main thread.

Oiya, bagi yang belum tahu apa itu thread berikut penjelasan singkatnya.

Thread adalah sekumpulan perintah (instruksi) yang dapat dilaksanakan


(dieksekusi) secara beriringan dengan thread lainnya. Hal ini dicapai
dengan menggunakan mekanisme time slice (ketika satu CPU melakukan
perpindahan antara satu thread ke thread lainnya) atau multiprocess
(ketika thread tersebut dijalankan oleh CPU yang berbeda dalam satu
sistem).

Gambarannya sebagai berikut:

Thread A dan Thread B masih tetep berjalan beriringan dengan


main thread.
Thread

Dengan menggunakan background thread, kita dapat menciptakan


thread baru dan menjalankan proses di thread tersebut. Terciptanya
thread baru dapat memenuhi aturan no 1 tentang pengalaman pengguna
yang baik, yaitu dengan tidak memblokir ui thread.

Contoh bagaimana menggunakan threads yaitu:

 Kotlin
 Java

1. fun onClick(v: View) {

2. Thread(Runnable {

3. val txt = loadStringFromNetwork("[Link]

4. [Link](txt)

5. }).start()

6. }

Sayangnya, contoh background thread di atas tidak memenuhi aturan no


2, yaitu jangan melakukan pemanggilan ui di luar ui thread atau main
thread. Perhatikan metode setText. Metode ini adalah bagian dari
textView. Sementara itu textView adalah salah satu komponen ui.
Sehingga, ia tidak diperbolehkan untuk dijalankan di background thread.

Ada beberapa fungsi yang dapat dimanfaatkan untuk memenuhi aturan 2


yaitu:

1. [Link](Runnable)
2. [Link](Runnable)
3. [Link](Runnable , long)

Agar kode di atas memenuhi aturan no 2, kita dapat menggunakan


metode post milik ui atau view. Contohnya:

 Kotlin
 Java

1. fun onClick(v: View) {

2. Thread(Runnable {
3. val txt = loadStringFromNetwork("[Link]

4. [Link](Runnable() {

5. [Link](txt)

6. }

7. }).start()

8. }

Kode Thread dan Runnable di awal digunakan untuk membuat thread


baru, sehingga proses dapat berjalan secara asynchronous di background
thread. Kemudian untuk berpindah ke main thread lagi, dibutuhkan fungsi
post supaya tidak terjadi eror. Namun perlu diperhatikan bahwa fungsi ini
hanya bisa digunakan di masing-masing komponen saja.

Handler

Beberapa fungsi seperti [Link] dapat menyelesaikan masalah tentang


background thread. Akan tetapi menggunakan fungsi [Link] akan
menjadikan kode lebih kompleks dan sulit untuk dikelola.

Solusinya adalah dengan menggunakan handler yang dapat mengirim dan


memproses message dan objek runnable lainnya yang berhubungan
dengan thread. Ketika handler diciptakan, maka dia terkait dengan thread
di mana diciptakan.

Berikut adalah contoh perubahan yang dapat kita lakukan:

 Kotlin
 Java

1. private val handler = Handler([Link]()) {

2. fun handleMessage(msg:Message) {

3. val message = [Link] as String

4. [Link] = message

5. }

6. }

7.

8. fun onClick(v:View) {
9. Thread(Runnable {

10. public override fun run() {

11. val txt = loadStringFromNetwork("[Link]

12. val msg = [Link]()

13. [Link] = txt

14. [Link](handler)

15. [Link]()

16. }

17. }).start()

18. }

Dengan mendeklarasikan obyek handler pada ui thread maka obyek


handlers diikat ke ui thread. Ketika suatu message dikirim ke handler,
maka handler akan memprosesnya. Proses dari message tersebut terjadi
pada ui thread sehingga aturan no 2 dapat terpenuhi.

Executor

Executor adalah komponen pada Android yang memiliki kemampuan


untuk memanajemen banyak thread sekaligus, baik dari segi urutan
proses, penjadwalan, maupun menjalankan proses secara paralel. Alih-alih
menjalankan Thread satu per satu, Anda sebaiknya menggunakan
Executor seperti berikut:

 Kotlin
 Java

1. val executor:Executor = anExecutor()

2. [Link](RunnableTask1())

3. [Link](RunnableTask2())

4. ...

Kemudian untuk mengatur jumlah thread yang dibuat, Anda bisa


memanfaatkan beberapa jenis Executor berikut:

1. newSingleThreadExecutor, digunakan jika Anda hanya ingin membuat


satu thread saja. Karena hanya ada satu thread saja, apabila ada
beberapa request yang berjalan maka request selanjutnya akan dijalankan
setelah thread pertama selesai.
2. newFixedThreadPool(nThreads), digunakan jika Anda ingin membuat
banyak thread dengan memasukkan nThread dengan jumlah thread yang
ingin Anda buat. Misal newFixedThreadPool(4), maka akan membuat 4
thread yang tetap. Sehingga apabila ada beberapa request yang berjalan,
maka akan dibagi ke 4 thread tersebut secara paralel.
3. newCachedThreadPool, thread akan dibuat sesuai dengan kebutuhan,
ia juga akan menggunakan thread sebelumnya jika bisa dipakai. Lalu
thread yang sudah tidak dipakai selama 1 menit akan otomatis dihapus
dari cache.

Selain Executor, juga ada yang namanya ExecutorService yang


merupakan turunan dari Executor. Bedanya yaitu pada ExecutorService
kita bisa memantau proses yang berjalan dengan menggunakan fungsi
submit yang menghasilkan Future. Selain itu ada fungsi shutdown yang
berfungsi untuk menolak task baru dan cancel untuk membatalkan task.

Berikut ini adalah contoh penggunaan ExecutorService dengan Handler


untuk meng-update View di UI Thread

 Kotlin
 Java

1. val executor: ExecutorService = [Link]()

2. val handler = Handler([Link]())

3. [Link] {

4. //do background process

5. val txt: String = loadStringFromNetwork("[Link]

6. [Link] {

7. //do ui update

8. [Link](txt)

9. }

10. }

Coroutine (Kotlin)

Coroutine adalah salah satu fitur unggulan dari Kotlin yang diperkenalkan
sejak versi 1.3. Walaupun coroutines dan threads bekerja dengan cara
sama, coroutines jauh lebih ringan dan efisien. Jutaan coroutines dapat
berjalan pada beberapa threads. Jika dibandingkan dengan threads,
coroutines tidak hanya mudah diterapkan, melainkan juga jauh lebih
powerful. Kelebihan tersebut terutama berlaku pada lingkungan mobile, di
mana setiap milliseconds kenaikan kinerja sangat diperhitungkan. Selain
itu, perbedaan lainnya adalah coroutines dikelola oleh pengguna,
sedangkan threads dikelola oleh sistem operasi. Dengan coroutine kita
tidak memerlukan lagi callback sehingga kode menjadi lebih simpel dan
ringkas.

Untuk mulai menggunakan Coroutine kita perlu menambahkan


dependency berikut terlebih dahulu:

1. implementation "[Link]:kotlinx-coroutines-core:1.1.1"

2. implementation "[Link]:kotlinx-coroutines-android:1.1.1"

Ada dua jenis cara untuk memanggil coroutine yaitu:

1. Launch, yaitu kita memanggil proses tanpa ada nilai kembalian alias fire
and forget.
Contoh dari penerapan fungsi ini yaitu seperti ini:

1. fun main(args: Array<String>) = runBlocking {

2. launch {

3. delay(1000)

4. println("Coroutines!")

5. }

6. println("Hello")

7. }

8.

9. //print

10. Hello

11. Coroutines!

Kata Hello akan ditampilkan lebih awal, dan kata Coroutines! akan ditampilkan
1 detik setelahnya. Mengapa demikian? Padahal jika diperhatikan, kode untuk
menampilkan kata Coroutines! dituliskan lebih dulu. Fungsi delay(1000) di
dalam launch digunakan untuk menunda kode berikutnya selama 1 detik. Delay
adalah fungsi yang spesial pada coroutines. Ia merupakan sebuah suspending
function yang tidak akan memblokir sebuah thread. Selama proses penundaan
tersebut, main thread akan terus berjalan sehingga fungsi println("Hello,") akan
langsung dijalankan. Setelah 1 detik, baru fungsi println("Coroutines!") akan
dijalankan.

Async, yaitu kita memanggil proses dengan menunggu nilai kembalian dalam
bentuk Deferred. Untuk mendapatkan hasil dari proses, kita bisa menggunakan
fungsi await.
Contoh dari penerapan fungsi ini yaitu seperti ini:

1. fun main(args: Array<String>) = runBlocking {

2. val first = async { getNumber() }

3. val result = [Link]()

4. println(result)

5. }

6. suspend fun getNumber(): Int {

7. delay(1000)

8. return 3*2

9. }

10.

11. //print

12. 6

Di sini kita menggunakan async dan kemudian membuat suspending


function yang memilik nilai kembalian berupa integer, kemudian untuk
mendapatkan datanya kita menggunakan await. Cukup mudah bukan?

Selain itu, kita juga perlu mengetahui Dispatcher untuk mengetahui di


mana proses coroutine kita berjalan. Ada beberapa macam pilihan
Dispatcher yang bisa kita pilih:

1. [Link], adalah default dispatcher jika tidak ada


Dispatcher yang didefinisikan, cocok untuk fungsi yang membutuhkan
proses CPU yang tinggi, seperti parsing 100 data.
2. [Link], untuk menjalankan fungsi yang berisi read-write data ke
Network/Disk, seperti menulis atau mengambil data ke database dan ke
server.
3. [Link], untuk menjalankan fungsi di Main Thread, biasanya
digunakan untuk mengupdate View UI.

Selain itu, dengan menggunakan Coroutines, kita dapat mengurangi


potensi memory leaks. Hal ini karena setiap Coroutine harus dijalankan di
dalam Scope yang membentuk structured concurrency. Scope ini
berfungsi untuk mengontrol seberapa lama coroutine menjalankan
tugasnya. Berikut ini adalah beberapa scope yang bisa Anda pakai di
Android.

 CoroutineScope: Scope umum yang digunakan untuk untuk


melacak coroutine yang sedang berjalan di dalamnya. Coroutine
dapat dibatalkan dengan memanggil fungsi cancel() kapan saja.
 LifecycleScope: Scope khusus yang digunakan di dalam Activity
atau Fragment. Dengan menggunakan scope ini, sistem akan
membatalkan coroutine secara otomatis ketika lifecycle masuk ke
keadaan mati (onDestroy).
 ViewModelScope: Scope yang khusus digunakan di ViewModel.
Dengan menggunakan scope ini, sistem akan membatalkan
coroutine secara otomatis ketika ViewModel dibersihkan
(onCleared).

Untuk lebih mendalami topik ini, Anda dapat membaca tautan berikut:

 Process and Threads


 Threading
 Coroutine
 Understanding Kotlin Coroutines with this mental model
Latihan Simulasi Proses Asynchronous dengan Background Thread
Tujuan

Codelab ini bertujuan untuk mengimplementasikan Background Process


pada aplikasi yang Anda bangun. Harapannya aktivitas ini dapat memberi
gambaran yang jelas tentang cara kerja proses yang di dalam
background.

Codelab kali ini akan menghasilkan aplikasi berikut ini:

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama MyBackgroundThread.


2. Mengatur tampilan pada berkas layout XML.
3. Memembuat countdown.
4. Menjalankan aplikasi dan mengalami freeze.
5. Menambahkan Executor Service.
6. Menjalankan aplikasi kembali dan tidak terjadi freeze.
7. Menambahkan Coroutine sebagai alternative Executor Service di Kotlin.
8. Menjalankan aplikasi kembali.

Codelab Background Process Sederhana

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:


Nama Project MyBackgroundThread

Target & Minimum Target Phone and Tablet, Api


SDK level 21

Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Artifacts True

Language Kotlin/Java

2. Jika sudah, kondisikan agar activity_main.xml menjadi seperti ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <ScrollView xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:layout_width="match_parent"

5. android:layout_height="wrap_content"

6. tools:context=".MainActivity">

7.

8. <LinearLayout

9. android:layout_width="match_parent"

10. android:layout_height="wrap_content"

11. android:gravity="center"

12. android:orientation="vertical"

13. android:padding="16dp">

14.

15. <Button

16. android:id="@+id/btn_start"

17. android:layout_width="wrap_content"

18. android:layout_height="wrap_content"

19. android:layout_marginTop="300dp"
20. android:text="@string/start_task" />

21.

22. <TextView

23. android:id="@+id/tv_status"

24. android:layout_width="wrap_content"

25. android:layout_height="wrap_content"

26. tools:text="@string/status" />

27.

28. <TextView

29. android:layout_width="wrap_content"

30. android:layout_height="wrap_content"

31. android:layout_marginTop="400dp"

32. android:text="@string/hidden_text" />

33. </LinearLayout>

34.

35. </ScrollView>

Jangan lupa masukkan teks yang ada ke dalam [Link] beserta teks lain
yang akan kita gunakan pada langkah selanjutnya.

1. <resources>

2. <string name="app_name">MyBackgroundThread</string>

3. <string name="start_task">Start Task</string>

4. <string name="status">Status</string>

5. <string name="hidden_text">Now you see me!</string>

6. <string name="task_completed">Task Completed</string>

7. <string name="compressing">Compresssing %d %%</string>

8. </resources>

Pada awal latihan ini kita akan mencoba untuk menjalankan suatu proses yang
lama di UI thread dan melihat bagaimana efeknya. Sebagai contoh di sini kita
mensimulasikan proses compress yang memakan waktu 5 detik dengan
menambahkan kode berikut di dalam MainActivity:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2.

3. override fun onCreate(savedInstanceState: Bundle?) {

4. [Link](savedInstanceState)

5. setContentView([Link].activity_main)

6.

7. val btnStart = findViewById<Button>([Link].btn_start)

8. val tvStatus = findViewById<TextView>([Link].tv_status)

9.

10. [Link] {

11. try {

12. //simulate process compressing

13. for (i in 0..10) {

14. [Link](500)

15. val percentage = i * 10

16. if (percentage == 100) {

17. [Link]([Link].task_completed)

18. } else {

19. [Link] =
[Link](getString([Link]), percentage)

20. }

21. }

22. } catch (e: InterruptedException) {

23. [Link]()
24. }

25. }

26. }

27. }

Selanjutnya jalankan project Anda dan klik tombol START TASK. Selama proses
berlangsung (5 detik) cobalah untuk scroll layar dan lihat apa yang terjadi.

Dapat dilihat saat simulasi proses berlangsung, aplikasi terlihat tidak bisa discroll
alias freeze. Baru setelah 5 detik proses scrolling 5 detik. Hal ini karena Anda
menjalankan suatu proses yang lama di ui thread, sehingga Anda tidak dapat
melakukan interkasi UI lainnya ketika proses tersebut berlangsung. Hal ini
tentunya akan menyebabkan pengalaman user menjadi tidak nyaman karena
aplikasi terasa tertinggal (lagging).

Belum lagi jika proses tersebut berjalan sangat lama (misal 500 detik dengan
mengganti Thread Sleep menjadi 50000) maka aplikasi dipastikan akan
mengalami force close dikarenakan eror Application Not Responding (ANR).
Untuk mengatasinya, pada langkah selanjutnya Anda akan menggunakan
Executor untuk membuat thread baru dan Handler untuk update hasil dari
proses ke komponen UI berupa TextView.

Kita masuk ke kelas MainActivity dan ubah kode pada kelas tersebut menjadi
seperti berikut:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2.

3. override fun onCreate(savedInstanceState: Bundle?) {

4. [Link](savedInstanceState)

5. setContentView([Link].activity_main)

6.

7. val btnStart = findViewById<Button>([Link].btn_start)

8. val tvStatus = findViewById<TextView>([Link].tv_status)


9.

10. val executor = [Link]()

11. val handler = Handler([Link]())

12.

13. [Link] {

14. [Link] {

15. try {

16. //simulate process in background thread

17. for (i in 0..10) {

18. [Link](500)

19. val percentage = i * 10

20. [Link] {

21. //update ui in main thread

22. if (percentage == 100) {

23. [Link]([Link].task_completed)

24. } else {

25. [Link] = [Link](getString([Link]),


percentage)

26. }

27. }

28. }

29. } catch (e: InterruptedException) {

30. [Link]()

31. }

32. }

33. }

34. }

35. }
Selanjutnya jalankan kembali aplikasi Anda dan lihat perbedaannya.

Dapat dilihat sekarang latihan aplikasi dapat berjalan mulus saat menjalankan
suatu proses. Tidak ada lagi freeze dan lagging pada aplikasi Anda. User pun
mendapatkan pengalaman berinteraksi yang nyaman. Mantap!
Selamat! Anda sudah mencoba menjalankan background process dengan
menggunakan Executor. Kali ini khusus untuk Kotlin ada fitur spesial untuk
mengatur background process, yaitu dengan menggunakan Coroutine. Kita akan
mencoba me-refactor kode yang sudah dibuat dengan Executor + Handler
menjadi menggunakan Coroutine. Salah satu kelebihan menggunakan Coroutine
yaitu kita tidak memerlukan Callback lagi.

Untuk menggunakan Coroutine kita perlu menambahkan


dependencies terlebih dahulu di [Link](Module: app).

1. implementation "[Link]:kotlinx-coroutines-core:1.3.7"

2. implementation "[Link]:kotlinx-coroutines-android:1.3.9"

3. implementation "[Link]:lifecycle-runtime-ktx:2.2.0"

Kemudian ubah kode di bagian MainActivity menjadi seperti ini:

1. class MainActivity : AppCompatActivity() {

2.

3. override fun onCreate(savedInstanceState: Bundle?) {


4. [Link](savedInstanceState)

5. setContentView([Link].activity_main)

6.

7. val btnStart = findViewById<Button>([Link].btn_start)

8. val tvStatus = findViewById<TextView>([Link].tv_status)

9.

10. [Link] {

11. [Link]([Link]) {

12. //simulate process in background thread

13. for (i in 0..10) {

14. delay(500)

15. val percentage = i * 10

16. withContext([Link]) {

17. //update ui in main thread

18. if (percentage == 100) {

19. [Link]([Link].task_completed)

20. } else {

21. [Link] =
[Link](getString([Link]), percentage)

22. }

23. }

24. }

25. }

26. }

27. }

28. }

8. Kemudian jalankan kembali maka kita akan mendapatkan hasil yang


sama namun dengan kode yang lebih simpel dan ringkas.
Bedah Kode
Executor+Handler

Proyek yang kita kerjakan adalah contoh sederhana dari penggunaan


Executor untuk membuat proses berjalan secara asynchronous dan
Handler untuk tetap bisa berkomunikasi (synchronize) dengan ui thread.
Karena itulah di awal kita memerlukan kode untuk inisialisasi kedua
Object tersebut:

 Kotlin
 Java

1. val executor = [Link]()

2. val handler = Handler([Link]())

Di sini Anda menggunakan newSingleThreadExecutor yang artinya hanya


satu thread yang Anda buat. Sehingga ketika Anda klik tombol berkali-kali
hanya satu proses yang dijalankan dan proses lainnya akan dieksekusi
setelahnya proses sebelumnya selesai. Jika Anda ingin lebih dari satu
thread dan melihat perbedaannya, Anda bisa menggunakan
newFixedThreadPool atau newCachedThreadPool.

Kemudian pada Handler di sini Anda menggunakan getMainLooper karena


Anda ingin proses yang di dalam Handler dijalankan di main/ui thread.
Pada kasus lain, jika Anda ingin Handler berjalan dengan thread yang
sama dengan sebelumnya Anda bisa menggunakan myLooper.

Setelah kedua Object siap, berikut adalah kode untuk eksekusinya.

 Kotlin
 Java

1. [Link] {

2. try {

3. //simulate process in background thread

4. for (i in 0..10) {

5. [Link](500)

6. val percentage = i * 10

7. [Link] {
8. //update ui in main thread

9. if (percentage == 100) {

10. [Link]([Link].task_completed)

11. } else {

12. [Link] = [Link](getString([Link]),


percentage)

13. }

14. }

15. }

16. } catch (e: InterruptedException) {

17. [Link]()

18. }

19. }

Pada latihan ini Anda mensimulasikan sebuah proses compressing yang


panjang dengan menggunakan [Link]() dengan parameter dalam
satuan millisecond. Sehingga total waktu yang dibutuhkan yaitu 500 ms x
10 = 5000 ms / 5 detik.

Kemudian ketika akan menampilkan status pada sebuah TextView, Anda


tidak dapat melakukannya langsung dari background thread, melainkan
Anda harus melakukannya di main thread, karena itulah dibutuhkan
[Link] untuk berpindah antar thread tersebut.

Sederhana bukan? Dengan menggunakan asynchronous task untuk


menyelesaikan proses yang memakan waktu dan membutuhkan
komputasi intensif, aplikasi pun akan tetap responsif.

Coroutine

1. [Link]([Link]) { ... }

lifecycleScope merupakan scope yang sudah disediakan library lifecycle-


runtime-ktx untuk menjalankan coroutine pada Activity yang sudah
aware dengan lifecycle. Dengan begitu instance coroutine akan otomatis
dihapus ketika aplikasi dalam keadaan onDestroy sehingga aplikasi tidak
mengalami memory leak (kebocoran memori).

Selanjutnya di sini kita menggunakan method launch karena kita akan


memulai background process tanpa nilai kembalian alias fire and
forget. Sedangkan scope [Link] dipilih karena kita akan
melakukan proses biasa di Background Thread yang tidak memerlukan
proses read-write.

1. delay(500)

delay adalah method khusus yang berasal dari library coroutine. Secara
sekilas memiliki fungsi yang hampir mirip dengan [Link], namun
secara fundamental mereka sangatlah berbeda. Jika kita menggunakan
[Link] yaitu dia akan mem-block proses secara keseluruhan.
Sedangkan jika kita menggunakan delay, yang berhenti hanya yang di
dalam coroutine itu saja, sedangkann coroutine lain masih bisa dijalankan.

1. withContext([Link]) { ... }

Salah satu kelebihan coroutine yaitu kita bisa berpindah antar


thread lebih mudah. Di sini kita menggunakan
withContext([Link]) karena kita perlu pindah ke Main Thread
untuk update UI berupa TextView, jika tidak menggunakan ini, maka
UI/TextView tidak akan pernah ter-update.

Selamat! Kamu sudah belajar cukup jauh tentang fundamental aplikasi


Android.

Anda bisa mengunduh source code di atas di :

 Source Code Latihan Background Thread


Teori Service

Kita telah belajar mengenai activity dan implementasinya. Activity dan


fragment merupakan dua komponen yang memberikan pengalaman
langsung kepada pengguna. Pengguna dapat melihat dan berinteraksi di
atasnya.

Service berada pada sisi yang lain. Komponen ini tidak memiliki
antarmuka dan bahkan pengguna tidak akan tahu bagaimana ia bekerja.
Pengalaman yang diberikan oleh service hanya berupa proses yang tak
terlihat. Ia digunakan untuk menjalankan beragam macam proses yang
memakan waktu lama.

Walaupun berjalan secara background, pada dasarnya service dan


komponen Android lainnya running pada satu proses dan thread yang
sama yaitu main thread atau ui thread. Bekerja di background bukan
berarti ia bekerja secara asynchronous. Service tetap membutuhkan
thread terpisah jika kita ingin melakukan proses yang membutuhkan
komputasi intensif atau yang memakan waktu.

Contoh pemanfaatan service sudah banyak sekali, antara lain:

 Aplikasi sosial media atau aplikasi yang memiliki kemampuan untuk


menerima push notification. Aplikasi semacam ini pasti memiliki sebuah
service yang berjalan dalam posisi stand by untuk selalu menerima pesan
yang masuk.
 Aplikasi chat juga membutuhkan service untuk melakukan pengiriman dan
menerima pesan yang dikirimkan oleh pengguna.
 Aplikasi pemutar musik juga melakukan hal yang sama. Untuk
memberikan pengalaman yang lebih baik kepada pengguna, aplikasi
pemutar musik biasanya meletakkan proses streaming atau memainkan
musik di komponen service dengan tetap mempertahankan integrasi
dengan komponen lain, misalnya notifikasi. Seperti gambar berikut:
Secara umum, terdapat dua cara untuk memulai service:

1. Started Service
Service berjenis ini adalah tipe yang dijalankan oleh komponen lain, misal
activity. Sekali dijalankan, service ini akan berjalan selama belum
dimatikan atau proses yang dijalankan selesai. Service akan tetap berjalan
walaupun komponen yang lain dimatikan oleh sistem Android. Umumnya
penggunaan service ini adalah untuk melakukan proses yang tidak
memberikan nilai balik ke komponen yang memanggilnya. Contohnya
adalah, mengunduh atau mengunggah berkas.
2. Bound Service
Service jenis ini merupakan tipe service yang dijalankan oleh komponen
lain, namun saling mengikat. Hubungan yang terjadi antar kedua
komponen tersebut seperti client-server. Bisa saling menerima hasil dan
menerima request yang ada. Pada service ini dimungkinkan terjadinya
proses IPC (Interprocess Communication).
Service ini akan tetap berjalan di background selama masih ada
komponen lain yang mengikatnya. Jika tidak, maka service akan dimatikan
oleh sistem. Aplikasi pemutar musik merupakan salah satu jenis aplikasi
yang mengimplementasikan service jenis ini.

Pada bagian ini kita akan sepenuhnya membahas service berjenis started.
Tipe service tersebut akan dibagi menjadi dua bagian dalam
implementasinya:

 Kelas Service yang inherit langsung kepada kelas Service

Ketika sebuah kelas java inherit ke service ingin menjalankan proses


yang memakan waktu lama, maka kelas tersebut diharuskan
membuat thread terpisah agar tidak memblokade ui thread yang
ada. Service ini akan selalu hidup di background selama tidak ada
komponen yang memanggil stopService() atau dimatikan oleh
sistem.

 Kelas Service yang inherit ke JobIntentService

Ini adalah kelas yang sangat memudahkan hidup kita. Apa pasal? Ia
bersifat fire and forget. Setelah menunaikan tugasnya, ia akan
mematikan dirinya.

Poin-poin penting lain tentang service di antaranya:

 Setiap kelas Java dinyatakan sebuah service bila kelas tersebut


inherit/extends ke kelas Service atau IntentService.
 Service memiliki life cycle-nya sendiri dan bergantung pada tipe service
apa yang digunakan, started atau bound service.
 Untuk menjalankan service dari komponen lain seperti activity, cukup
gunakan startService(Intent).
Sebaliknya untuk mematikan/stop service terdapat dua cara. Pertama
stopService(Intent) dijalankan dari komponen yang memanggil dan
stopSelf() dari kelas Service itu sendiri.

Untuk mendalami topik Service lebih lanjut, Anda dapat membaca tautan
berikut:

 Services
 Component Services
 Service Element
Latihan Service
Tujuan

Codelab ini menitikberatkan pada implementasi kelas service untuk


melakukan aksi di latar belakang. Bagian dari codelab ini akan menjawab
beberapa pertanyaan umum dalam pengembangan aplikasi Android
sebagai berikut:

 Bagaimana penggunaan kelas service dalam android studio?


 Bagaimana cara memodifikasi kelas service dengan Intent Service?
 Bagaimana cara service berjalan dan berhenti?

Codelab selanjutnya adalah membuat aplikasi yang dapat menerapkan


kegunaan service. Seperti ini tampilannya.

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru.


2. Mengatur tampilan pada berkas layout XML.
3. Membuat kelas MyService untuk mengatur konfigurasi service.
4. Mengimplementasikan MyService ke dalam Activity untuk menjalankan
dan mengentikan service.
5. Menggunakan Executor Service (Java) atau Corotine (Kotlin) sebagai
pengelola background process untuk mematikan service yang berjalan.
6. Menjalankan aplikasi.
Codelab Service Sederhana

Ikuti langkah-langkah berikut ini.

1. Buat proyek dengan kriteria sebagai berikut:

Nama Project MyService

Target & Minimum Target Phone and Tablet, Api


SDK level 21

Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Android True

Language Kotlin/Java

2. Setelah proyek tercipta, lengkapi activity_main.xmldengan contoh


seperti ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:layout_width="match_parent"

5. android:layout_height="match_parent"

6. android:padding="16dp"

7. android:orientation="vertical"

8. tools:context=".MainActivity">

9.

10. <Button

11. android:id="@+id/btn_start_service"

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/start_service"

15. android:layout_marginBottom="16dp"/>
16.

17. <Button

18. android:id="@+id/btn_start_job_intent_service"

19. android:layout_width="match_parent"

20. android:layout_height="wrap_content"

21. android:text="@string/start_job_intent_service"

22. android:layout_marginBottom="16dp"/>

23.

24. <LinearLayout

25. android:layout_width="match_parent"

26. android:layout_height="wrap_content"

27. android:orientation="horizontal"

28. android:weightSum="2"

29. android:gravity="center">

30. <Button

31. android:id="@+id/btn_start_bound_service"

32. android:layout_width="0dp"

33. android:layout_height="wrap_content"

34. android:layout_marginEnd="16dp"

35. android:layout_weight="1"

36. android:text="@string/start_bound_service" />

37. <Button

38. android:id="@+id/btn_stop_bound_service"

39. android:layout_width="0dp"

40. android:layout_height="wrap_content"

41. android:layout_weight="1"

42. android:text="@string/stop_bound_service" />


43. </LinearLayout>

44. </LinearLayout>

Akan ada bagian eror pada bagian android:text. Nah, seperti pada modul
sebelumnya kita perlu tambahkan resource string-nya.

Masukkan beberapa
baris kode di bawah ini di dalam res → values → [Link].

1. <resources>

2. <string name="app_name">MyServiceApp</string>

3. <string name="start_service">Start Service</string>

4. <string name="start_job_intent_service">Start Job Intent


Service</string>

5. <string name="start_bound_service">Start Bound Service</string>

6. <string name="stop_bound_service">Stop bound Service</string>

7. </resources>

Resource string ini akan kita gunakan selama latihan service.

Pada MainActivitysilakan lengkapi kodenya untuk mengenalkan obyek yang


sudah kita masukkan. Simak berikut ini:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2.

3. override fun onCreate(savedInstanceState: Bundle?) {

4. [Link](savedInstanceState)

5. setContentView([Link].activity_main)

6.

7. val btnStartService = findViewById<Button>([Link].btn_start_service)

8. [Link] {

9.
10. }

11.

12. val btnStartJobIntentService =


findViewById<Button>([Link].btn_start_job_intent_service)

13. [Link] {

14.

15. }

16.

17. val btnStartBoundService =


findViewById<Button>([Link].btn_start_bound_service)

18. [Link] {

19.

20. }

21.

22. val btnStopBoundService =


findViewById<Button>([Link].btn_stop_bound_service)

23. [Link] {

24.

25. }

26. }

27. }

Selanjutnya buat kelas service bernama MyService dengan cara klik


kanan pada package project → New → Service → Service. MyService akan
inherit (extends) kepada kelas Service.
Selanjutnya pada dialog yang tampil, isikan nama kelas service yang diinginkan.
Di sini kita menamainya sebagai MyService dan pastikan exported dan enabled
tercentang. Klik Finish untuk menyelesaikan proses.

Selanjutnya buka berkas AndroidManifest pada package manifest


dan perhatikan isi berkas tersebut. Service yang baru saja kita buat, sudah ada
di dalam tag <application> :

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4.
5. <application

6. android:allowBackup="true"

7. android:icon="@mipmap/ic_launcher"

8. android:label="@string/app_name"

9. android:roundIcon="@mipmap/ic_launcher_round"

10. android:supportsRtl="true"

11. android:theme="@style/AppTheme">

12. <activity android:name=".MainActivity">

13. <intent-filter>

14. <action android:name="[Link]" />

15.

16. <category android:name="[Link]" />

17. </intent-filter>

18. </activity>

19.

20. <service

21. android:name=".MyService"

22. android:enabled="true"

23. android:exported="true"/>

24. </application>

25.

26. </manifest>

Kode pada AndroidManifest sudah dibuat secara otomatis. Dengan demikian


kita sudah bisa menjalankan kelas service tersebut. Namun, sebelum
menjalankan aplikasi, lengkapi kode pada MyServicemenjadi seperti berikut:

 Kotlin
 Java
1. class MyService : Service() {

2.

3. companion object {

4. internal val TAG = MyService::[Link]

5. }

6.

7. override fun onBind(intent: Intent): IBinder? {

8. throw UnsupportedOperationException("Not yet implemented")

9. }

10.

11. override fun onStartCommand(intent: Intent, flags: Int, startId: Int):


Int {

12. Log.d(TAG, "Service dijalankan...");

13. return START_STICKY

14. }

15. }

Selanjutnya pada MainActivity tambahkan baris berikut:

 Kotlin
 Java

1. [Link] {

2. val mStartServiceIntent = Intent(this, MyService::[Link])

3. startService(mStartServiceIntent)

4. }

Sekarang jalankan aplikasi. Klik tombol start service dan perhatikan pada log-
nya.

MyService telah dijalankan dan tak akan pernah mati sampai dimatikan oleh
sistem atau metode stopSelf() atau stopService() dijalankan.
Baik. Sekarang kita akan membuat sebuah background process seperti pada
modul sebelumnya untuk mematikan/menghentikan service yang telah
dijalankan dengan memanggil metode stopSelf()setelah 3 detik.

 Kotlin
 Java

Tambahkan terlebih dahulu library Coroutine di [Link] (Module: app):

1. dependencies {

2. ...

3. implementation "[Link]:kotlinx-coroutines-core:1.3.7"

4. implementation "[Link]:kotlinx-coroutines-
android:1.3.9"

5. }

Kemudian klik Sync Now di pojok kanan atas untuk men-download library.
Setelah selesai, di kelas MyService tambahkan kode berikut:

1. class MyService : Service() {

2.

3. companion object {

4. internal val TAG = MyService::[Link]

5. }

6.

7. private val serviceJob = Job()

8. private val serviceScope = CoroutineScope([Link] +


serviceJob)

9.

10. override fun onBind(intent: Intent): IBinder? {

11. throw UnsupportedOperationException("Not yet implemented")

12. }

13.

14. override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

15. Log.d(TAG, "Service dijalankan...")


16. [Link] {

17. delay(3000)

18. stopSelf()

19. Log.d(TAG, "Service dihentikan")

20. }

21. return START_STICKY

22. }

23.

24. override fun onDestroy() {

25. [Link]()

26. [Link]()

27. Log.d(TAG, "onDestroy: ")

28. }

29.

30. }
10. Jalankan aplikasinya. Klik tombol start service dan perhatikan log-
nya dengan kata kunci MyService. Service dijalankan secara
asynchronous dan mematikan dirinya sendiri setelah proses selesai.

[Link] berhasil dijalankan, log android monitor akan seperti ini:

Bedah Kode

Kita baru saja berkenalan dengan service melalui kelas MyService. Kita
inherit kelas dasar dari service seperti yang dijelaskan sebelumnya.
Service jenis ini berada pada thread yang sama yaitu ui thread.

StartServices

Service di atas dijalankan dengan cara berikut ini:


 Kotlin
 Java

1. val mStartServiceIntent = Intent(this@MainActivity, MyService::[Link])

2. startService(mStartServiceIntent)

Ingat! Bukan startActivity() seperti pada contoh sebelumnya. Namun


startService() karena kita menginginkan sebuah service yang berjalan.
Setelah dijalankan, metode onStartCommand() pada MyService akan
dijalankan.

 Kotlin
 Java

1. override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

2. Log.d(TAG, "Service dijalankan...")

3. [Link] {

4. delay(3000)

5. stopSelf()

6. Log.d(TAG, "Service dihentikan")

7. }

8. return START_STICKY

9. }

Pada metode tersebut kita menjalankan sebuah background process


untuk melakukan simulasi proses yang sulit. Dan ia berjalan secara
asynchronous.

Kekurangan dari service tipe ini adalah ia tak menyediakan


background thread diluar ui thread secara default. Jadi tiada cara lainnya
selain membuat thread secara sendiri.

START_STICKY menandakan bahwa bila service tersebut dimatikan oleh


sistem Android karena kekurangan memori, ia akan diciptakan kembali
jika sudah ada memori yang bisa digunakan. Metode onStartCommand()
juga akan kembali dijalankan.

stopSelf() berfungsi untuk memberhentikan atau mematikan MyService


dari sistem Android.
Latihan JobIntentService

Pada titik ini Anda sudah bisa membuat sebuah service yang berjalan di
background. Nah, selenjutnya Anda akan belajar menggunakan
JobIntentService untuk melakukan suatu aksi yang dapat berhenti sendiri
di background thread.

1. Sekarang buat kembali sebuah service dengan nama


MyJobIntentService dengan cara klik kanan pada package utama
project → New → Service → Service (Intent Service).

2. Pada dialog yang tampil masukkan nama MyJobIntentService seperti ini:


Klik Finish untuk menyelesaikan proses. Pastikan untuk
tidak mencentang include helper. Include helper akan menambahkan
metode yang dapat membantu di dalam pembuatan services. Akan tetapi
pada modul ini kita tidak menggunakan metode tersebut.

3. Perhatikan pada [Link] dan tambahkan permission berikut


pada komponen service yang baru saja dibuat.

1. <service

2. android:name=".MyJobIntentService"

3. android:permission="[Link].BIND_JOB_SERVICE"

4. android:exported="false" />

Setelah kelas MyJobIntentService diciptakan, terdapat banyak kode auto-


generatedyang telah disertakan. Kita tidak membutuhkan semua metode
tersebut. Sehingga ubah kelas tersebut menjadi seperti contoh di bawah ini:

 Kotlin
 Java

1. class MyJobIntentService : JobIntentService() {

2.

3. override fun onHandleWork(intent: Intent) {

4.

5. }

6.

7. }

Skenarionya adalah sebagai berikut:

 Kita menjalankan IntentService tersebut dengan sebuah obyek Intent dari


MainActivity dengan membawa data, dalam konteks ini adalah nilai
integer yang menentukan berapa lama background process akan
dijalankan.
 MyIntentService dijalankan dan kemudian memproses obyek Intent yang
dikirimkan untuk menjalankan background.
 Seperti sifatnya, IntentService tidak perlu mematikan dirinya sendiri.
Secara otomatis ketika proses yang dilakukan selesai, maka
JobIntentService berhenti dengan sendirinya.

Pada MyJobIntentService lengkapilah kodenya, fungsi onHandleWork dan


onDestroy menjadi seperti berikut:
 Kotlin
 Java

1. class MyJobIntentService : JobIntentService() {

2.

3. companion object {

4. private const val JOB_ID = 1000

5. internal const val EXTRA_DURATION = "extra_duration"

6. private val TAG = MyJobIntentService::[Link]

7.

8. fun enqueueWork(context: Context, intent: Intent){

9. enqueueWork(context, MyJobIntentService::[Link], JOB_ID,


intent)

10. }

11. }

12.

13. override fun onHandleWork(intent: Intent) {

14. Log.d(TAG, "onHandleWork: Mulai.....")

15. val duration = [Link](EXTRA_DURATION, 0)

16. try {

17. [Link](duration)

18. Log.d(TAG, "onHandleWork: Selesai.....")

19. } catch (e: InterruptedException) {

20. [Link]()

21. [Link]().interrupt()

22. }

23. }

24.

25. override fun onDestroy() {


26. [Link]()

27. Log.d(TAG, "onDestroy()")

28. }

29.

30. }

Selanjutnya pada MainActivity di metode onClick()


bagian [Link].btn_start_intent_service, kita tambahkan tiga baris berikut untuk
menjalankan JobIntentService yang baru saja dibuat.

 Kotlin
 Java

1. [Link] {

2. val mStartIntentService = Intent(this,


MyJobIntentService::[Link])

3.
[Link](MyJobIntentService.EXTRA_DURATIO
N, 5000L)

4. [Link](this, mStartIntentService)

5. }
7. Setelah selesai semua, silakan jalankan kembali aplikasinya dan
perhatikan proses yang terekam pada log aplikasi di Android Studio.

Bedah Kode

Kita tinjau kembali skenarionya. Kita menjalankan JobIntentService


tersebut dengan sebuah obyek intent yang membawa data dari
MainActivity. Kita dapat menentukan berapa lama service ini akan
berjalan.

JobIntentService

 Kotlin
 Java
1. val mStartIntentService = Intent(this, MyJobIntentService::[Link])

2. [Link](MyJobIntentService.EXTRA_DURATION, 5000L)

3. [Link](this, mStartIntentService)

MyJobIntentService dijalankan. Service tersebut akan melakukan


pemrosesan obyek Intent yang dikirimkan dan menjalankan suatu proses
yang berjalan di background.

 Kotlin
 Java

1. override fun onHandleWork(intent: Intent) {

2. Log.d(TAG, "onHandleWork: Mulai.....")

3. val duration = [Link](EXTRA_DURATION, 0)

4. try {

5. [Link](duration)

6. Log.d(TAG, "onHandleWork: Selesai.....")

7. } catch (e: InterruptedException) {

8. [Link]()

9. [Link]().interrupt()

10. }

11. }

Kode di atas akan dijalankan pada thread terpisah secara asynchronous.


Jadi kita tak lagi perlu membuat background thread seperti pada service
sebelumnya.

Terakhir, IntentService tak perlu mematikan dirinya sendiri. Service ini


akan berhenti dengan sendirinya ketika sudah selesai menyelesaikan
tugasnya.
Latihan Bound Service

Pada titik ini Anda sudah bisa membuat sebuah service dan
Intent service yang berjalan di background.

1. Sekarang buat kembali sebuah service dengan nama MyBoundService.


Carannya klik kanan pada package utama project → New → Service →
Service (Service).

2. Selanjutnya pada dialog yang tampil isikan nama kelas service yang
diinginkan. Di sini kita menamainya sebagai MyBoundService dan
pastikan exported dan enabled tercentang. Klik Finish untuk
menyelesaikan proses.
3. Setelah terbentuk, kode dari kelas MyBoundService adalah seperti ini.

o Kotlin
o Java

1. class MyBoundService : Service() {

2.

3. override fun onBind(intent: Intent): IBinder {

4. TODO("Return the communication channel to the service.")

5. }

6. }

Selanjutnya kita buat kelas MyBinder yang kita warisi dari kelas Binder di dalam
kelas MyBoundService.

 Kotlin
 Java

1. class MyBoundService : Service() {

2.

3. companion object {

4. private val TAG = MyBoundService::[Link]

5. }

6. private var mBinder = MyBinder()

7. private val startTime = [Link]()

8.

9. internal inner class MyBinder : Binder() {

10. val getService: MyBoundService = this@MyBoundService

11. }

12.

13. ...

14. }

Setelah membuat kelas MyBinder, kita buat sebuah variabel untuk menghitung
mundur. Kita gunakan bantuan CountTimerDown seperti berikut ini.
 Kotlin
 Java

1. class MyBoundService : Service() {

2.

3. ...

4.

5. private var mTimer: CountDownTimer = object :


CountDownTimer(100000, 1000) {

6. override fun onTick(l: Long) {

7. val elapsedTime = [Link]() - startTime

8. Log.d(TAG, "onTick: $elapsedTime")

9. }

10.

11. override fun onFinish() {

12.

13. }

14. }

15. }

Selanjutnya kita bisa menerapkan CountTimerDown ke dalam kelas


MyBoundService. Gunanya untuk memulai service.

 Kotlin
 Java

1. class MyBoundService : Service() {

2.

3. ...

4.

5. override fun onCreate() {

6. [Link]()

7. Log.d(TAG, "onCreate: ")


8. }

9.

10. override fun onBind(intent: Intent): IBinder? {

11. Log.d(TAG, "onBind: ")

12. [Link]()

13. return mBinder

14. }

15.

16. override fun onDestroy() {

17. [Link]()

18. Log.d(TAG, "onDestroy: ")

19. }

20.

21. override fun onUnbind(intent: Intent): Boolean {

22. Log.d(TAG, "onUnbind: ")

23. [Link]()

24. return [Link](intent)

25. }

26.

27. override fun onRebind(intent: Intent) {

28. [Link](intent)

29. Log.d(TAG, "onRebind: ")

30. }

31. }

Maka kelas MyBoundService menjadi seperti ini:

 Kotlin
 Java
1. class MyBoundService : Service() {

2.

3. companion object {

4. private val TAG = MyBoundService::[Link]

5. }

6. private var mBinder = MyBinder()

7. private val startTime = [Link]()

8.

9. private var mTimer: CountDownTimer = object : CountDownTimer(100000,


1000) {

10. override fun onTick(l: Long) {

11. val elapsedTime = [Link]() - startTime

12. Log.d(TAG, "onTick: $elapsedTime")

13. }

14.

15. override fun onFinish() {

16.

17. }

18. }

19.

20. override fun onCreate() {

21. [Link]()

22. Log.d(TAG, "onCreate: ")

23. }

24.

25. override fun onBind(intent: Intent): IBinder? {

26. Log.d(TAG, "onBind: ")

27. [Link]()
28. return mBinder

29. }

30.

31. override fun onDestroy() {

32. [Link]()

33. Log.d(TAG, "onDestroy: ")

34. }

35.

36. override fun onUnbind(intent: Intent): Boolean {

37. Log.d(TAG, "onUnbind: ")

38. [Link]()

39. return [Link](intent)

40. }

41.

42. override fun onRebind(intent: Intent) {

43. [Link](intent)

44. Log.d(TAG, "onRebind: ")

45. }

46.

47. internal inner class MyBinder : Binder() {

48. val getService: MyBoundService = this@MyBoundService

49. }

50. }

Selanjutnya pada MainActivity kita akan membuat ServiceConnection untuk


menghubungkan MainActivity dengan MyBoundService seperti berikut ini:

 Kotlin
 Java
1. class MainActivity : AppCompatActivity() {

2.

3. private var mServiceBound = false

4. private lateinit var mBoundService: MyBoundService

5.

6. private val mServiceConnection = object : ServiceConnection {

7.

8. override fun onServiceDisconnected(name: ComponentName) {

9. mServiceBound = false

10. }

11.

12. override fun onServiceConnected(name: ComponentName, service:


IBinder) {

13. val myBinder = service as MyBinder

14. mBoundService = [Link]

15. mServiceBound = true

16. }

17. }

18. ...

19. }

Setelah membuat penghubung kelas MyBoundService dengan MainActivity, kita


akan memberi aksi untuk button memulai dan mengakhiri MyBoundService
seperti berikut ini:

 Kotlin
 Java

1. val btnStartBoundService = findViewById<Button>([Link].btn_start_bound_service)

2. [Link] {

3. val mBoundServiceIntent = Intent(this, MyBoundService::[Link])


4. bindService(mBoundServiceIntent, mServiceConnection,
BIND_AUTO_CREATE)

5. }

6.

7. val btnStopBoundService = findViewById<Button>([Link].btn_stop_bound_service)

8. [Link] {

9. unbindService(mServiceConnection)

10. }

Tambahkan beberapa kode di metode onDestroy(). Tujuannya agar ketika


aplikasi sudah keluar, service akan berhenti secara otomatis.

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2.

3. ...

4.

5. override fun onDestroy() {

6. [Link]()

7. if (mServiceBound) {

8. unbindService(mServiceConnection)

9. }

10. }

11. }

Maka kelas MainActivity menjadi seperti ini:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2.

3. private var mServiceBound = false


4. private lateinit var mBoundService: MyBoundService

5.

6. private val mServiceConnection = object : ServiceConnection {

7.

8. override fun onServiceDisconnected(name: ComponentName) {

9. mServiceBound = false

10. }

11.

12. override fun onServiceConnected(name: ComponentName, service:


IBinder) {

13. val myBinder = service as MyBinder

14. mBoundService = [Link]

15. mServiceBound = true

16. }

17.

18. }

19.

20. override fun onCreate(savedInstanceState: Bundle?) {

21. [Link](savedInstanceState)

22. setContentView([Link].activity_main)

23.

24. val btnStartService = findViewById<Button>([Link].btn_start_service)

25. [Link] {

26. val mStartServiceIntent = Intent(this, MyService::[Link])

27. startService(mStartServiceIntent)

28. }

29.
30. val btnStartJobIntentService =
findViewById<Button>([Link].btn_start_job_intent_service)

31. [Link] {

32. val mStartIntentService = Intent(this,


MyJobIntentService::[Link])

33.
[Link](MyJobIntentService.EXTRA_DURATION,
5000L)

34. [Link](this, mStartIntentService)

35. }

36.

37. val btnStartBoundService =


findViewById<Button>([Link].btn_start_bound_service)

38. [Link] {

39. val mBoundServiceIntent = Intent(this,


MyBoundService::[Link])

40. bindService(mBoundServiceIntent, mServiceConnection,


BIND_AUTO_CREATE)

41. }

42.

43. val btnStopBoundService =


findViewById<Button>([Link].btn_stop_bound_service)

44. [Link] {

45. unbindService(mServiceConnection)

46. }

47. }

48.

49. override fun onDestroy() {

50. [Link]()

51. if (mServiceBound) {

52. unbindService(mServiceConnection)

53. }
54. }

55. }
11. Sekarang jalankan aplikasi. Klik tombol Start Bound Service
kemudian klik tombol Stop Bound Service dan perhatikan pada log-nya.

Bedah Kode
MyBoundService

Kita baru saja berkenalan dengan service melalui kelas


MyBoundService. Kita inherit kelas dasar dari service seperti yang
dijelaskan sebelumnya. Service jenis ini berada pada thread yang sama
yaitu ui thread.

Service di atas dijalankan dengan cara berikut ini:

 Kotlin
 Java

1. val mBoundServiceIntent = Intent(this@MainActivity, MyBoundService::[Link])

2. bindService(mBoundServiceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE)

Pada kode di atas kita menggunakan bindService yang digunakan untuk


memulai mengikat kelas MyBoundService ke kelas MainActivity.
Sedangkan mBoundServiceIntent adalah sebuah intent eksplisit yang
digunakan untuk menjalankan komponen dari dalam sebuah aplikasi.
Sedangkan mServiceConnection adalah sebuah ServiceConnection
berfungsi sebagai callback dari kelas MyBoundService. Kemudian ada
juga BIND_AUTO_CREATE yang membuat sebuah service jika service tersebut
belum aktif. Selain BIND_AUTO_CREATE, fungsi-fungsi lain yang
memungkinkan adalah:

 BIND_ABOVE_CLIENT : yang digunakan ketika sebuah service lebih penting


daripada aplikasi itu sendiri.
 BIND_ADJUST_WITH_ACTIVITY : saat mengikat sebuah service dari activity,
maka ia akan mengizinkan untuk menargetkan service mana yang lebih
penting berdasarkan activity yang terlihat oleh pengguna.
 BIND_ALLOW_OOM_MANAGEMENT : memungkinkan untuk mengikat
service hosting untuk mengatur memori secara normal.
 BIND_AUTO_CREATE : secara otomatis membuat service selama binding-
nya aktif.
 BIND_DEBUG_UNBIND : berfungsi sebagai bantuan ketika debug
mengalami masalah pada pemanggilan unBind.
 BIND_EXTERNAL_SERVICE : merupakan service yang terikat dengan
service eksternal yang terisolasi.
 BIND_IMPORTANT : service ini sangat penting bagi klien, jadi harus dibawa
ke tingkat proses foreground.
 BIND_NOT_FOREGROUND : pada service ini tak disarankan untuk
mengubah ke tingkat proses foreground.
 BIND_WAIVE_PRIORITY : service ini tidak akan mempengaruhi penjadwalan
atau prioritas manajemen memori dari target proses layanan hosting.

Lihat kode pada mServiceConnection berikut ini:

 Kotlin
 Java

1. private val mServiceConnection = object : ServiceConnection {

2.

3. override fun onServiceDisconnected(name: ComponentName) {

4. mServiceBound = false

5. }

6.

7. override fun onServiceConnected(name: ComponentName, service: IBinder) {

8. val myBinder = service as MyBinder

9. mBoundService = [Link]

10. mServiceBound = true

11. }

12.

13. }

Kode di atas merupakan sebuah listener untuk menerima callback dari


ServiceConnetion. Kalau dilihat ada dua callback, yakni ketika mulai
terhubung dengan kelas service dan juga ketika kelas service sudah
terputus.

Binder

 Kotlin
 Java

1. internal inner class MyBinder : Binder() {

2. val getService: MyBoundService = this@MyBoundService

3. }

Kode di atas adalah kelas yang dipanggil di metode onServiceConnected


untuk memanggil kelas service. Fungsinya untuk mengikat kelas service.
Kelas MyBinder yang diberi turunan kelas Binder, mempunyai fungsi untuk
melakukan mekanisme pemanggilan prosedur jarak jauh.

Perhatikan kode onDestroy() yang ada di MainActivity.

 Kotlin
 Java

1. override fun onDestroy() {

2. [Link]()

3. if (mServiceBound) {

4. unbindService(mServiceConnection)

5. }

6. }

Kode onDestroy() seperti yang dijelaskan di metode sebelumnya, akan


memanggil unBindService atau melakukan pelepasan service dari Activity.
Pemanggilan unbindService di dalam onDestroy ditujukan untuk
mencegah memory leaks dari bound services.

Selanjutnya di dalam MyBoundService terdapat mTimer.

 Kotlin
 Java

1. private var mTimer: CountDownTimer = object : CountDownTimer(100000, 1000)


{

2. override fun onTick(l: Long) {

3. val elapsedTime = [Link]() - startTime

4. Log.d(TAG, "onTick: $elapsedTime")

5. }
6.

7. override fun onFinish() {

8.

9. }

10. }

Ketika CountDownTimer dijalankan, countdown timer akan berjalan


sampai 100.000 milisecond atau 100 detik. Intervalnya setiap 1.000
milisecond atau 1 detik akan menampilkan log. Hitungan mundur tersebut
berfungsi untuk melihat proses terikatnya kelas MyBoundService ke
MainActivity.

Kemudian perhatikan metode metode yang ada di kelas


MyBoundService.

 Kotlin
 Java

1. override fun onCreate() {

2. [Link]()

3. Log.d(TAG, "onCreate: ")

4. }

Metode onCreate() dipanggil ketika memulai pembentukan kelas


MyBoundService.

Setelah onCreate() terjadi maka akan lanjut ke onBind().

 Kotlin
 Java

1. override fun onBind(intent: Intent): IBinder? {

2. Log.d(TAG, "onBind: ")

3. [Link]()

4. return mBinder

5. }

Pada metode onBind(), service akan berjalan dan diikatkan atau


ditempelkan dengan activity pemanggil. Pada metode ini juga, mTimer
akan mulai berjalan.
Setelah service mulai terikat maka mTimer akan berjalan sesuai dengan
yang ditentukan. Selanjutnya untuk mengakhiri MyBoundService yang
masih terikat, bisa gunakan kode berikut:

 Kotlin
 Java

1. unbindService(mServiceConnection)

Kode di atas berfungsi untuk melepaskan service dari activity pemanggil.


Secara tidak langsung maka ia akan memanggil metode onUnbind yang
ada di kelas MyBoundService.

 Kotlin
 Java

1. override fun onUnbind(intent: Intent): Boolean {

2. Log.d(TAG, "onUnbind: ")

3. [Link]()

4. return [Link](intent)

5. }

Kode di atas berfungsi untuk melepaskan service dari activity pemanggil.


Kemudian setelah metode onUnbind dipanggil, maka ia akan memanggil
metode onDestroy() di kelas MyBoundService.

 Kotlin
 Java

1. override fun onDestroy() {

2. [Link]()

3. Log.d(TAG, "onDestroy: ")

4. }

Metode onDestroy() yang ada di MyBoundService ini berfungsi untuk


melakukan penghapusan kelas MyBoundService dari memori. Jadi
setelah service sudah terlepas dari kelas MainActivity, kelas
MyBoundService juga terlepas dari memori android.

Sampai titik ini Anda sudah mempelajari teori dasar dari service. Pada
modul berikutnya Anda akan menerapkan service pada kasus yang lebih
nyata.

Silakan unduh source code dari contoh di atas di tautan berikut ini.
 Source Code Latihan Service
Teori BroadcastReceiver

Ketika mengembangkan sebuah aplikasi di Android, adakalanya kita butuh


menjalankan suatu proses tertentu ketika sebuah proses lain telah terjadi.
Misal kita ingin menjalankan sesuatu ketika device Android sudah selesai
booting dengan sempurna. Kemampuan ini dapat kita manfaatkan untuk
memberi pengalaman yang lebih baik.

Di sinilah komponen bernama Broadcast Receiver berperan penting.

Menurut definisinya, broadcast receiver adalah cara sederhana untuk


menindaklanjuti sebuah broadcast message yang dipancarkan oleh
dirinya sendiri, aplikasi lain atau sistem Android. Sementara
itu, broadcast message adalah pesan yang akan dipancarkan melalui
obyek Intent.

Saat ini banyak aplikasi yang menggunakan validasi pengguna dengan


mengirimkan kode verifikasi melalui SMS. Tanpa disadari, ketika SMS
tersebut masuk dan ia berasal dari provider aplikasi, maka akan ada
komponen dari aplikasi lain yang akan menindaklanjuti dan memproses
pesan tadi. Misalnya dengan menkonfirmasi proses validasi tersebut
dengan cara menghubungi sebuah API di server.

Interaksi antara BroadcastReceiver dengan sitem Android dapat


digambarkan dengan ilustrasi berikut ini:

Untuk mendalami lebih lanjut tentang broadcast receiver, Anda dapat


membuka tautan berikut ini:

 BroadcastReceiver
 BroadcastReceiver Article
 BroadcastReceiver Article 2
Latihan BroadcastReceiver dari Event SMS
Tujuan

Pada materi kali ini Anda akan belajar menerapkan dua mekanisme umum
dalam memanfaatkan komponen broadcast receiver yaitu :

 Implementasi broadcast receiver untuk menerima event yang di-


broadcast oleh sistem Android. Kali ini kita akan menerima broadcast
ketika ada SMS yang masuk.
 Implementasi broadcast receiver untuk komunikasi antar aplikasi.

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama MyBroadcastReceiver .


2. Mengatur tampilan pada berkas layout XML.
3. Membuat kelas FileModel untuk menampung sementara data pengguna.
4. Membuat kelas SmsReceiver untuk mengatur konfigurasi broadcast
receiver.
5. Mengimplementasikan SmsReceiver ke dalam Activity.
6. Menambahkan permission untuk mengakses SMS.
7. Menjalankan aplikasi dan memicu munculnya SMS.

Latihan BroadcastReceiver dari Event SMS

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:

Nama Project MyBroadcastReceiver

Target & Minimum Target Phone and Tablet, Api


SDK level 21
Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Android True

Language Kotlin/Java

2. Setelah terbentuk proyek, kita lihat activity_main.xml dan ubah


komponen TextViewmenjadi:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:padding="16dp"

8. android:orientation="vertical"

9. tools:context="[Link]">

10.

11. <TextView

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/this_sms_receiver_app"

15. android:layout_marginBottom="16dp" />

16.

17. <Button

18. android:id="@+id/btn_permission"

19. android:layout_width="match_parent"

20. android:layout_height="wrap_content"

21. android:text="@string/check_sms_permission" />


22. </LinearLayout>

Akan ada yang eror pada bagian android:text. Seperti pada modul sebelumnya
kita perlu menambahkan resource string-nya.

Tambahkan beberapa baris kode di bawah ini di dalam res → values →


[Link].

1. <resources>

2. <string name="app_name">MyBroadcastReceiver</string>

3. <string name="this_sms_receiver_app">Ini Aplikasi Sms


Receiver</string>

4. <string name="download_file">Download a File</string>

5. <string name="coming_from">Coming From</string>

6. <string name="this_play">Oii, main kesini jadi ngga? sekarang


yak.</string>

7. <string name="close">Tutup</string>

8. <string name="check_sms_permission">Check SMS


Permission</string>

9. <string name="incoming_message">Incoming Message</string>

10. <string name="from">from : %s</string>

11. </resources>

Resource string ini akan kita gunakan selama latihan broadcast receiver.

Selanjutnya kita tambahkan listener pada button yang sudah kita buat di
activity_main.xml ke dalam MainActivity.

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.
3. private var binding: ActivityMainBinding? = null

4.

5. override fun onCreate(savedInstanceState: Bundle?) {

6. [Link](savedInstanceState)

7.

8. binding = [Link](layoutInflater)

9. setContentView(binding?.root)

10.

11. binding?.btnPermission?.setOnClickListener(this)

12.

13. }

14.

15. override fun onClick(v: View) {

16. when ([Link]) {

17.

18. }

19. }

20.

21. override fun onDestroy() {

22. [Link]()

23.

24. binding = null

25. }

26. }

Jangan lupa untuk menambahkan konfigurasi untuk mengaktifkan ViewBinding


pada [Link] (module: app) seperti berikut:

1. android {

2.
3. ...

4.

5. buildFeatures {

6. viewBinding = true

7. }

8. }

Kemudian buatlah sebuah activity baru bernama SmsReceiverActivity.

Pada activity_sms_receiver.xml sempurnakan kode yang ada menjadi seperti


berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_sms_receiver"

5. android:layout_width="match_parent"

6. android:layout_height="wrap_content"

7. android:orientation="vertical"

8. android:padding="16dp"
9.
tools:context="[Link]
ty">

10.

11. <TextView

12. android:id="@+id/tv_from"

13. android:layout_width="match_parent"

14. android:layout_height="wrap_content"

15. android:layout_marginBottom="16dp"

16. android:text="@string/coming_from" />

17.

18. <TextView

19. android:id="@+id/tv_message"

20. android:layout_width="match_parent"

21. android:layout_height="wrap_content"

22. android:layout_marginBottom="16dp"

23. android:text="@string/this_play"

24. android:textSize="18sp" />

25.

26. <Button

27. android:id="@+id/btn_close"

28. android:layout_width="wrap_content"

29. android:layout_height="?actionBarSize"

30. android:padding="8dp"

31. android:layout_marginBottom="24dp"

32. android:layout_gravity="end"

33. android:text="@string/close" />

34. </LinearLayout>
Lalu buka berkas [Link] pada direktori resource res → values dan
tambahkan style baru seperti ini:

1. <style name="[Link]"
parent="[Link]" />

Sehingga kode dari [Link] secara keseluruhan akan menjadi seperti ini:

1. <resources xmlns:tools="[Link]

2. <!-- Base application theme. -->

3. <style name="[Link]"
parent="[Link]">

4. <!-- Primary brand color. -->

5. <item name="colorPrimary">@color/purple_500</item>

6. <item name="colorPrimaryVariant">@color/purple_700</item>

7. <item name="colorOnPrimary">@color/white</item>

8. <!-- Secondary brand color. -->

9. <item name="colorSecondary">@color/teal_200</item>

10. <item name="colorSecondaryVariant">@color/teal_700</item>

11. <item name="colorOnSecondary">@color/black</item>

12. <!-- Status bar color. -->

13. <item name="android:statusBarColor"


tools:targetApi="l">?attr/colorPrimaryVariant</item>

14. <!-- Customize your theme here. -->

15. </style>

16.

17. <style name="[Link]"


parent="[Link]" />

18. </resources>

Idenya adalah menampilkan sebuah Activity seolah-olah menjadi sebuah dialog.


Kemudian pasang style yang sudah kita buat ke SmsReceiverActivity pada
AndroidManifest.

1. <activity

2. android:name=".SmsReceiverActivity"

3. android:theme="@style/[Link]" />

Langkah selanjutnya adalah dengan menambahkan kode di


SmsReceiverActivity untuk memberi aksi pada Textview dan Button yang
sudah kita masukkan di activity_sms_receiver.xml. Sehingga kodenya menjadi
seperti berikut ini:

 Kotlin
 Java

1. class SmsReceiverActivity : AppCompatActivity() {

2.

3. companion object {

4. const val EXTRA_SMS_NO = "extra_sms_no"

5. const val EXTRA_SMS_MESSAGE = "extra_sms_message"

6. }

7.

8. private var binding: ActivitySmsReceiverBinding? = null

9.

10. override fun onCreate(savedInstanceState: Bundle?) {

11. [Link](savedInstanceState)

12.

13. binding = [Link](layoutInflater)

14. setContentView(binding?.root)

15.

16. title = getString([Link].incoming_message)

17.

18. binding?.btnClose?.setOnClickListener {
19. finish()

20. }

21.

22. val senderNo = [Link](EXTRA_SMS_NO)

23. val senderMessage = [Link](EXTRA_SMS_MESSAGE)

24.

25. binding?.tvFrom?.text = getString([Link], senderNo)

26. binding?.tvMessage?.text = senderMessage

27. }

28.

29. override fun onDestroy() {

30. [Link]()

31. binding = null

32. }

33. }

Selanjutnya kita akan membuat sebuah kelas Receiver dengan cara klik kanan
pada package project utama → New → Other → BroadcastReceiver.
Pada dialog yang tampil isikan SmsReceiver dan klik Finish untuk
menyelesaikan.

Setelah tercipta secara otomatis kelas SmsReceiver yang kita buat sudah
teregistrasi di berkas AndroidManifest.

1. <receiver

2. android:name=".SmsReceiver"

3. android:enabled="true"

4. android:exported="true"/>

Namun jika Anda membuat Kelas SmsReceiver secara manual, Anda harus
memasukkan kode di atas secara manual juga. Perikasa kembali
AndroidManifest Anda.

Kemudian kita akan menambahkan kode untuk menerima data dari pesan yang
masuk.

 Kotlin
 Java

1. class SmsReceiver : BroadcastReceiver() {

2.
3. companion object {

4. private val TAG = SmsReceiver::[Link]

5. }

6.

7. override fun onReceive(context: Context, intent: Intent) {

8.

9. }

10.

11. private fun getIncomingMessage(aObject: Any, bundle: Bundle):


SmsMessage {

12. val currentSMS: SmsMessage

13. val format = [Link]("format")

14. currentSMS = if ([Link].SDK_INT >= 23) {

15. [Link](aObject as ByteArray, format)

16. } else [Link](aObject as ByteArray)

17. return currentSMS

18. }

19. }

Setelah metode getIncomingMessage dibuat, ia akan diterapkan ke metode


onReceive di kelas SmsReceiver.

 Kotlin
 Java

1. class SmsReceiver : BroadcastReceiver() {

2.

3. companion object {

4. private val TAG = SmsReceiver::[Link]

5. }

6.
7. override fun onReceive(context: Context, intent: Intent) {

8. val bundle = [Link]

9. try {

10. if (bundle != null) {

11. /*

12. Bundle dengan key "pdus" sudah merupakan standar yang


digunakan oleh system

13. */

14. val pdusObj = [Link]("pdus") as Array<*>

15. for (aPdusObj in pdusObj) {

16. val currentMessage = getIncomingMessage(aPdusObj as


Any, bundle)

17. val senderNum = [Link]

18. val message = [Link]

19. Log.d(TAG, "senderNum: $senderNum; message:


$message")

20. val showSmsIntent = Intent(context,


SmsReceiverActivity::[Link])

21. [Link] = Intent.FLAG_ACTIVITY_NEW_TASK

22.
[Link](SmsReceiverActivity.EXTRA_SMS_NO,
senderNum)

23.
[Link](SmsReceiverActivity.EXTRA_SMS_MESSAGE,
message)

24. [Link](showSmsIntent)

25. }

26. }

27. } catch (e: Exception) {

28. Log.d(TAG, "Exception smsReceiver $e")

29. }

30. }
31.

32. ...

33. }

Maka kelas SmsReceiver menjadi seperti berikut:

 Kotlin
 Java

1. class SmsReceiver : BroadcastReceiver() {

2.

3. companion object {

4. private val TAG = SmsReceiver::[Link]

5. }

6.

7. override fun onReceive(context: Context, intent: Intent) {

8. val bundle = [Link]

9. try {

10. if (bundle != null) {

11. /*

12. Bundle dengan key "pdus" sudah merupakan standar yang digunakan
oleh system

13. */

14. val pdusObj = [Link]("pdus") as Array<*>

15. for (aPdusObj in pdusObj) {

16. val currentMessage = getIncomingMessage(aPdusObj as Any, bundle)

17. val senderNum = [Link]

18. val message = [Link]

19. Log.d(TAG, "senderNum: $senderNum; message: $message")

20. val showSmsIntent = Intent(context, SmsReceiverActivity::[Link])

21. [Link] = Intent.FLAG_ACTIVITY_NEW_TASK


22. [Link](SmsReceiverActivity.EXTRA_SMS_NO,
senderNum)

23. [Link](SmsReceiverActivity.EXTRA_SMS_MESSAGE,
message)

24. [Link](showSmsIntent)

25. }

26. }

27. } catch (e: Exception) {

28. Log.d(TAG, "Exception smsReceiver $e")

29. }

30. }

31.

32. private fun getIncomingMessage(aObject: Any, bundle: Bundle): SmsMessage {

33. val currentSMS: SmsMessage

34. val format = [Link]("format")

35. currentSMS = if ([Link].SDK_INT >= 23) {

36. [Link](aObject as ByteArray, format)

37. } else [Link](aObject as ByteArray)

38. return currentSMS

39. }

40. }

Selanjutnya pada berkas AndroidManifest tambahkan permission di atas tag


seperti ini:

1. <uses-permission android:name="[Link].RECEIVE_SMS"/>

2. <uses-permission android:name="[Link].READ_SMS" />

Dan tambahkan baris intent-filterdi dalam tag yang kita punya seperti di bawah
ini:

1. <intent-filter>
2. <action android:name="[Link].SMS_RECEIVED"
/>

3. </intent-filter>

Sehingga berkas [Link] akan menjadi seperti ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4. <uses-permission android:name="[Link].RECEIVE_SMS"
/>

5. <uses-permission android:name="[Link].READ_SMS" />

6. <application

7. android:allowBackup="true"

8. android:icon="@mipmap/ic_launcher"

9. android:label="@string/app_name"

10. android:supportsRtl="true"

11. android:theme="@style/[Link]">

12. <activity android:name=".MainActivity">

13. <intent-filter>

14. <action android:name="[Link]" />

15. <category android:name="[Link]" />

16. </intent-filter>

17. </activity>

18. <activity android:name=".SmsReceiverActivity"

19. android:theme="@style/[Link]"/>

20. <receiver

21. android:name=".SmsReceiver"

22. android:enabled="true"
23. android:exported="true">

24. <intent-filter>

25. <action
android:name="[Link].SMS_RECEIVED" />

26. </intent-filter>

27. </receiver>

28. </application>

29. </manifest>

Selesai sudah bagian pertama dalam memahami broadcast receiver. Silakan


jalankan aplikasi Anda. Cobalah mengirim SMS ke peranti Android Anda dan
perhatikan hasilnya.

Jika dilihat dari hasil pesan yang masuk, aplikasi masih belum bisa membaca dan
menampilkan pesan tersebut ke layar Android. Mengapa demikian? Peranti
tersebut menggunakan OS Android 9.0. Bila Anda memiliki peranti Android
dengan versi Marshmallow ke atas, Anda perlu memberikan permission pada
aplikasi untuk mengirimkan SMS. Jadi kita perlu menambahkan kelas untuk
mengatur permission.

Buat object(Kotlin)/classs(Java) baru dan beri nama PermissionManager.


Selanjutnya kita tambahkan metode untuk pengecekan permission ke dalam
PermissionManager.

 Kotlin
 Java
1. object PermissionManager {

2. fun check(activity: Activity, permission: String, requestCode: Int) {

3. if ([Link](activity, permission) !=
PackageManager.PERMISSION_GRANTED) {

4. [Link](activity,
arrayOf(permission), requestCode)

5. }

6. }

7. }

Setelah kita buat metode untuk checkpermission, panggil metode check ke


dalam MainActivity seperti berikut:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.

3. companion object {

4. private const val SMS_REQUEST_CODE = 101

5. }

6.

7. ...

8.

9. override fun onClick(v: View) {

10. when {

11. [Link] == [Link].btn_permission -> [Link](this,


[Link].RECEIVE_SMS, SMS_REQUEST_CODE)

12. }

13. }

14.

15. override fun onRequestPermissionsResult(requestCode: Int,


permissions: Array<String>, grantResults: IntArray) {
16. [Link](requestCode,
permissions, grantResults)

17. if (requestCode == SMS_REQUEST_CODE) {

18. when {

19. grantResults[0] ==
PackageManager.PERMISSION_GRANTED -> [Link](this,
"Sms receiver permission diterima", Toast.LENGTH_SHORT).show()

20. else -> [Link](this, "Sms receiver permission


ditolak", Toast.LENGTH_SHORT).show()

21. }

22. }

23. }

24. }

[Link] menambahkan permission, jalankan aplikasi Anda. Cobalah kirim


SMS ke peranti Android Anda dan perhatikan hasilnya. Setiap SMS yang
masuk akan ditampilkan langsung dilayar. Keren kan!

[Link] memberikan permission dari SMS, Anda bisa menonaktifkan


dengan caranya, buka settings → App & notifications →
MyBroadcastReceiver → Permissions → SMS.
Bedah Kode

Bedah kode kita mulai dari awal, yaitu bagian ide. Kali ini kami mengajak
Anda untuk membuat sebuah aplikasi sederhana yang memiliki fungsi
untuk menampilkan setiap SMS yang masuk ke layar pengguna. Intinya
agar pengguna dapat melihat SMS yang masuk pada panel notifikasi
perangkat Android-nya.

SmsReceiver

Logika sederhananya adalah sebagai berikut:


Komponen broadcast receiver yaitu SmsReceiver akan bereaksi ketika
ada SMS yang masuk. Hal ini tertuang di dalam [Link]
seperti berikut:

1. <receiver

2. android:name=".SmsReceiver"

3. android:enabled="true"

4. android:exported="true">

5. <intent-filter>

6. <action android:name="[Link].SMS_RECEIVED" />

7. </intent-filter>
8. </receiver>

Pada bagian action


android:name="[Link].SMS_RECEIVED" berfungsi
sebagai flag intent-filter di mana SmsReceiver kita bisa merespon jika
ada SMS masuk dengan permission yang diberikan.

1. <uses-permission android:name="[Link].RECEIVE_SMS" />

2. <uses-permission android:name="[Link].READ_SMS" />

Di metode onReceive()receiver akan memproses metadata dari SMS yang


masuk.

Selanjutnya ReceiverActivity akan dijalankan dengan membawa data


melalui sebuah intent showSmsIntent.

 Kotlin
 Java

1. override fun onReceive(context: Context, intent: Intent) {

2. val bundle = [Link]

3. try {

4. if (bundle != null) {

5. val pdusObj = [Link]("pdus") as Array<Any>

6. for (aPdusObj in pdusObj) {

7. val currentMessage = getIncomingMessage(aPdusObj, bundle)

8. val senderNum = [Link]

9. val message = [Link]

10. Log.d(TAG, "senderNum: $senderNum; message: $message")

11. val showSmsIntent = Intent(context, SmsReceiverActivity::[Link])

12. [Link] = Intent.FLAG_ACTIVITY_NEW_TASK

13. [Link](SmsReceiverActivity.EXTRA_SMS_NO,
senderNum)

14. [Link](SmsReceiverActivity.EXTRA_SMS_MESSAGE,
message)

15. [Link](showSmsIntent)

16. }
17. }

18. } catch (e: Exception) {

19. Log.d(TAG, "Exception smsReceiver$e")

20. }

21. }

Kita memanfaatkan fasilitas yang terdapat pada kelas SmsManager dan


SmsMesssage untuk melakukan pemrosesan SMS. Untuk memperoleh
obyek dari kelas SmsMessage, yaitu obyek currentMessage, kita
menggunakan metode getIncomingMessage(). Metode ini akan
mengembalikan currentMessage berdasarkan OS yang dijalankan oleh
perangkat Android. Hal ini perlu dilakukan karena metode
[Link]((object); sudah deprecated di peranti
dengan OS Marshmallow atau versi setelahnya.

 Kotlin
 Java

1. private fun getIncomingMessage(aObject: Any, bundle: Bundle): SmsMessage {

2. val currentSMS: SmsMessage

3. if ([Link].SDK_INT >= 23) {

4. val format = [Link]("format")

5. currentSMS = [Link](aObject as ByteArray, format)

6. } else currentSMS = [Link](aObject as ByteArray)

7. return currentSMS

8. }

Flag pada [Link](Intent.FLAG_ACTIVITY_NEW_TASK);


akan menjalankan Activity pada task yang berbeda. Bila Activity
tersebut sudah ada di dalam stack, maka ia akan ditampilkan ke layar.
Anda dapat membaca penjelasan lebih lanjut mengenai konsep ini pada
tautan berikut:

 Task and Back Stack

Setelah obyek intent diterima, ReceiverActivity akan ditampilkan


dengan background transparan. Ini akan membuatnya seolah-olah
menjadi sebuah dialog. Dengan cara seperti ini, pesan dapat ditampilkan.
 Kotlin
 Java

1. val senderNo = [Link](EXTRA_SMS_NO)

2. val senderMessage = [Link](EXTRA_SMS_MESSAGE)

3.

4. binding?.tvFrom?.text = getString([Link], senderNo)

5. binding?.tvMessage?.text = senderMessage

Wow! Sekarang Anda sudah bisa membuat aplikasi yang bisa


menindaklanjuti sebuah event penting pada perangkat Android. Anda juga
dapat mengembangkan aplikasi tersebut lebih lanjut. Misalnya membuat
fitur untuk mendeteksi SMS spam semacam "Papa, Mama minta saham…"
Latihan BroadcastReceiver dari Custom Event

Selanjutnya, mari beranjak ke kasus kedua di mana aplikasi kita bisa


mem-broadcast sebuah event. Diumpamakan:

 Aplikasi melakukan pengunduhan berkas menggunakan background


service dan asynchronous.
 Ketika proses mengunduh selesai, service akan melakukan broadcast
sebuah event dan akan ada Activity yang merespon.

Jadi, ayo kita ngoding lagi.

1. Buka activity_main.xml dan silakan kondisikan kode di dalamnya


menjadi seperti berikut.

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:padding="16dp"

8. android:orientation="vertical"

9. tools:context="[Link]">

10.

11. <TextView

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/this_sms_receiver_app"

15. android:layout_marginBottom="16dp" />

16.

17. <Button

18. android:id="@+id/btn_permission"

19. android:layout_width="match_parent"
20. android:layout_height="wrap_content"

21. android:text="@string/check_sms_permission" />

22.

23. <Button

24. android:id="@+id/btn_download"

25. android:layout_width="match_parent"

26. android:layout_height="wrap_content"

27. android:text="@string/download_file" />

28. </LinearLayout>

Setelah selesai, lengkapi kode pada MainActivity untuk mengenalkan obyek


yang udah dibuat di activity_main.xml menjadi:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.

3. companion object {

4. const val ACTION_DOWNLOAD_STATUS = "download_status"

5. private const val SMS_REQUEST_CODE = 101

6. }

7.

8. private lateinit var downloadReceiver: BroadcastReceiver

9. private var binding: ActivityMainBinding? = null

10.

11. override fun onCreate(savedInstanceState: Bundle?) {

12. [Link](savedInstanceState)

13.

14. binding = [Link](layoutInflater)

15. setContentView(binding?.root)
16.

17. binding?.btnPermission?.setOnClickListener(this)

18. binding?.btnDownload?.setOnClickListener(this)

19. }

20.

21. override fun onClick(v: View) {

22. when ([Link]) {

23. [Link].btn_permission -> [Link](this,


[Link].RECEIVE_SMS, SMS_REQUEST_CODE)

24. [Link].btn_download -> {

25.

26. }

27. }

28. }

29.

30. ...

31. }

Buat sebuah class baru dengan nama DownloadService dengan cara klik kanan
pada package utama project → new → Java/Kotlin Class. Setelah tercipta
sesuaikan kode di dalamnya menjadi seperti berikut:

 Kotlin
 Java

1. class DownloadService : JobIntentService() {

2.

3. companion object {

4. val TAG: String = DownloadService::[Link]

5. }

6.
7. override fun onStartCommand(intent: Intent?, flags: Int, startId: Int):
Int {

8. if (intent != null) {

9. enqueueWork(this, this::[Link], 101, intent)

10. }

11. return [Link](intent, flags, startId)

12. }

13.

14. override fun onHandleWork(intent: Intent) {

15. Log.d(TAG, "Download Service dijalankan")

16. try {

17. [Link](5000)

18. } catch (e: InterruptedException) {

19. [Link]()

20. }

21.

22. val notifyFinishIntent =


Intent(MainActivity.ACTION_DOWNLOAD_STATUS)

23. sendBroadcast(notifyFinishIntent)

24. }

25. }

Kemudian tambahkan kode berikut pada AndroidManifest untuk mendaftarkan


service yang telah kita buat:

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4. ...

5. <application
6. ...

7. <receiver

8. android:name=".SmsReceiver"

9. android:enabled="true"

10. android:exported="true">

11. <intent-filter>

12. <action android:name="[Link].SMS_RECEIVED" />

13. </intent-filter>

14. </receiver>

15.

16. <service

17. android:name=".DownloadService"

18. android:permission="[Link].BIND_JOB_SERVICE" />

19.

20. </application>

21.

22. </manifest>

Terakhir, Pada MainActivity lengkapi kode yang ada dengan menambahkan


sebuah Intent untuk menjalankan DownloadService di metode onClick()sehingga
kode di dalamnya menjadi seperti ini:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.

3. companion object {

4. const val ACTION_DOWNLOAD_STATUS = "download_status"

5. private const val SMS_REQUEST_CODE = 101

6. }
7.

8. private lateinit var downloadReceiver: BroadcastReceiver

9. private var binding: ActivityMainBinding? = null

10.

11. override fun onCreate(savedInstanceState: Bundle?) {

12. [Link](savedInstanceState)

13.

14. ...

15.

16. downloadReceiver = object : BroadcastReceiver() {

17. override fun onReceive(context: Context, intent: Intent) {

18. Log.d([Link], "Download Selesai")

19. [Link](context, "Download Selesai",


Toast.LENGTH_SHORT).show()

20. }

21. }

22. val downloadIntentFilter =


IntentFilter(ACTION_DOWNLOAD_STATUS)

23. registerReceiver(downloadReceiver, downloadIntentFilter)

24. }

25.

26. override fun onClick(v: View) {

27. when ([Link]) {

28. [Link].btn_permission -> [Link](this,


[Link].RECEIVE_SMS, SMS_REQUEST_CODE)

29. [Link].btn_download -> {

30. val downloadServiceIntent = Intent(this,


DownloadService::[Link])

31. startService(downloadServiceIntent)
32. }

33. }

34. }

35.

36. override fun onDestroy() {

37. [Link]()

38. unregisterReceiver(downloadReceiver)

39. binding = null

40. }

41.

42. ...

43. }

Dan kode MainActivity menjadi seperti ini:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.

3. companion object {

4. const val ACTION_DOWNLOAD_STATUS = "download_status"

5. private const val SMS_REQUEST_CODE = 101

6. }

7.

8. private lateinit var downloadReceiver: BroadcastReceiver

9. private var binding: ActivityMainBinding? = null

10.

11. override fun onCreate(savedInstanceState: Bundle?) {

12. [Link](savedInstanceState)
13.

14. binding = [Link](layoutInflater)

15. setContentView(binding?.root)

16.

17. binding?.btnPermission?.setOnClickListener(this)

18. binding?.btnDownload?.setOnClickListener(this)

19.

20. downloadReceiver = object : BroadcastReceiver() {

21. override fun onReceive(context: Context, intent: Intent) {

22. Log.d([Link], "Download Selesai")

23. [Link](context, "Download Selesai",


Toast.LENGTH_SHORT).show()

24. }

25. }

26. val downloadIntentFilter = IntentFilter(ACTION_DOWNLOAD_STATUS)

27. registerReceiver(downloadReceiver, downloadIntentFilter)

28. }

29.

30. override fun onClick(v: View) {

31. when ([Link]) {

32. [Link].btn_permission -> [Link](this,


[Link].RECEIVE_SMS, SMS_REQUEST_CODE)

33. [Link].btn_download -> {

34. val downloadServiceIntent = Intent(this,


DownloadService::[Link])

35. startService(downloadServiceIntent)

36. }

37. }

38. }
39.

40. override fun onDestroy() {

41. [Link]()

42. unregisterReceiver(downloadReceiver)

43. binding = null

44. }

45.

46. override fun onRequestPermissionsResult(requestCode: Int, permissions:


Array<String>, grantResults: IntArray) {

47. [Link](requestCode,
permissions, grantResults)

48. if (requestCode == SMS_REQUEST_CODE) {

49. when (PackageManager.PERMISSION_GRANTED) {

50. grantResults[0] -> [Link](this, "Sms receiver


permission diterima", Toast.LENGTH_SHORT).show()

51. else -> [Link](this, "Sms receiver permission


ditolak", Toast.LENGTH_SHORT).show()

52. }

53. }

54. }

55. }
6. Selesai! Sekarang jalankan aplikasi Anda dan klik pada tombol Download
File dan lihat pada log Android Monitor di Android Studio Anda.
7. Skenario di atas sudah terimplementasi dengan baik. Sekarang ayo kita
bahas kodenya.

Bedah Kode

Mari kita bedah skenario yang telah dijabarkan di awal modul ini.

Aplikasi melakukan pengunduhan berkas menggunakan background


service secara asynchronous.

onHandleWork

Di sini kita akan menjalankan Intent Service yang akan melakukan proses
mengunduh file secara Asynchronous di background. Kelas
DownloadService mengambil peran di sini. Pada kenyataanya,
DownloadService ini hanya melakukan proses sleep() selama 5 detik
dan kemudian mem-broadcast sebuah IntentFilter dengan Action yang
telah ditentukan, ACTION_DOWNLOAD_STATUS. Kodenya sebagai berikut:

 Kotlin
 Java

1. override fun onHandleWork(intent: Intent) {


2. Log.d(TAG, "Download Service dijalankan")

3. try {

4. [Link](5000)

5. } catch (e: InterruptedException) {

6. [Link]()

7. }

8.

9. val notifyFinishIntent = Intent(MainActivity.ACTION_DOWNLOAD_STATUS)

10. sendBroadcast(notifyFinishIntent)

11. }

Ketika proses pengunduhan berkas tersebut selesai, service akan mem-


broadcast sebuah event dan akan ada Activity yang merespon.

BroadcastReceiver

Kembali ke MainActivity, perhatikan ada dua variable tambahan di atas


metode onCreate(), yaitu:

 Kotlin
 Java

1. companion object {

2. const val ACTION_DOWNLOAD_STATUS = "download_status"

3. ...

4. }

5.

6. private lateinit var downloadReceiver: BroadcastReceiver

Pada metode onCreate() kita membuat sebuah obyek dari


DownloadReceiver. Kemudian MainActivity diregistrasikan untuk
mendengar event/action dengan tag:
ACTION_DOWNLOAD_STATUS. Ketika event/action tersebut ditangkap oleh
MainActivity, maka obyek downloadReceiver akan dijalankan.

 Kotlin
 Java
1. downloadReceiver = object : BroadcastReceiver() {

2. override fun onReceive(context: Context, intent: Intent) {

3. Log.d([Link], "Download Selesai")

4. [Link](context, "Download Selesai",


Toast.LENGTH_SHORT).show()

5. }

6. }

7. val downloadIntentFilter = IntentFilter(ACTION_DOWNLOAD_STATUS)

8. registerReceiver(downloadReceiver, downloadIntentFilter)

Ketika baris ini dijalankan pada DownloadService.

 Kotlin
 Java

1. val notifyFinishIntent = Intent(MainActivity.ACTION_DOWNLOAD_STATUS)

2. sendBroadcast(notifyFinishIntent)

Maka seketika itu pula metode onReceive() di bawah ini akan merespon
untuk melakukan proses di dalamnya:

 Kotlin
 Java

1. override fun onReceive(context: Context, intent: Intent) {

2. Log.d([Link], "Download Selesai")

3. [Link](context, "Download Selesai",


Toast.LENGTH_SHORT).show()

4. }

Pada konteks di atas, kita hanya menampilkan sebuah Toast dan Log.
Terakhir, ketika Activity dimatikan atau dihancurkan oleh sistem Android
maka kita tidak boleh lupa untuk melakukan proses pencopotan
(unregister) obyek receiver yang telah diregistrasikan sebelumnya,
seperti yang dituangkan oleh baris berikut:

 Kotlin
 Java

1. override fun onDestroy() {


2. [Link]()

3. unregisterReceiver(downloadReceiver)

4.

5. ...

6. }

Jika ada yang menyadari bahwa ini adalah proses yang sama dengan cara
sebelumnya, namun lebih manual, maka itu benar sekali! Kami
menunjukan bahwa kita bisa menggunakan metode manual dengan
pemrograman Java pada proses manipulasi di Activity/Fragment.

Berikut adalah 3 poin penting yang menjadi kesimpulan dari proses


manual di atas:

 Registrasikan sebuah obyek BroadcastReceiver pada komponen aplikasi


seperti Activity dan Fragment dan tentukan action/event apa yang ingin
didengar/direspon.
 Lakukan proses terkait pada metode onReceive() ketika event atau
action yang dipantau di-broadcast oleh komponen lain.
 Jangan lupa untuk mencopot pemasangan obyek receiver sebelum
komponen tersebut dihancurkan atau dimatikan.

Selamat, satu lagi komponen penting dari pengembangan aplikasi Android


telah Anda pelajari. Untuk lebih mendalami pemahaman tentang
komponen ini kami menekankan Anda untuk membaca lebih lanjut
dokumentasi teknis dari link yang kami berikan.

Untuk source code project ini bisa Anda unduh di:

 Source Code Latihan Broadcast Receiver


Teori Notification

Seperti yang telah kita ketahui bersama, banyak aplikasi yang dapat
mengabari kita ketika ada informasi baru yang diperolehnya. Informasi
tersebut bernama notification. Misalnya saat ada pesan masuk di
WhatsApp, pemberitahuan di Facebook, dan lain-lain.

Ada beragam jenis notifikasi. Ada yang berupa pop-up, dan ada juga yang
muncul di status bar perangkat kita.
Pada modul ini, Anda akan mempelajari bagaimana menampilkan
notifikasi sederhana. Anda juga akan mempelajari bagaimana mengirim
notifikasi tersebut menggunakan PendingIntent.

Design Considerations

Notifikasi sebagai bagian yang penting dari antarmuka Android, memiliki


design guidelines sendiri. Sejak material design diperkenalkan, Anda perlu
mempelajari bagaimana material design mempengaruhi rancangan
sebuah notifikasi.

Selengkapnya, temukan di tautan berikut:

[Link]

Creating Notifications

Anda bisa menentukan informasi user interface dan action dari notifikasi
dengan obyek [Link]. Untuk membuat notifikasi itu
sendiri, Anda bisa menggunakan [Link]() yang
akan mengembalikan sebuah obyek notifikasi. Untuk memberikan
notifikasi, Anda bisa menggunakan [Link]().

Required Notifications Contents

Berikut adalah parameter yang harus ada dalam membuat notifikasi:

1. Small Icon, dapat diatur dengan setSmallIcon().


2. Title, dapat diatur dengan setContentTitle().
3. Detail Text, dapat diatur dengan setContentText().
Notification Actions

Meskipun ini tidak wajib, Anda sebaiknya menambahkan sebuah action


untuk notifikasi yang Anda buat. Action ini digunakan untuk melihat
informasi yang lebih detail dari notifikasi tersebut.

Pada praktiknya, sebuah notifikasi bisa memiliki lebih dari satu action.
Misalnya dalam notifikasi terdapat sebuah tombol, seperti pada notifikasi
SMS, yang dengannya Anda dapat langsung membalas SMS tersebut.

Dalam notifikasi tersebut, sebuah action didefinisikan dengan


PendingIntent dan dimasukkan ke dalam [Link].
Sebagai contoh jika Anda ingin membuka sebuah activity ketika notifikasi
tersebut disentuh, Anda bisa menggunakan PendingIntent dengan
memanggil setContentIntent().
Contoh penggunaanya yaitu:

 Kotlin
 Java

1. var notificationIntent = Intent(this, MainActivity::[Link])

2. var notificationPendingIntent = [Link](this,


NOTIFICATION_ID, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT)

3. var mBuilder = [Link](this)

4. .setSmallIcon([Link].notification_icon)

5. .setContentTitle("Hey, check this app now!")

6. .setContentText("Something important you need to know.")

7. .setContentIntent(notificationPendingIntent)

Notification Priority

Anda juga bisa mengatur prioritas notifikasi yang Anda buat. Anda bisa
mengatur bagaimana notifikasi akan ditampilkan.
Untuk mengatur prioritas dari notifikasi, Anda bisa menggunakan
[Link]() kemudian di masukkan ke dalam
NotificationCompat. Ada 5 (lima) tingkatan prioritas, mulai dari
PRIORITY_MIN (-2) sampai PRIORITY_MAX (2). Secara default prioritas
diatur dengan PRIORITY_DEFAULT (0).

Contoh penggunaanya yaitu:

 Kotlin
 Java

1. [Link](Notification.PRIORITY_HIGH)

Peeking

Notification dengan priority MAX atau HIGH bisa menggunakan fitur


peeking. Di mana notification akan muncul sebentar di layar kita seperti
mengintip (peeking), tidak terpengaruh dengan aplikasi apapun yang
sedang kita buka. Sebagai catatan, pada Android versi 6.0 ke atas fitur ini
bisa dinon-aktifkan oleh user melalui menu Setting.

Contoh penggunannya yaitu:

 Kotlin
 Java

1. val mBuilder =

2. [Link](this)

3. .setSmallIcon([Link].notification_icon)

4. .setContentTitle("My notification")

5. .setContentText("Hello World!")

6. .setPriority(NotificationCompat.PRIORITY_HIGH)

7. .setDefaults(NotificationCompat.DEFAULT_ALL)

Expanded view layouts

Saat memunculkan notifikasi, selain menggunakan normal view, kita juga


bisa menggunakan expanded view. Expanded View ini mulai dikenalkan
pada Android 4.1. Ada beberapa macam expanded view yang bisa kita
gunakan, yaitu:

1. BigTextStyle, untuk notifikasi dengan isian text yang banyak.


2. InboxStyle, untuk notifikasi yang berisi list berupa text, biasanya
diguanakan oleh aplikasi chatting.

3. MediaStyle, untuk notifikasi yang khusus menangani Media Player, di


mana sudah tersedia tombol play, pause, maupun stop.
4. BigPictureStyle, untuk notifikasi yang menampilkan gambar di dalamnya,
kita sering melihatnya dipakai oleh aplikasi seperti Instagram.

Berikut contoh penggunaan dari BigPictureStyle:

 Kotlin
 Java

1. val androidImage = [Link](resources,


[Link].mascot_1)

2.

3. val notif = [Link](this)

4. .setContentTitle("New photo from " + [Link]())

5. .setContentText(subject)
6. .setSmallIcon([Link].new_post)

7. .setLargeIcon(androidImage)

8. .setStyle([Link]()

9. .bigPicture(androidImage)

10. .setBigContentTitle("Large Notification Title"))

11. .build()

Notification pada Android O ke Atas

Mulai Android Oreo, Anda harus membuat channel agar notifikasi bisa
berjalan. Channel di sini berfungsi untuk mengelompokkan notifikasi
dalam beberapa grup seperti kanal (channel). Kelebihannya yaitu kita
mengatur notifikasi yang dalam satu channel tersebut dalam sekali
waktu, seperti mengatur prioritas, suara, maupun getaran. Hal ini tentu
akan memudahkan pengguna untuk mem-filter notifikasi yang masuk,
sehingga meningkatkan kenyamanan user dalam menggunakan aplikasi.

Langkah yang harus dilakukan yaitu dengan menambahkan kode berikut:

 Kotlin
 Java

1. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

2. val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME,


NotificationManager.IMPORTANCE_DEFAULT)

3. [Link](CHANNEL_ID)

4. [Link](channel)

5. }

Pembahasan lebih lanjut mengenai notification dapat Anda baca pada


tautan berikut:

 Notifications : Documentation
Latihan Notification dengan NotificationBuilder & NotificatonManager
Tujuan

Codelab kali ini akan membahas secara lengkap tentang notifications


beserta cara mengatur notifikasi. Beberapa poin yang bisa didapatkan
pada codelab kali ini adalah:

1. Menggunakan notifikasi secara sederhana.


2. Memanfaatkan pendingintent ke dalam notifikasi.

Codelab ini akan menjadi sebuah aplikasi seperti ini:

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama SimpleNotif.


2. Menambahkan kode untuk memunculkan notifikasi.
3. Mengatur tampilan pada berkas layout XML.
4. Menjalankan aplikasi.
Codelab Menampilkan Notifikasi

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:

Nama Project SimpleNotif

Target & Minimum Target Phone and Tablet, Api


SDK level 21

Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Artifacts True

Language Kotlin/Java

2. Setelah membuat sebuah project, tambahkan beberapa string pada


[Link], sehingga menjadi seperti berikut:

1. <resources>

2. <string name="app_name">SimpleNotif</string>

3. <string name="content_title">Content title</string>

4. <string name="content_text">Content text</string>

5. <string name="subtext">Sub text</string>

6. <string name="send_notif">Send Notif</string>

7. </resources>

Selanjutnya kita butuh mengatur tampilan dari notifikasi. Caranya,


gunakan [Link]. OK, silakan tambahkan
NotificationBuilder pada MainActivity:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2. companion object {

3. private const val NOTIFICATION_ID = 1

4. private const val CHANNEL_ID = "channel_01"

5. private const val CHANNEL_NAME = "dicoding channel"


6. }

7.

8. override fun onCreate(savedInstanceState: Bundle?) {

9. [Link](savedInstanceState)

10. setContentView([Link].activity_main)

11. }

12.

13. //aksi untuk onClick pada button

14. fun sendNotification(view: View) {

15.

16. val mNotificationManager =


getSystemService(Context.NOTIFICATION_SERVICE) as
NotificationManager

17. val mBuilder = [Link](this, CHANNEL_ID)

18. .setSmallIcon([Link].ic_notifications_white_48px)

19. .setLargeIcon([Link](resources,
[Link].ic_notifications_white_48px))

20. .setContentTitle([Link]([Link].content_title))

21. .setContentText([Link]([Link].content_text))

22. .setSubText([Link]([Link]))

23. .setAutoCancel(true)

24.

25. val notification = [Link]()

26.

27. [Link](NOTIFICATION_ID, notification)

28. }

29. }

Anda dapat mengunduh ikon pada halaman ini:

[Link]
Namun jika anda jalankan di OS Oreo ke atas, notifkasi tidak akan muncul.
Hal ini terjadi karena kita perlu menambahkan kode tentang Notification
Channel, sehingga kelas MainActivity menjadi seperti berikut:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2. companion object {

3. private const val NOTIFICATION_ID = 1

4. private const val CHANNEL_ID = "channel_01"

5. private const val CHANNEL_NAME = "dicoding channel"

6. }

7.

8. override fun onCreate(savedInstanceState: Bundle?) {

9. [Link](savedInstanceState)

10. setContentView([Link].activity_main)

11. }

12.

13. //aksi untuk onClick pada button

14. fun sendNotification(view: View) {

15.

16. val mNotificationManager =


getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

17. val mBuilder = [Link](this, CHANNEL_ID)

18. .setSmallIcon([Link].ic_notifications_white_48px)

19. .setLargeIcon([Link](resources,
[Link].ic_notifications_white_48px))

20. .setContentTitle([Link]([Link].content_title))

21. .setContentText([Link]([Link].content_text))

22. .setSubText([Link]([Link]))

23. .setAutoCancel(true)
24.

25. /*

26. Untuk android Oreo ke atas perlu menambahkan notification


channel

27. */

28.

29. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

30.

31. /* Create or update. */

32. val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME,


NotificationManager.IMPORTANCE_DEFAULT)

33. [Link] = CHANNEL_NAME

34.

35. [Link](CHANNEL_ID)

36. [Link](channel)

37. }

38.

39. val notification = [Link]()

40.

41. [Link](NOTIFICATION_ID, notification)

42. }

43. }

Tahap selanjutnya adalah membuat layout yang berisi button dan


memanggil fungsi sendNotification() langsung dari layout dengan
menggunakan onClick. Silakan tambahkan kode berikut pada
activity_main.xml.

1. <RelativeLayout

2. xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"
5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. tools:context=".MainActivity">

8.

9. <Button

10. android:id="@+id/button"

11. android:layout_width="wrap_content"

12. android:layout_height="wrap_content"

13. android:layout_centerInParent="true"

14. android:layout_gravity="center"

15. android:onClick="sendNotification"

16. android:text="@string/send_notif"

17. android:textColor="@android:color/white" />

18.

19. </RelativeLayout>

5. Sampai di sini Anda sudah bisa melihat hasilnya. Silakan jalankan


aplikasinya.
Bedah Kode

1. <Button

2. android:id="@+id/button"

3. android:layout_width="wrap_content"

4. android:layout_height="wrap_content"

5. android:layout_centerInParent="true"

6. android:layout_gravity="center"

7. android:onClick="sendNotification"

8. android:text="@string/send_notif"

9. android:textColor="@android:color/white" />

Digunakan untuk memanggil fungsi langsung dari layout, syaratnya yaitu


fungsi tersebut memiliki parameter View.

 Kotlin
 Java

1. fun sendNotification(view: View) {


Kelebihan dari cara ini yakni kita tidak perlu menginisialisasi Button
tersebut di onCreate, sehingga pemanggilan kode bisa lebih cepat.

Notification Builder

 Kotlin
 Java

1. val mBuilder = [Link](this, CHANNEL_ID)

2. .setSmallIcon([Link].ic_notifications_white_48px)

3. .setLargeIcon([Link](resources,
[Link].ic_notifications_white_48px))

4. .setContentTitle([Link]([Link].content_title))

5. .setContentText([Link]([Link].content_text))

6. .setSubText([Link]([Link]))

7. .setAutoCancel(true)

Kode di atas digunakan untuk menambahkan elemen apa saja yang akan
ditampikan pada notifikasi seperti icon, action, title, content, dan lain-
lain.

Dalam notification builder terdapat beberapa komponen sebagai berikut:

 Small Icon : Ikon ini yang akan muncul pada status bar (wajib ada).
 Large Icon : Ikon ini yang akan muncul di sebelah kiri dari text
notifikasi.
 Content Intent : Intent ini sebagai action jika notifikasi ditekan.
 Content Title : Judul dari notifikasi (wajib ada).
 Content Text : Text yang akan muncul di bawah judul notifikasi
(wajib ada).
 Subtext : Text ini yang akan muncul di bawah content text.
 Auto Cancel : Digunakan untuk menghapus notifikasi setelah
ditekan.

NotificationManager

 Kotlin
 Java

1. val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE)


as NotificationManager

2. [Link](NOTIFICATION_ID, notification)
Kode di atas digunakan untuk mengirim notifikasi sesuai dengan id yang
kita berikan. Fungsi id di sini nanti juga bisa untuk membatalkan notifikasi
yang sudah muncul.

Notif Untuk OS Oreo ke Atas

 Kotlin
 Java

1. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

2. val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME,


NotificationManager.IMPORTANCE_DEFAULT)

3. [Link] = CHANNEL_NAME

4. [Link](CHANNEL_ID)

5. [Link](channel)

6. }

Untuk memunculkan notifkasi pada OS Oreo ke atas, Anda harus


membuat channel agar notifikasi bisa berjalan.

Pada modul berikutnya Anda akan coba menambahkan aksi ketika sebuah
notifikasi diklik. Tetap semangat ya!
Latihan Notification dengan PendingIntent

Sekarang kita akan mengatur aplikasi tersebut dengan menggunakan


pending intent.

Pending intent adalah sebuah token yang diberikan untuk aplikasi lainnya
(termasuk juga aplikasi itu sendiri). Contohnya seperti
NotificationManager, AlarmManager, HomeScreenAppWidgetManager, dan
lain-lain.

Di sini kita kan menggunakan pending intent untuk mengirimkan


notifikasi. Kita juga akan menggunakan HandlerpostDelayed agar kita bisa
mengatur delay untuk mengirimkan notifikasi tersebut. Di sini kita atur
waktu jedanya (delay) 5000 ms atau 5 detik.

Selain itu, kita juga bisa mengatur action dari intent tersebut. Pada contoh
ini kita mengatur action untuk membuka sebuah web url dengan
menggunakan Intent.ACTION_VIEW dan menambahkan url pada [Link].

1. Kemudian tambahkan PendingIntent untuk diterapkan di mBuilder


pada MainActivity seperti di bawah ini:

o Kotlin
o Java

1. fun sendNotification(view: View) {

2. val intent = Intent(Intent.ACTION_VIEW,


[Link]("[Link]

3. val pendingIntent = [Link](this, 0, intent, 0)

4.

5. val mNotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

6. val mBuilder = [Link](this, CHANNEL_ID)

7. .setContentIntent(pendingIntent)

8. .setSmallIcon([Link].ic_notifications_white_48px)

9. .setLargeIcon([Link](resources,
[Link].ic_notifications_white_48px))

10. .setContentTitle([Link]([Link].content_title))

11. .setContentText([Link]([Link].content_text))

12. .setSubText([Link]([Link]))
13. .setAutoCancel(true)

14.

15. ...

16. }

Maka kelas MainActivity menjadi seperti ini :

 Kotlin
 Java

1.

1. class MainActivity : AppCompatActivity() {

2. companion object {

3. private const val NOTIFICATION_ID = 1

4. private const val CHANNEL_ID = "channel_01"

5. private const val CHANNEL_NAME = "dicoding channel"

6. }

7.

8. override fun onCreate(savedInstanceState: Bundle?) {

9. [Link](savedInstanceState)

10. setContentView([Link].activity_main)

11. }

12.

13. //aksi untuk onClick pada button

14. fun sendNotification(view: View) {

15. val intent = Intent(Intent.ACTION_VIEW,


[Link]("[Link]

16. val pendingIntent = [Link](this, 0, intent, 0)

17.

18. val mNotificationManager =


getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

19. val mBuilder = [Link](this, CHANNEL_ID)


20. .setContentIntent(pendingIntent)

21. .setSmallIcon([Link].ic_notifications_white_48px)

22. .setLargeIcon([Link](resources,
[Link].ic_notifications_white_48px))

23. .setContentTitle([Link]([Link].content_title))

24. .setContentText([Link]([Link].content_text))

25. .setSubText([Link]([Link]))

26. .setAutoCancel(true)

27.

28. /*

29. Untuk android Oreo ke atas perlu menambahkan notification channel

30. */

31. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

32.

33. /* Create or update. */

34. val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME,


NotificationManager.IMPORTANCE_DEFAULT)

35.

36. [Link](CHANNEL_ID)

37.

38. [Link](channel)

39. }

40.

41. val notification = [Link]()

42.

43. [Link](NOTIFICATION_ID, notification)

44. }

45. }
2. Lalu jalankan aplikasinya. Jika Anda tekan notifikasi tersebut, Anda
akan masuk ke website Dicoding.

Bedah Kode
PendingIntent

 Kotlin
 Java

1. val intent = Intent(Intent.ACTION_VIEW, [Link]("[Link]

2. val pendingIntent = [Link](this, 0, intent, 0)

Digunakan untuk memberikan action jika notifikasi disentuh. Selain ke


halaman Web, anda juga bisa mengganti intent tersebut ke Activity
dengan menggunakan Intent untuk memanggil activity seperti biasanya.

Selamat Anda sudah belajar Notification dengan Pending Intent!

Anda dapat mengunduh source code pada tautan ini:

 Source Code Latihan Notification


Teori Task dan Back Stack

Sebelumnya kita telah belajar bagaimana activity dan intent bekerja


dalam membentuk sebuah hierarki dalam sebuah aplikasi. Pengguna
dapat berpindah dari satu activity ke activity lain melalui fasilitas intent.
Tidak hanya itu, activity dapat berinteraksi dengan activity lain pada
aplikasi yang berbeda.

Kita juga belajar bagaimana sebuah activity memiliki sebuah daur hidup
dan terkumpul dalam sebuah stack dengan konsep last in, first out
(LIFO). Activity yang terakhir masuk akan berada pada posisi paling atas
dalam tumpukan stack. Ketika pengguna menekan tombol back pada
peranti, maka activity tersebut yang akan pertama kali dikeluarkan dari
stack. Activity yang dikeluarkan tadi akan kembali muncul di layar
pengguna. Ketika pengguna menekan tombol back, semua task terkait
aplikasi tersebut akan dihapus dari memori.

Pola umum dalam sebuah aplikasi Android bersifat list to detail seperti ini:

Di mana interaksi pertama pengguna dengan aplikasi adalah dengan


menekan ikon aplikasi di layar home screen atau application gallery.
Kemudian sistem Android akan memeriksa apakah Aplikasi tersebut
memiliki task atau tidak.

Bila aplikasi tidak memiliki task, maka aplikasi akan membuat sebuah
task baru. Aplikasi tersebut akan mencari Activity yang memiliki
IntentFilter dengan action sebagai launcher yang memiliki kategori main
di [Link].

Contohnya seperti baris berikut:

1. <activity android:name=".MainActivity">

2. <intent-filter>

3. <action android:name="[Link]" />


4. <category android:name="[Link]" />

5. </intent-filter>

6. </activity>

MainActivity ditampilkan ke layar pengguna. Activity ini masuk ke dalam


sebuah stack yang dinamakan back stack. Activity ini akan berada pada
posisi foreground/running. Ia menjadi root activity. MainActivity tersebut
berbentuk list email seperti pada gambar di atas.

Ketika pengguna menekan salah satu item pada list, MainActivity akan
diberhentikan dan semua state-nya akan disimpan di obyek bundle secara
otomatis. Kemudian metode callback onStop() pada activity tersebut akan
dijalankan.

Pada tahap berikutnya, DetailEmailActivity akan muncul ke layar. Ia


berada pada posisi foreground dan berada pada posisi teratas di dalam
back stack.

Ketika pengguna menekan physical back button pada perangkat Android


atau menekan tombol back pada toolbar DetailEmailActivity, maka
DetailEmailActivity akan dikeluarkan dari back stack. Metode onDestroy()
akan dijalankan dan DetailEmailActivity akan dihancurkan dari memori.
Berikutnya, MainActivity akan muncul kembali dilayar.

Penjelasan di atas dapat digambarkan pada ilustrasi di bawah ini.

Proses di atas dinamakan back stack. Secara arsitektur, setiap aplikasi di


Android memiliki back stack dan task-nya masing-masing. Back stack dan
task tadi terbungkus dalam sebuah virtual sandbox sehingga integritas
masing-masing aplikasi lebih terjaga.
Task yang dimaksud di sini adalah kumpulan dari beberapa activity yang
melakukan sebuah pekerjaan. Jika digambarkan seperti ini:

Task A di atas akan melakukan sebuah pekerjaan yaitu mengirim email


dari salah satu opsi yang diklik pada activity detail.

Sebuah aplikasi Android bisa memiliki beberapa task. Namun hanya ada
satu task yang dapat berjalan di foreground atau tampil di layar. Sisanya
akan berada di background. Model ini yang memungkinkan Android
menerapkan mutlitasking.

Anda dapat memahami lebih dalam topik task dan back stack pada tautan
berikut ini:

 Task and Back Stack

Launch Mode dan Flag

Secara umum, fungsi default dari back stack ini sudah bagus dan
konsisten. Namun di beberapa kasus Anda perlu mengubahnya. Sebagai
contoh Anda memiliki SearchView untuk melakukan pencarian. Ketika
Anda menekan Enter, ia akan memanggil Activity baru untuk
menampilkan hasil pencarian. Bayangkan jika Anda melakukan pencarian
berkali-kali, maka akan terdapat banyak Activity yang sama terbuat. Dan
untuk kembali ke halaman awal, Anda harus menekan tombol back
berulang-ulang kali. Untuk mengatasinya, Anda bisa memanfaatkan
launchMode dan flag. Dalam kasus ini yaitu dengan dengan
menambahkan launchMode=”singleTop” di Android Manifest atau
dengan menambahkan flag Intent.FLAG_ACTIVITY_SINGLE_TOP pada
Intent.

Berikut adalah macam-macam launch mode dan flag lainnya:

Misal
sudah
Misal sudah
ada A dan
ada A dan B
B apa
apa yang
Mode Proses yang terjadi yang
terjadi jika
terjadi
memanggil
jika
B?
memangg
il A?
Setiap kali Intent,
A B
Instance baru terbuat.
Standard (default) B B
Bisa lebih dari satu di
A A
dalam sebuah task.
Gunakan lagi instance
jika sudah ada di
paling atas task dan
singleTop | A
memanggil B
FLAG_ACTIVITY_SINGLE_ B
onNewIntent(). Jika A
TOP A
tidak di paling atas,
instance akan dibuat
lagi.
Gunakan lagi instance
jika sudah ada di
singleTask |
dalam task yang sama B
FLAG_ACTIVITY_NEW_TA A
di mana pun posisinya, A
SK
kemudian memanggil
onNewIntent().
Hanya ada satu
singleInstance instance di dalam satu A B
task.
Catatan:
Susunan di atas menunjukkan susunan back stack dari atas ke bawah, di mana
yang paling atas adalah yang terlihat di layar.

Untuk memahami lebih lanjut contohnya di dalam aplikasi, Anda dapat


melihat tautan berikut:

 Android Tasks and Back Stack


TaskStackBuilder

Jika Anda membuat notifikasi yang ketika diklik menuju suatu activity
yang cukup dalam, DetailActivity misalnya. Ada satu yang harus Anda
perhatikan di sini. Yaitu ketika Anda menekan tombol back, secara
default aplikasi akan langsung keluar. Padahal lumrahnya ia menuju ke
halaman MainActivity (awal) terlebih dahulu.

Mari kita lihat contoh notifikasi chat dari aplikasi WhatsApp. Ketika
notifikasi tersebut diklik, Anda akan menuju halaman detail chat. Ketika
anda tekan tombol back, apa yang terjadi? Aplikasi menuju ke halaman
list chat. Sebagaimana ketika Anda membuka aplikasi dari awal menuju
ke detail chat. Seperti itulah seharusnya navigasi dari suatu aplikasi
mengalir.

Lalu bagaimana cara untuk membuat alur navigasi seperti itu? Di sinilah
TaskStackBuilder datang sebagai solusi. Dengan menggunakan
TaskStackBuilder Anda dapat membangun back stack sendiri dan
dikirimkan melalui Intent.

Namun syarat yang perlu dilakukan yaitu Anda perlu menambahkan


hierarki dari Activity Anda pada [Link]. Caranya yaitu
dengan menambahkan kode android:parentActivityName pada setiap
Activity yang dituju dari notifikasi. Sebagai contohnya seperti berikut:

1. <activity

2. android:name=".MainActivity"

3. android:label="@string/app_name" >

4. <intent-filter>

5. <action android:name="[Link]" />

6. <category android:name="[Link]" />

7. </intent-filter>

8. </activity>

9. <activity

10. android:name=".DetailActivity"

11. android:parentActivityName=".MainActivity" />

12. ...

13. </activity>
Selanjutnya ini adalah cara default untuk membuat PendingIntent di
dalam Notification:

 Kotlin
 Java

1. val intent = Intent(this, DetailActivity::[Link]).apply {

2. flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

3. }

4. val pendingIntent: PendingIntent = [Link](this, 0, intent, 0)

Alih alih menggunakan cara di atas, Anda akan menggunakan


TaskStackBuilder untuk membuat PendingIntent yang memiliki back stack
seperti ini:

 Kotlin
 Java

1. val pendingIntent = [Link](this)

2. .addParentStack(DetailActivity::[Link])

3. .addNextIntent(notifDetailIntent)

4. .getPendingIntent(110, PendingIntent.FLAG_UPDATE_CURRENT)

Dengan menggunakan PendingIntent ini, maka ketika Anda menuju


DetailActivity dari notifikasi dan tekan tombol back, maka aplikasi akan
kembali ke halaman MainActivity.

Untuk memahami lebih lanjut Anda dapat melihat tautan berikut:

 Start an Activity from a Notification


Up and Back Button

Selanjutnya, Anda pasti sudah mengenali dua tombol back yang ada di
Android. Pertama tombol yang berada di bagian bawah dan menempel ke
peranti, yang bernama system back button. Tombol back kedua berada
pada toolbar yang disebut up button.

Penggunaan kedua tombol di atas hampir sama. Yaitu memungkinkan


pengguna pindah ke activity sebelumnya. Bedanya, up button terbatas
hanya untuk hierarki antar layar dalam sebuah Aplikasi. Sementara itu,
system back button akan mengembalikan pengguna ke halaman home
screen bila pada saat itu pengguna berada pada root activity.

Kita telah mempelajari alur activity pada penjelasan di atas. Kita juga
telah belajar bagaimana menerapkan fitur notifikasi. Pada modul
berikutnya, kita akan mempelajari bagaimana menangani kejadian ketika
notifikasi di atas ditekan oleh pengguna dan mengarahkan pengguna ke
activity yang berkaitan. Kita juga akan mempelajari bagaimana
menambahkan up button pada toolbar di tiap activity.
Latihan Task dan Back Stack dengan TaskStackBuilder
Tujuan

Pada latihan kali ini kita akan mencoba menerapkan skenario yang mirip
dengan kejadian pada gambar berikut:

Pada skenario di atas ketika pengguna menakan tombol membuat email


baru, maka pengguna akan diarahkan ke activity yang bernama
ComposeActivity. Di Activity ini, ketika pengguna menekan tombol up
button, maka pada halaman utama aplikasi akan tampil InboxActivity.
Hal yang sama akan terjadi bila pada ComposeActivity, pengguna
menekan system back button. InboxActivity akan ditampilkan pula.

Perbedaan baru muncul ketika pengguna berada di halaman utama,


InboxActivity. Hanya system back button saja yang dapat digunakan
untuk menutup aplikasi. Ketika tombol ini ditekan, maka activity utama
akan dihancurkan. Semua task yang dimiliki aplikasi akan dihapus dari
memori. Di tahap akhir, pengguna akan dikembalikan ke halaman home
screen dari perangkat Androidnya.

Alur di atas tidak hanya berlaku pada widget saja. Ia juga dapat terjadi
pada notifikasi. Ketika terjadi pada notifikasi, mekanisme tersebut
bernama deeplink.

Pada codelab kali ini, Anda akan membuat aplikasi seperti berikut:
Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama MyDeepNavigation.


2. Mengatur tampilan pada berkas layout XML.
3. Menambahkan TaskStackBuilder (PendingIntent) untuk melakukan
perpindahan ke Activity tertentu.
4. Menjalankan aplikasi.

Codelab TaskStackBuilder Sederhana

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:

Nama Project MyDeepNavigation

Target & Minimum Target Phone and Tablet, Api


SDK level 21

Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Artifacts True

Language Kotlin/Java
2. Yang pertama khusus bahasa Kotlin tambahkan view binding pada
[Link](Module) seperti ini:

1. android {

2. ...

3. buildFeatures {

4. viewBinding = true

5. }

6. }

Kemudian pada activity_main.xml sesuaikan kodenya menjadi seperti ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:padding="16dp"

8. android:orientation="horizontal"

9. tools:context="[Link]">

10. <Button

11. android:id="@+id/btn_open_detail"

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/open_detail"/>

15. </LinearLayout>
Selanjutnya, buat activity baru dengan nama DetailActivity dan sesuaikan kode
di dalam activity_detail.xml menjadi seperti berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_detail"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:padding="16dp"

8. android:orientation="vertical"

9. tools:context="[Link]">

10. <TextView

11. android:id="@+id/tv_title"

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/title"

15. android:textStyle="bold"

16. android:layout_marginBottom="16dp"/>

17. <TextView

18. android:id="@+id/tv_message"

19. android:layout_width="match_parent"

20. android:layout_height="wrap_content"

21. android:text="@string/message"

22. android:layout_marginBottom="16dp"/>

23. </LinearLayout>

Jangan lupa untuk menambahkan teks yang ada di layout ke dalam [Link].

1. <resources>
2. <string name="app_name">MyDeepNavigation</string>

3. <string name="activity_home">Home</string>

4. <string name="activity_detail">Detail</string>

5.

6. <string name="hello_blank_fragment">Hello blank fragment</string>

7. <string name="title">Title</string>

8. <string name="message">Message</string>

9. <string name="open_detail">Open Detail</string>

10. <string name="notification_title">Hi, how are you?</string>

11. <string name="notification_message">Do you have any plan this


weekend? Let\'s hangout</string>

12. <string name="detail_title">Hola, Good News</string>

13. <string name="detail_message">Now you can learn android in


Dicoding</string>

14. </resources>

Pada DetailActivity lengkapi kodenya menjadi seperti berikut:

 Kotlin
 Java

1. class DetailActivity : AppCompatActivity() {

2.

3. private lateinit var binding: ActivityDetailBinding

4.

5. companion object {

6. const val EXTRA_TITLE = "extra_title"

7. const val EXTRA_MESSAGE = "extra_message"

8. }

9.

10. override fun onCreate(savedInstanceState: Bundle?) {


11. [Link](savedInstanceState)

12. binding = [Link](layoutInflater)

13. setContentView([Link])

14.

15. val title = [Link](EXTRA_TITLE)

16. val message = [Link](EXTRA_MESSAGE)

17.

18. [Link] = title

19. [Link] = message

20. }

21. }

Buka berkas [Link]. Tambahkan atribut


android:launchMode="singleTop" pada MainActivity dan
android:parentActivityName=".MainActivity" pada DetailActivity. Kemudian
tambahkan beberapa baris berikut ke dalam DetailActivity.

1. <meta-data

2. android:name="[Link].PARENT_ACTIVITY"

3. android:value=".MainActivity" />

Sehingga secara keseluruhan [Link] kita miliki menjadi


seperti berikut.

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4.

5. <application

6. android:allowBackup="true"

7. android:icon="@mipmap/ic_launcher"

8. android:label="@string/app_name"
9. android:roundIcon="@mipmap/ic_launcher_round"

10. android:supportsRtl="true"

11. android:theme="@style/AppTheme">

12. <activity

13. android:name=".MainActivity"

14. android:launchMode="singleTop">

15. <intent-filter>

16. <action android:name="[Link]" />

17. <category android:name="[Link]" />

18. </intent-filter>

19. </activity>

20. <activity

21. android:name=".DetailActivity"

22. android:label="@string/activity_detail"

23. android:parentActivityName=".MainActivity">

24. <meta-data

25. android:name="[Link].PARENT_ACTIVITY"

26. android:value=".MainActivity" />

27. </activity>

28. </application>

29.

30. </manifest>

Dan terakhir pada MainActivity lengkapi kodenya menjadi seperti berikut:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.
3. private lateinit var binding: ActivityMainBinding

4.

5. override fun onCreate(savedInstanceState: Bundle?) {

6. [Link](savedInstanceState)

7. binding = [Link](layoutInflater)

8. setContentView([Link])

9.

10. [Link](this)

11. }

12.

13.

14. override fun onClick(v: View) {

15. if ([Link] == [Link].btn_open_detail) {

16. val detailIntent = Intent(this@MainActivity,


DetailActivity::[Link])

17. [Link](DetailActivity.EXTRA_TITLE,
getString([Link].detail_title))

18. [Link](DetailActivity.EXTRA_MESSAGE,
getString([Link].detail_message))

19. startActivity(detailIntent)

20. }

21. }

22. }

Setelah itu buat fungsi untuk menampilkan notification sehingga kode pada
MainActivity menjadi seperti ini:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.

3. private lateinit var binding: ActivityMainBinding


4.

5. override fun onCreate(savedInstanceState: Bundle?) {

6. [Link](savedInstanceState)

7. binding = [Link](layoutInflater)

8. setContentView([Link])

9.

10. [Link](this)

11.

12.

13. showNotification(this@MainActivity,
getString([Link].notification_title),
getString([Link].notification_message), 110)

14.

15. }

16.

17.

18. override fun onClick(v: View) {

19. if ([Link] == [Link].btn_open_detail) {

20. val detailIntent = Intent(this@MainActivity,


DetailActivity::[Link])

21. [Link](DetailActivity.EXTRA_TITLE,
getString([Link].detail_title))

22. [Link](DetailActivity.EXTRA_MESSAGE,
getString([Link].detail_message))

23. startActivity(detailIntent)

24. }

25. }

26.

27. private fun showNotification(context: Context, title: String,


message: String, notifId: Int) {

28. val CHANNEL_ID = "Channel_1"


29. val CHANNEL_NAME = "Navigation channel"

30.

31. val notifDetailIntent = Intent(this, DetailActivity::[Link])

32. [Link](DetailActivity.EXTRA_TITLE, title)

33. [Link](DetailActivity.EXTRA_MESSAGE,
message)

34.

35. val pendingIntent = [Link](this)

36. .addParentStack(DetailActivity::[Link])

37. .addNextIntent(notifDetailIntent)

38. .getPendingIntent(110,
PendingIntent.FLAG_UPDATE_CURRENT)

39.

40. val notificationManagerCompat =


[Link](Context.NOTIFICATION_SERVICE) as
NotificationManager

41. val alarmSound =


[Link](RingtoneManager.TYPE_NOTIFICA
TION)

42. val builder = [Link](context,


CHANNEL_ID)

43. .setContentTitle(title)

44. .setSmallIcon([Link].ic_email_black_24dp)

45. .setContentText(message)

46. .setColor([Link](context,
[Link]))

47. .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))

48. .setSound(alarmSound)

49. .setContentIntent(pendingIntent)

50. .setAutoCancel(true)

51.

52. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {


53. val channel = NotificationChannel(CHANNEL_ID,

54. CHANNEL_NAME,

55. NotificationManager.IMPORTANCE_DEFAULT)

56.

57. [Link](true)

58. [Link] = longArrayOf(1000, 1000, 1000,


1000, 1000)

59.

60. [Link](CHANNEL_ID)

61.

62.
[Link](channel)

63. }

64.

65. val notification = [Link]()

66.

67. [Link](notifId, notification)

68. }

69. }

Untuk ikon yang digunakan oleh notifikasi, Anda bisa mengunduhnya di


sini. Pilih ikon email pada kategori communication. Gunakan yang
berwarna hitam.
Setelah diunduh, buka direktorinya dan gunakan ikon yang berada di
direktori hdpi.

7. Selesai. Sekarang jalankan aplikasinya. Perhatikan notifikasi yang muncul


3 detik setelah aplikasi dijalankan, dan lihat apa yang terjadi di Activity
Detail.
Ketika notifikasi muncul dan Anda menekannya, Anda akan diarahkan ke
DetailActivity. Di DetailActivity, bila Anda menekan up button, Anda
akan diarahkan ke MainActivity.

Implementasi seperti ini lumrah terjadi ketika Anda sudah membuat


sebuah aplikasi dengan alur yang kompleks, apalagi ketika sudah
terintegrasi dengan layanan push notification. Esensi dari latihan di atas
adalah Anda harus bisa menerapkan mekanisme task dan stack yang tidak
standard namun sesuai dengan pola navigasi yang dianjurkan. Anda bisa
menemukan implementasi ini di banyak aplikasi hebat seperti Twitter,
Gmail, Instagram dan juga Tokopedia.

Bedah Kode
TaskStackBuilder

Coba perhatikan baris berikut ini:

 Kotlin
 Java

1. val notifDetailIntent = Intent(this, DetailActivity::[Link])

2. [Link](DetailActivity.EXTRA_TITLE, title)

3. [Link](DetailActivity.EXTRA_MESSAGE, message)

4.

5. val pendingIntent = [Link](this)


6. .addParentStack(DetailActivity::[Link])

7. .addNextIntent(notifDetailIntent)

8. .getPendingIntent(110, PendingIntent.FLAG_UPDATE_CURRENT)

Di sini kita memanfaatkan TaskStackBuilder API untuk membuat sebuah


back stack baru yang akan dimasukkan ke dalam task yang sudah ada.
Ketika kelas DetailActivity dijalankan, kemudian pengguna menekan
tombol back baik itu system back button maupun up button, maka
pengguna akan diarahkan ke ParentActivity dari DetailActivity.
ParentActivity tersebut didefinisikan di berkas [Link]
sebagai berikut:

1. <activity

2. android:name=".DetailActivity"

3. android:label="@string/activity_detail"

4. android:parentActivityName=".MainActivity">

5. <meta-data

6. android:name="[Link].PARENT_ACTIVITY"

7. android:value=".MainActivity" />

8. </activity>

Sedangkan, penambahan atribut launchMode pada MainActivity di


[Link] dimaksudkan agar MainActivity tidak selalu
menciptakan dirinya kembali (recreate) dan akan diarahkan ke instance
MainActivity yang telah tercipta di memori.

1. <activity android:name=".MainActivity"

2. android:launchMode="singleTop">

3. <intent-filter>

4. <action android:name="[Link]" />

5. <category android:name="[Link]" />

6. </intent-filter>

7. </activity>
Pada materi ini, Anda telah mempelajari proses yang tidak baku dalam
menjalankan sebuah Activity. Gunakan tautan di bawah untuk memahami
topik bahasan modul ini lebih dalam.

 Task and Back Stack


 Navigation
 Proper Back Navigation
 Up Navigation
 Back and Up : Navigations

Anda bisa mengakses source code di:

 Latihan Task dan Back Stack dengan TaskStackBuilder


Teori AlarmManager

Adakalanya kita memerlukan suatu mekanisme untuk mengulang sebuah


proses di waktu tertentu. Misalnya, kita ingin memiliki fungsi pengingat
(reminder) pada aplikasi event, jadwal tayang film di bioskop, atau
aplikasi alarm untuk mengingatkan kita bangun di pagi hari. Di sinilah
alarm manager berperan penting.

Alarm manager akan memberikan kemudahan bagi aplikasi untuk


melakukan operasi berbasis waktu di luar daur hidup aplikasi itu sendiri.

Contohnya seperti yang baru saja disebutkan di atas. Umumnya alarm


memiliki beberapa karakteristik, antara lain:

1. Dapat menjalankan obyek intent berdasarkan waktu dan interval yang


ditentukan.
2. Bisa bekerja dengan baik bersama broadcast receiver untuk menjalankan
komponen lain seperti service untuk melakukan operasi tertentu.
3. Alarm berjalan di luar daur hidup aplikasi induknya sehingga kita bisa
merancang fungsi alarm untuk melakukan sebuah aksi ketika aplikasi
sedang tidak dijalankan, bahkan ketika peranti dalam keadaan idle atau
sleep.
4. Esensi utama dari alarm manager adalah untuk meminimalkan
penggunaan resource dan menghindari penggunaan timer dan
background service yang berkepanjangan untuk melakukan operasi atau
aksi yang dibutuhkan.

Penggunaan dan perancangan alarm manager yang tepat akan membuat


aplikasi kita menjadi lebih baik, serta tentunya memberikan pengalaman
yang juga berkesan. Namun sebaliknya desain yang buruk akan menjadi
bumerang bagi aplikasi kita. Akibat dari rancangan yang tidak sesuai akan
menguras baterai di peranti kita.

Penggunaan alarm manager yang baik adalah dengan menerapkan cara


berikut ini:

1. Lakukan hanya untuk proses di lokal.


2. Jaga frekuensi alarm yang dijalankan dengan interval yang tidak pendek.
3. Jika tidak terlalu penting, hindari penggunaan tipe alarm yang dapat
membangunkan peranti seperti RTC_WAKEUP.
4. Jika alarm manager digunakan untuk menjadwalkan scheduling task yang
terhubung ke jaringan dan melakukan request ke server, misalnya unduh
data terbaru atau sinkronisasi data, maka hindari penggunaan
setRepeating() dan gunakan setInExactRepeating() dengan penggunaan
tipe alarm berbasis ELAPSED_REALTIME. Pendekatan tersebut merupakan
strategi untuk mengelompokkan proses alarm dari aplikasi yang berbeda
dengan dijalankan secara bersamaan agar lebih menghemat daya baterai.

Catatan : Di atas API 19 KitKat semua pemanggilan repeating


alarm adalah menggunakan in exact repeating. Baca tautan berikut ini.
Untuk lebih mendalami topik alarm manager, Anda dapat membaca
tautan berikut ini:

 Alarm Manager
 Scheduling Alarm

Latihan One Time AlarmManager


Tujuan

Kali ini kita akan membuat sebuah proyek sederhana dengan skenario
seperti berikut:

1. Aplikasi akan memiliki kemampuan untuk melakukan one-time-execution


alarm dan alarm yang berulang berdasarkan waktu yang ditentukan.
2. Aplikasi akan menyimpan beberapa nilai untuk kedua tipe alarm tersebut.
3. Aplikasi akan menampilkan notifikasi dengan suara dan getar ketika alarm
dijalankan.

Codelab pada kali ini akan menghasilkan aplikasi seperti ini:

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:
1. Membuat project baru dengan nama MyAlarmManager.
2. Mengatur tampilan pada berkas layout XML.
3. Membuat kelas AlarmReceiver untuk mengatur konfigurasi penyimpanan
file.
4. Mengimplementasikan AlarmReceiver ke dalam Activity.
5. Menjalankan aplikasi.

Codelab Alarm Manager Sederhana

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:

Nama Project MyAlarmManager

Target & Minimum Target Phone and Tablet, Api


SDK level 21

Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Artifacts True

Language Kotlin/Java

2. Sebelum kita melangkah lebih jauh, yuk kita siapkan dulu beberapa aset
yang akan digunakan pada aplikasi ini. Caranya cukup klik kanan pada
folder drawable → New → Vector Asset. Kemudian cara icon date pada
Clip Art seperti berikut:

3. Ganti nama seperti di atas, kemudian klik Next dan Finish. Lakukan hal
yang sama untuk menambahkan icon baru untuk date seperti berikut:
4. Selanjutnya pada activity_main.xmlsesuaikan kodenya menjadi seperti
berikut.

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:orientation="vertical"

8. android:padding="16dp"

9. tools:context="[Link]">

10.

11. <TextView

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:layout_marginBottom="16dp"

15. android:text="@string/one_time_alarm"

16. android:textSize="16sp"
17. android:textStyle="bold" />

18.

19. <LinearLayout

20. android:layout_width="match_parent"

21. android:layout_height="wrap_content"

22. android:gravity="center_vertical"

23. android:orientation="horizontal">

24.

25. <ImageButton

26. android:id="@+id/btn_once_date"

27. style="@style/[Link]"

28. android:layout_width="50dp"

29. android:layout_height="wrap_content"

30. android:src="@drawable/ic_date_range_black" />

31.

32. <TextView

33. android:id="@+id/tv_once_date"

34. android:layout_width="wrap_content"

35. android:layout_height="wrap_content"

36. android:text="@string/alarm_date" />

37. </LinearLayout>

38.

39. <LinearLayout

40. android:layout_width="match_parent"

41. android:layout_height="wrap_content"

42. android:gravity="center_vertical"

43. android:orientation="horizontal">
44.

45. <ImageButton

46. android:id="@+id/btn_once_time"

47. android:layout_width="50dp"

48. android:layout_height="wrap_content"

49. android:src="@drawable/ic_access_time_black" />

50.

51. <TextView

52. android:id="@+id/tv_once_time"

53. android:layout_width="wrap_content"

54. android:layout_height="wrap_content"

55. android:text="@string/alarm_time" />

56. </LinearLayout>

57.

58. <EditText

59. android:id="@+id/edt_once_message"

60. android:layout_width="match_parent"

61. android:layout_height="wrap_content"

62. android:layout_marginBottom="16dp"

63. android:hint="@string/alarm_message"

64. android:inputType="text" />

65.

66. <Button

67. android:id="@+id/btn_set_once_alarm"

68. android:layout_width="match_parent"

69. android:layout_height="wrap_content"

70. android:layout_marginBottom="16dp"
71. android:text="@string/set_one_time_alarm" />

72. </LinearLayout>

Jangan lupa untuk menambahkan beberapa resource string di bawah ini di dalam
res → values → [Link].

1. <resources>

2. <string name="app_name">MyAlarmManager</string>

3. <string name="one_time_alarm">One Time Alarm</string>

4. <string name="alarm_date">Tanggal Alarm</string>

5. <string name="set_one_time_alarm">Set One Time Alarm</string>

6. <string name="alarm_message">Pesan Alarm</string>

7. <string name="repeating_alarm">Repeating Alarm</string>

8. <string name="set_repeating_alarm">Set Repeating Alarm</string>

9. <string name="cancel_alarm">Batalkan Alarm</string>

10. <string name="alarm_time">Waktu Alarm</string>

11. </resources>

Resource string ini akan kita gunakan selama latihan .

Sehingga ketika dijalankan, aplikasi dari activity_main.xml seperti berikut:


Setelah itu kita akan membuat sebuah kelas baru dengan nama
AlarmReceiver.
Caranya, klik kanan pada package project yang ada saat ini → New →
Other → Broadcast Receiver.

Setelah itu kita akan membuat metode-metode tambahan untuk kelas


AlarmReceiver.

 Kotlin
 Java

1. class AlarmReceiver : BroadcastReceiver() {

2.
3. companion object {

4. const val TYPE_ONE_TIME = "OneTimeAlarm"

5. const val TYPE_REPEATING = "RepeatingAlarm"

6. const val EXTRA_MESSAGE = "message"

7. const val EXTRA_TYPE = "type"

8.

9. // Siapkan 2 id untuk 2 macam alarm, onetime dan repeating

10. private const val ID_ONETIME = 100

11. private const val ID_REPEATING = 101

12. }

13.

14. override fun onReceive(context: Context, intent: Intent) {

15.

16. }

17. }

Kemudian kita tambahkan kode di dalam metode onReceive() dan kita


tambahkan metode showToast di dalam kelas AlarmReceiver.

 Kotlin
 Java

1. class AlarmReceiver : BroadcastReceiver() {

2. ...

3.

4. override fun onReceive(context: Context, intent: Intent) {

5. val type = [Link](EXTRA_TYPE)

6. val message = [Link](EXTRA_MESSAGE)

7.

8. val title = if ([Link](TYPE_ONE_TIME, ignoreCase = true))


TYPE_ONE_TIME else TYPE_REPEATING
9. val notifId = if ([Link](TYPE_ONE_TIME, ignoreCase = true))
ID_ONETIME else ID_REPEATING

10.

11. showToast(context, title, message)

12. }

13.

14. private fun showToast(context: Context, title: String, message:


String?) {

15. [Link](context, "$title : $message",


Toast.LENGTH_LONG).show()

16. }

17. }

Berikutnya kita tambahkan metode untuk mengatur alarm manager.

 Kotlin
 Java

1. ...

2. import [Link].*

3. import [Link]

4. import [Link]

5.

6. class AlarmReceiver : BroadcastReceiver() {

7.

8. companion object {

9. ...

10. private const val DATE_FORMAT = "yyyy-MM-dd"

11. private const val TIME_FORMAT = "HH:mm"

12. }

13. ...

14. fun setOneTimeAlarm(context: Context, type: String, date: String,


time: String, message: String) {
15.

16. if (isDateInvalid(date, DATE_FORMAT) || isDateInvalid(time,


TIME_FORMAT)) return

17.

18. val alarmManager =


[Link](Context.ALARM_SERVICE) as AlarmManager

19. val intent = Intent(context, AlarmReceiver::[Link])

20. [Link](EXTRA_MESSAGE, message)

21. [Link](EXTRA_TYPE, type)

22.

23. Log.e("ONE TIME", "$date $time")

24. val dateArray = [Link]("-").toTypedArray()

25. val timeArray = [Link](":").toTypedArray()

26.

27. val calendar = [Link]()

28. [Link]([Link], [Link](dateArray[0]))

29. [Link]([Link], [Link](dateArray[1]) - 1)

30. [Link](Calendar.DAY_OF_MONTH,
[Link](dateArray[2]))

31. [Link](Calendar.HOUR_OF_DAY,
[Link](timeArray[0]))

32. [Link]([Link], [Link](timeArray[1]))

33. [Link]([Link], 0)

34.

35. val pendingIntent = [Link](context,


ID_ONETIME, intent, 0)

36. [Link](AlarmManager.RTC_WAKEUP,
[Link], pendingIntent)

37.

38. [Link](context, "One time alarm set up",


Toast.LENGTH_SHORT).show()
39. }

40.

41. private fun isDateInvalid(date: String, format: String): Boolean {

42. return try {

43. val df = SimpleDateFormat(format, [Link]())

44. [Link] = false

45. [Link](date)

46. false

47. } catch (e: ParseException) {

48. true

49. }

50. }

51. }

Lalu kita tambahkan notifikasi untuk melihat hasil dari alarm manager yang
sudah dibuat sebelumnya.

 Kotlin
 Java

1. class AlarmReceiver : BroadcastReceiver() {

2. ...

3.

4. private fun showAlarmNotification(context: Context, title: String,


message: String, notifId: Int) {

5.

6. val channelId = "Channel_1"

7. val channelName = "AlarmManager channel"

8.

9. val notificationManagerCompat =
[Link](Context.NOTIFICATION_SERVICE) as
NotificationManager
10. val alarmSound =
[Link](RingtoneManager.TYPE_NOTIFICATION)

11. val builder = [Link](context, channelId)

12. .setSmallIcon([Link].ic_access_time_black)

13. .setContentTitle(title)

14. .setContentText(message)

15. .setColor([Link](context,
[Link]))

16. .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))

17. .setSound(alarmSound)

18.

19. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

20.

21. val channel = NotificationChannel(channelId,

22. channelName,

23. NotificationManager.IMPORTANCE_DEFAULT)

24.

25. [Link](true)

26. [Link] = longArrayOf(1000, 1000, 1000, 1000,


1000)

27.

28. [Link](channelId)

29.

30. [Link](channel)

31. }

32.

33. val notification = [Link]()

34.

35. [Link](notifId, notification)


36.

37. }

38. }

Setelah membuat sebuah AlarmReceiver, kita tambahkan kelas untuk


membantu kita mengambil waktu. Pertama, kita buat fragment untuk setup
tanggal. Buatlah kelas dengan nama DatePickerFragment.
Selanjutnya ubah kelas DatePickerFragmentmenjadi sebagai berikut:

 Kotlin
 Java

1. ...

2. import [Link]

3. import [Link].*

4.

5. class DatePickerFragment : DialogFragment(),


[Link] {

6.

7. private var mListener: DialogDateListener? = null

8.

9. override fun onAttach(context: Context) {

10. [Link](context)

11. mListener = context as DialogDateListener?

12. }

13.

14. override fun onDetach() {

15. [Link]()

16. if (mListener != null) {

17. mListener = null

18. }

19. }
20.

21. override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

22. val calendar = [Link]()

23. val year = [Link]([Link])

24. val month = [Link]([Link])

25. val date = [Link]([Link])

26.

27. return DatePickerDialog(activity as Context, this, year, month,


date)

28. }

29.

30. override fun onDateSet(view: DatePicker, year: Int, month: Int,


dayOfMonth: Int) {

31. mListener?.onDialogDateSet(tag, year, month, dayOfMonth)

32. }

33.

34. interface DialogDateListener {

35. fun onDialogDateSet(tag: String?, year: Int, month: Int,


dayOfMonth: Int)

36. }

37.

38. }

Ketika mengimport komponen Calendar, pastikan Anda menggunakan


[Link]. Jangan sampai keliru dengan komponen calendar lainnya yang
baru ditambahkan pada API 24.

Selanjutnya kita buat kelas baru lagi dengan nama TimePickerFragment. Kali
ini kelas fragment ini akan membantu untuk setup jam.
Ubah kelas TimePickerFragmentmenjadi sebagai berikut:

 Kotlin
 Java

1. ...
2. import [Link]

3. import [Link].*

4.

5. class TimePickerFragment : DialogFragment(),


[Link] {

6.

7. private var mListener: DialogTimeListener? = null

8.

9. override fun onAttach(context: Context) {

10. [Link](context)

11. mListener = context as DialogTimeListener?

12. }

13.

14. override fun onDetach() {

15. [Link]()

16. if (mListener != null) {

17. mListener = null

18. }

19. }

20.

21. override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

22. val calendar = [Link]()

23. val hour = [Link](Calendar.HOUR_OF_DAY)

24. val minute = [Link]([Link])

25. val formatHour24 = true

26. return TimePickerDialog(activity, this, hour, minute, formatHour24)

27. }

28.
29. override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int)
{

30. mListener?.onDialogTimeSet(tag, hourOfDay, minute)

31. }

32.

33. interface DialogTimeListener {

34. fun onDialogTimeSet(tag: String?, hourOfDay: Int, minute: Int)

35. }

36. }

Sekarang saatnya kita memberikan aksi pada View di dalam kelas MainActivity.

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link] {

2.

3. private var binding: ActivityMainBinding? = null

4. private lateinit var alarmReceiver: AlarmReceiver

5.

6. override fun onCreate(savedInstanceState: Bundle?) {

7. [Link](savedInstanceState)

8.

9. binding = [Link](layoutInflater)

10. setContentView(binding?.root)

11.

12. // Listener one time alarm

13. binding?.btnOnceDate?.setOnClickListener(this)

14. binding?.btnOnceTime?.setOnClickListener(this)

15. binding?.btnSetOnceAlarm?.setOnClickListener(this)

16.
17. alarmReceiver = AlarmReceiver()

18. }

19.

20. override fun onClick(v: View) {

21. when ([Link]) {

22. [Link].btn_once_date -> {

23.

24. }

25. [Link].btn_once_time -> {

26.

27. }

28. [Link].btn_set_once_alarm -> {

29.

30. }

31. }

32. }

33.

34. override fun onDestroy() {

35. [Link]()

36.

37. binding = null

38. }

39. }

Jangan lupa untuk menambahkan konfigurasi untuk mengaktifkan ViewBinding


pada [Link] (module: app) seperti berikut dan sync project:

1. android {

2.

3. ...
4.

5. buildFeatures {

6. viewBinding = true

7. }

8. }

Kita telah mengimplementasikan onClick(). Selanjutnya kita implementasikan


kelas untuk pengambil waktu yang sudah kita buat sebelumnya dan memberikan
logika untuk onClick()tersebut.

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(),


[Link], [Link],
[Link] {

2.

3. ...

4.

5. companion object {

6. private const val DATE_PICKER_TAG = "DatePicker"

7. private const val TIME_PICKER_ONCE_TAG = "TimePickerOnce"

8. private const val TIME_PICKER_REPEAT_TAG = "TimePickerRepeat"

9. }

10.

11. ...

12.

13. override fun onClick(v: View) {

14. when ([Link]) {

15. [Link].btn_once_date -> {

16. val datePickerFragment = DatePickerFragment()

17. [Link](supportFragmentManager,
DATE_PICKER_TAG)
18. }

19. [Link].btn_once_time -> {

20. val timePickerFragmentOne = TimePickerFragment()

21. [Link](supportFragmentManager,
TIME_PICKER_ONCE_TAG)

22. }

23. [Link].btn_set_once_alarm -> {

24. val onceDate = binding?.tvOnceDate?.[Link]()

25. val onceTime = binding?.tvOnceTime?.[Link]()

26. val onceMessage = binding?.edtOnceMessage?.[Link]()

27.

28. [Link](this,
AlarmReceiver.TYPE_ONE_TIME,

29. onceDate,

30. onceTime,

31. onceMessage)

32. }

33. }

34. }

35.

36. override fun onDialogDateSet(tag: String?, year: Int, month: Int,


dayOfMonth: Int) {

37.

38. // Siapkan date formatter-nya terlebih dahulu

39. val calendar = [Link]()

40. [Link](year, month, dayOfMonth)

41. val dateFormat = SimpleDateFormat("yyyy-MM-dd",


[Link]())

42.
43. // Set text dari textview once

44. binding?.tvOnceDate?.text = [Link]([Link])

45. }

46.

47. override fun onDialogTimeSet(tag: String?, hourOfDay: Int, minute:


Int) {

48.

49. // Siapkan time formatter-nya terlebih dahulu

50. val calendar = [Link]()

51. [Link](Calendar.HOUR_OF_DAY, hourOfDay)

52. [Link]([Link], minute)

53.

54. val dateFormat = SimpleDateFormat("HH:mm", [Link]())

55.

56. // Set text dari textview berdasarkan tag

57. when (tag) {

58. TIME_PICKER_ONCE_TAG -> binding?.tvOnceTime?.text =


[Link]([Link])

59. TIME_PICKER_REPEAT_TAG -> {}

60. else -> {

61. }

62. }

63. }

64.

65. ...

66. }

Maka kelas MainActivitymenjadi seperti berikut:

 Kotlin
 Java
1. class MainActivity : AppCompatActivity(), [Link],
[Link], [Link]
{

2.

3. private var binding: ActivityMainBinding? = null

4. private lateinit var alarmReceiver: AlarmReceiver

5.

6. companion object {

7. private const val DATE_PICKER_TAG = "DatePicker"

8. private const val TIME_PICKER_ONCE_TAG = "TimePickerOnce"

9. private const val TIME_PICKER_REPEAT_TAG = "TimePickerRepeat"

10. }

11.

12. override fun onCreate(savedInstanceState: Bundle?) {

13. [Link](savedInstanceState)

14.

15. binding = [Link](layoutInflater)

16. setContentView(binding?.root)

17.

18. // Listener one time alarm

19. binding?.btnOnceDate?.setOnClickListener(this)

20. binding?.btnOnceTime?.setOnClickListener(this)

21. binding?.btnSetOnceAlarm?.setOnClickListener(this)

22.

23. alarmReceiver = AlarmReceiver()

24. }

25.

26. override fun onClick(v: View) {


27. when ([Link]) {

28. [Link].btn_once_date -> {

29. val datePickerFragment = DatePickerFragment()

30. [Link](supportFragmentManager,
DATE_PICKER_TAG)

31. }

32. [Link].btn_once_time -> {

33. val timePickerFragmentOne = TimePickerFragment()

34. [Link](supportFragmentManager,
TIME_PICKER_ONCE_TAG)

35. }

36. [Link].btn_set_once_alarm -> {

37. val onceDate = binding?.tvOnceDate?.[Link]()

38. val onceTime = binding?.tvOnceTime?.[Link]()

39. val onceMessage = binding?.edtOnceMessage?.[Link]()

40.

41. [Link](this, AlarmReceiver.TYPE_ONE_TIME,

42. onceDate,

43. onceTime,

44. onceMessage)

45. }

46. }

47. }

48.

49. override fun onDialogDateSet(tag: String?, year: Int, month: Int, dayOfMonth:
Int) {

50.

51. // Siapkan date formatter-nya terlebih dahulu

52. val calendar = [Link]()


53. [Link](year, month, dayOfMonth)

54. val dateFormat = SimpleDateFormat("yyyy-MM-dd", [Link]())

55.

56. // Set text dari textview once

57. binding?.tvOnceDate?.text = [Link]([Link])

58. }

59.

60. override fun onDialogTimeSet(tag: String?, hourOfDay: Int, minute: Int) {

61.

62. // Siapkan time formatter-nya terlebih dahulu

63. val calendar = [Link]()

64. [Link](Calendar.HOUR_OF_DAY, hourOfDay)

65. [Link]([Link], minute)

66.

67. val dateFormat = SimpleDateFormat("HH:mm", [Link]())

68.

69. // Set text dari textview berdasarkan tag

70. when (tag) {

71. TIME_PICKER_ONCE_TAG -> binding?.tvOnceTime?.text =


[Link]([Link])

72. TIME_PICKER_REPEAT_TAG -> {}

73. else -> {

74. }

75. }

76. }

77.

78. override fun onDestroy() {

79. [Link]()
80.

81. binding = null

82. }

83. }

Jangan lupa untuk menambahkan permission pada [Link]


baris berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4.

5. <uses-permission android:name="[Link].WAKE_LOCK" />

6. <uses-permission android:name="[Link]" />

7.

8. <application

9. ... >

10. <activity android:name=".MainActivity">

11. ...

12. </activity>

13.

14. <receiver

15. android:name=".AlarmReceiver"

16. android:enabled="true"

17. android:exported="true" />

18.

19. </application>

20. </manifest>

Jika sudah selesai, silakan jalankan aplikasi Anda. Dengan menekan tombol yang
ada pada sebelah TextView tanggal dan waktu, Anda dapat mengganti tanggal
dan waktu sesuai keinginan. Isikan juga pesan alarmmya.
Setelah tanggal, waktu dan pesan telah Anda isi, lanjutkan dengan klik tombol
SET ONE TIME ALARM dan silakan tunggu sampai waktu yang ditentukan
sehingga alarm bekerja. Jika sukses, maka waktu yang kita set di awal akan
tampil pada obyek TextView yang tersedia di sana.

Ketika waktunya sudah sesuai, maka alarm bekerja dan menampilkan notifikasi
getaran dan suara. Anda bisa mengaktifkan notifikasi dengan menambahkan
kode berikut di kelas AlarmReceiver pada baris berikut:

 Kotlin
 Java

1. override fun onReceive(context: Context, intent: Intent) {

2. ...

3. if (message != null) {

4. showAlarmNotification(context, title, message, notifId)

5. }

6. }

Maka akan menjadi seperti berikut ini:


Mudah kan? Sekarang Anda sudah bisa mengimplementasikan one-time-
alarm untuk tanggal dan jam yang telah ditentukan. Fungsi ini bisa
berguna untuk beragam fitur lain. Misalnya jika Anda membuat aplikasi
tentang event, pasti fungsi ini akan sangat berguna sebagai mekanisme
pengingat.

Bedah Kode

Kita mulai dengan skenario nya terlebih dahulu.

Kode pada aplikasi di atas berfungsi untuk menjalankan sebuah alarm


yang bersifat one-time-execution. Logika dasarnya adalah, kita
memanfaatkan fasilitas alarm manager yang terdapat dalam framework
Android untuk menjalankan tugas atau proses tertentu berdasarkan waktu
yang ditentukan.

Di sini kita akan melakukan proses penampilan notifikasi berdasarkan nilai


yang dimasukkan oleh pengguna. Baik itu tanggal, waktu dan pesannya.

Ketika waktu yang ditentukan cocok dengan waktu yang berjalan pada
sistem, maka metode onReceive pada BroadcastReceiver akan
dijalankan.

Logika yang sederhana bukan?


DatePicker dan TimePicker

Ada beberapa istilah baru yang bisa jadi teman-teman pun baru
menemukannya di modul ini. Untuk memahaminya, mari kita lanjut
membedah kode.

Kita awali dengan onClick yang ada di kelas MainActivity. Terdapat


beberapa kondisi when/switch-case yang berguna untuk membedakan
button mana yang disentuh. Button dengan id btn_once_date dan
btn_once_time memiliki fungsi memanggil kelas util untuk menampilkan
date picker dan time picker.

 Kotlin
 Java

1. when ([Link]) {

2. [Link].btn_once_date -> {

3. val datePickerFragment = DatePickerFragment()

4. [Link](supportFragmentManager,
DATE_PICKER_TAG)

5. }

6. [Link].btn_once_time -> {

7. val timePickerFragmentOne = TimePickerFragment()

8. [Link](supportFragmentManager,
TIME_PICKER_ONCE_TAG)

9. }

10. ...

11. }

Selanjutnya, perhatikan pada kelas DatePickerFragment.

 Kotlin
 Java

1. class DatePickerFragment : DialogFragment(),


[Link] {

2. ...
3.

4. override fun onDateSet(view: DatePicker, year: Int, month: Int,


dayOfMonth: Int) {

5. mListener?.onDialogDateSet(tag, year, month, dayOfMonth)

6. }

7.

8. ...

9. }

Fungsi onDateSet akan dipanggil ketika kita memilih tanggal yang kita
inginkan. Kemudian setelah tanggal dipilih maka variable tanggal, bulan
dan tahun akan dikirim ke MainActivity menggunakan bantuan interface
DialogDateListener.

 Kotlin
 Java

1. interface DialogDateListener {

2. fun onDialogDateSet(tag: String?, year: Int, month: Int, dayOfMonth: Int)

3. }

Masih di kelas DatePickerFragment, perhatikan baris di bawah ini:

 Kotlin
 Java

1. override fun onAttach(context: Context?) {

2. [Link](context)

3. mListener = context as DialogDateListener?

4. }

5.

6. override fun onDetach() {

7. [Link]()

8. if (mListener != null) {

9. mListener = null

10. }
11. }

Fungsi onAttach() hanya sekali dipanggil dalam fragment dan berfungsi


untuk mengkaitkan dengan activity pemanggil, sedangkan onDetach()
hanya dipanggil sebelum fragmen tidak lagi dikaitkan dengan activity
pemanggil.

Selanjutnya adalah kelas TimePickerFragment.

 Kotlin
 Java

1. class TimePickerFragment : DialogFragment(),


[Link] {

2. ...

3.

4. override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int)


{

5. mListener?.onDialogTimeSet(tag, hourOfDay, minute)

6. }

7.

8. ...

9. }

Berbeda dengan kelas DatePickerFragment,


TimePickerFragment diimplementasikan dengan
metode [Link]. Variabel yang berbeda
adalah jam dan menit.

Selanjutnya kita coba kembali ke kelas MainActivity. Kode di bawah ini


adalah hasil dari implementasi dari
[Link],
[Link].

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link],


[Link], [Link]
{

2.
3. ...

4.

5. override fun onDialogDateSet(tag: String?, year: Int, month: Int, dayOfMonth:


Int) {

6.

7. // Siapkan date formatter-nya terlebih dahulu

8. val calendar = [Link]()

9. [Link](year, month, dayOfMonth)

10. val dateFormat = SimpleDateFormat("yyyy-MM-dd", [Link]())

11.

12. // Set text dari textview once

13. binding?.tvOnceDate?.text = [Link]([Link])

14. }

15.

16. override fun onDialogTimeSet(tag: String?, hourOfDay: Int, minute: Int) {

17.

18. // Siapkan time formatter-nya terlebih dahulu

19. val calendar = [Link]()

20. [Link](Calendar.HOUR_OF_DAY, hourOfDay)

21. [Link]([Link], minute)

22.

23. val dateFormat = SimpleDateFormat("HH:mm", [Link]())

24.

25. // Set text dari textview berdasarkan tag

26. when (tag) {

27. TIME_PICKER_ONCE_TAG -> binding?.tvOnceTime?.text =


[Link]([Link])

28. TIME_PICKER_REPEAT_TAG -> {}


29. else -> {

30. }

31. }

32. }

33.

34. ...

35. }

Kode di atas adalah hasil dari callback dari


[Link] dan
[Link] yang kemudian akan ditampilkan
hasilnya.

 Kotlin
 Java

1. val dateFormat = SimpleDateFormat("yyyy-MM-dd", [Link]())

Perhatikan kode di atas. Kita menggunakan komponen SimpleDateFormat.


Perhatikan kode “yyyy-MM-dd”, inilah format untuk menampilkan
tanggal dengan format seperti 2016-09-29. Begitu juga untuk waktu
seperti berikut:

 Kotlin
 Java

1. val dateFormat = SimpleDateFormat("HH:mm", [Link]())

Terdapat beberapa format yang dapat kita gunakan dengan komponen


SimpleDateFormat. Untuk mengetahui lebih dalam mengenai
SimpleDateFormat, cek tautan berikut:

 SimpleDateFormat

AlarmReceiver

Lihat pada onClick() bagian dari MainActivity berikut ini:

 Kotlin
 Java

1. [Link].btn_set_once_alarm -> {

2. val onceDate = binding?.tvOnceDate?.[Link]()


3. val onceTime = binding?.tvOnceTime?.[Link]()

4. val onceMessage = binding?.edtOnceMessage?.[Link]()

5.

6. [Link](this, AlarmReceiver.TYPE_ONE_TIME,

7. onceDate,

8. onceTime,

9. onceMessage)

10. }

Kode di atas berfungsi untuk memanggil metode setOneTimeAlarm yang


berada di dalam AlarmReceiver.

Selanjutnya pada AlarmReceiver kita membuat beberapa variabel


konstanta penanda (flag) yang akan digunakan di keseluruhan bagian
kode, di antaranya:

 Kotlin
 Java

1. const val TYPE_ONE_TIME = "OneTimeAlarm"

2. const val TYPE_REPEATING = "RepeatingAlarm"

Dua baris di atas adalah konstanta untuk menentukan tipe alarm. Dan
selanjutnya, dua baris di bawah ini adalah konstanta untuk intent key.

 Kotlin
 Java

1. const val EXTRA_MESSAGE = "message"

2. const val EXTRA_TYPE = "type"

Di sini kita menggunakan dua konstanta bertipe data integer untuk


menentukan notif ID sebagai ID untuk menampilkan notifikasi kepada
pengguna.

 Kotlin
 Java

1. private const val ID_ONETIME = 100

2. private const val ID_REPEATING = 101


Selanjutnya, bagian penting pada metode setOneTimeAlarm() adalah:

 Kotlin
 Java

1. fun setOneTimeAlarm(context: Context, type: String, date: String, time: String,


message: String) {

2.

3. // Validasi inputan date dan time terlebih dahulu

4. if (isDateInvalid(date, DATE_FORMAT) || isDateInvalid(time, TIME_FORMAT))


return

5.

6. val alarmManager = [Link](Context.ALARM_SERVICE) as


AlarmManager

7. val intent = Intent(context, AlarmReceiver::[Link])

8. [Link](EXTRA_MESSAGE, message)

9. [Link](EXTRA_TYPE, type)

10.

11. Log.e("ONE TIME", "$date $time")

12. val dateArray = [Link]("-").toTypedArray()

13. val timeArray = [Link](":").toTypedArray()

14.

15. val calendar = [Link]()

16. [Link]([Link], [Link](dateArray[0]))

17. [Link]([Link], [Link](dateArray[1]) - 1)

18. [Link](Calendar.DAY_OF_MONTH, [Link](dateArray[2]))

19. [Link](Calendar.HOUR_OF_DAY, [Link](timeArray[0]))

20. [Link]([Link], [Link](timeArray[1]))

21. [Link]([Link], 0)

22.

23. val pendingIntent = [Link](context, ID_ONETIME, intent,


0)
24. [Link](AlarmManager.RTC_WAKEUP, [Link],
pendingIntent)

25.

26. [Link](context, "One time alarm set up",


Toast.LENGTH_SHORT).show()

27. }

Pada kode di atas kita membuat sebuah obyek untuk AlarmManager.


Kemudian kita menyiapkan sebuah Intent yang akan menjalankan
AlarmReceiver dan membawa data berupa alarm dan pesan.

 Kotlin
 Java

1. val dateArray = [Link]("-").toTypedArray()

2. val timeArray = [Link](":").toTypedArray()

3.

4. val calendar = [Link]()

5. [Link]([Link], [Link](dateArray[0]))

6. [Link]([Link], [Link](dateArray[1]) - 1)

7. [Link](Calendar.DAY_OF_MONTH, [Link](dateArray[2]))

8. [Link](Calendar.HOUR_OF_DAY, [Link](timeArray[0]))

9. [Link]([Link], [Link](timeArray[1]))

10. [Link]([Link], 0)

Pada kode di atas kita memecah data date dan time untuk mengambil
nilai tahun, bulan, hari, jam dan menit.

 Kotlin
 Java

1. [Link]([Link], [Link](dateArray[1]) - 1);

Mengapa kode di atas, data kita kurangi 1? Misal tanggal yang kita
masukkan adalah 2016-09-27. Jika kita pecah, maka kita akan
memperoleh nilai 2016 (tahun), 9 (bulan), dan 27 (hari).

Masalahnya adalah, nilai bulan ke 9 pada kelas Calendar bukanlah bulan


September. Ini karena indeksnya dimulai dari 0. Jadi, untuk memperoleh
bulan September, maka nilai 9 tadi harus kita kurangi 1.
Intent yang dibuat akan dieksekusi ketika waktu alarm sama dengan
waktu pada sistem Android. Di sini komponen PendingIntent akan
diberikan kepada BroadcastReceiver.

 Kotlin
 Java

1. val pendingIntent = [Link](context, ID_ONETIME, intent, 0)

2. [Link](AlarmManager.RTC_WAKEUP, [Link],
pendingIntent)

Yang membedakan satu alarm dengan alarm lain adalah pada ID.

 Kotlin
 Java

1. val pendingIntent = [Link](context, ID_ONETIME, intent, 0)

Jika kita merubah nilai waktu dan menjalankan ulang alarm dengan ID
yang sama, maka akan merubah yang sudah diset sebelumnya.

Maksud dari baris ini [Link](AlarmManager.RTC_WAKEUP,


[Link](), pendingIntent) adalah kita memasang alarm yang
dibuat dengan tipe RTC_WAKEUP. Tipe alarm ini dapat membangunkan
peranti (jika dalam posisi sleep) untuk menjalankan obyek PendingIntent.

Ketika kondisi sesuai, maka akan BroadcastReceiver akan


running dengan semua proses yang terdapat di dalam metode
onReceive().

 Kotlin
 Java

1. val type = [Link](EXTRA_TYPE)

2. val message = [Link](EXTRA_MESSAGE)

3. val title = if ([Link](TYPE_ONE_TIME, ignoreCase = true)) TYPE_ONE_TIME


else TYPE_REPEATING

4. val notifId = if ([Link](TYPE_ONE_TIME, ignoreCase = true)) ID_ONETIME else


ID_REPEATING

5.

6. showToast(context, title, message)

7.

8. if (message != null) {
9. showAlarmNotification(context, title, message, notifId)

10. }

Maka akan ada Toast yang akan tampil. Anda pun juga bisa memanggil
sebuah notifikasi sederhana agar tampil di panel notifikasi pengguna
dengan bunyi notifikasi umum dan getaran. Di sini metode
showAlarmNotification bekerja.

 Kotlin
 Java

1. private fun showAlarmNotification(context: Context, title: String, message: String,


notifId: Int) {

2.

3. val channelId = "Channel_1"

4. val channelName = "AlarmManager channel"

5.

6. val notificationManagerCompat =
[Link](Context.NOTIFICATION_SERVICE) as
NotificationManager

7. val alarmSound =
[Link](RingtoneManager.TYPE_NOTIFICATION)

8. val builder = [Link](context, channelId)

9. .setSmallIcon([Link].ic_access_time_black)

10. .setContentTitle(title)

11. .setContentText(message)

12. .setColor([Link](context, [Link]))

13. .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))

14. .setSound(alarmSound)

15.

16. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

17. val channel = NotificationChannel(channelId,

18. channelName,

19. NotificationManager.IMPORTANCE_DEFAULT)
20.

21. [Link](true)

22. [Link] = longArrayOf(1000, 1000, 1000, 1000, 1000)

23.

24. [Link](channelId)

25.

26. [Link](channel)

27. }

28.

29. val notification = [Link]()

30.

31. [Link](notifId, notification)

32.

33. }

Metode di atas merupakan sebuah metode untuk membuat dan


menampilkan notifikasi yang kompatibel dengan beragam API dari
Android. Metode ini memanfaatkan fasilitas NotificationCompat. Anda
akan lebih jauh memahami tentang Notifikasi ini pada modul berikutnya.
Untuk bagian OS Oreo ke atas, harus menggunakan NotificationChannel
untuk menampilkan Notifikasi.

Di titik ini kami harap Anda mendapatkan gambaran menyeluruh tentang


ide, konsep dan skenario yang diimplementasikan pada bagian
"menjalankan one-time-alarm manager."

Selanjutnya, kita akan membuat Repeating Alarm untuk melakukan tugas


yang berulang. Jadi ayo kita ngoding lagi.
Latihan Repeating AlarmManager

Kita lanjutkan ngoding untuk latihan alarm manager. Kali ini kita akan
membuat latihan sederhana tentang repeating-alarm.

1. Pada AlarmReceiver silakan buat metode setRepeatingAlarm() pada


baris berikut:

o Kotlin
o Java

1. fun setRepeatingAlarm(context: Context, type: String, time: String,


message: String) {

2.

3. if (isDateInvalid(time, TIME_FORMAT)) return

4.

5. val alarmManager =
[Link](Context.ALARM_SERVICE) as AlarmManager

6. val intent = Intent(context, AlarmReceiver::[Link])

7. [Link](EXTRA_MESSAGE, message)

8. val putExtra = [Link](EXTRA_TYPE, type)

9.

10. val timeArray = [Link](":".toRegex()).dropLastWhile { [Link]()


}.toTypedArray()

11.

12. val calendar = [Link]()

13. [Link](Calendar.HOUR_OF_DAY, [Link](timeArray[0]))

14. [Link]([Link], [Link](timeArray[1]))

15. [Link]([Link], 0)

16.

17. val pendingIntent = [Link](context,


ID_REPEATING, intent, 0)

18. [Link](AlarmManager.RTC_WAKEUP,
[Link], AlarmManager.INTERVAL_DAY, pendingIntent)

19.
20. [Link](context, "Repeating alarm set up",
Toast.LENGTH_SHORT).show()

21. }

Sehingga kode AlarmReceiver kita saat ini menjadi seperti ini:

 Kotlin
 Java

1. class AlarmReceiver : BroadcastReceiver() {

2.

3. companion object {

4. const val TYPE_ONE_TIME = "OneTimeAlarm"

5. const val TYPE_REPEATING = "RepeatingAlarm"

6. const val EXTRA_MESSAGE = "message"

7. const val EXTRA_TYPE = "type"

8.

9. // Siapkan 2 id untuk 2 macam alarm, onetime dan repeating

10. private const val ID_ONETIME = 100

11. private const val ID_REPEATING = 101

12.

13. private const val DATE_FORMAT = "yyyy-MM-dd"

14. private const val TIME_FORMAT = "HH:mm"

15.

16. }

17.

18. override fun onReceive(context: Context, intent: Intent) {

19. val type = [Link](EXTRA_TYPE)

20. val message = [Link](EXTRA_MESSAGE)

21.

22. val title = if ([Link](TYPE_ONE_TIME, ignoreCase = true))


TYPE_ONE_TIME else TYPE_REPEATING
23. val notifId = if ([Link](TYPE_ONE_TIME, ignoreCase = true))
ID_ONETIME else ID_REPEATING

24.

25. //Jika Anda ingin menampilkan dengan toast anda bisa menghilangkan
komentar pada baris dibawah ini.

26. // showToast(context, title, message)

27.

28. if (message != null) {

29. showAlarmNotification(context, title, message, notifId)

30. }

31. }

32.

33. // Gunakan metode ini untuk menampilkan toast

34.

35. private fun showToast(context: Context, title: String, message: String?) {

36. [Link](context, "$title : $message", Toast.LENGTH_LONG).show()

37. }

38.

39. // Gunakan metode ini untuk menampilkan notifikasi

40. private fun showAlarmNotification(context: Context, title: String, message:


String, notifId: Int) {

41.

42. val CHANNEL_ID = "Channel_1"

43. val CHANNEL_NAME = "AlarmManager channel"

44.

45. val notificationManagerCompat =


[Link](Context.NOTIFICATION_SERVICE) as
NotificationManager

46. val alarmSound =


[Link](RingtoneManager.TYPE_NOTIFICATION)

47. val builder = [Link](context, CHANNEL_ID)


48. .setSmallIcon([Link].ic_access_time_black)

49. .setContentTitle(title)

50. .setContentText(message)

51. .setColor([Link](context,
[Link]))

52. .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))

53. .setSound(alarmSound)

54.

55. /*

56. Untuk android Oreo ke atas perlu menambahkan notification channel

57. Materi ini akan dibahas lebih lanjut di modul extended

58. */

59. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

60.

61. /* Create or update. */

62. val channel = NotificationChannel(CHANNEL_ID,

63. CHANNEL_NAME,

64. NotificationManager.IMPORTANCE_DEFAULT)

65.

66. [Link](true)

67. [Link] = longArrayOf(1000, 1000, 1000, 1000, 1000)

68.

69. [Link](CHANNEL_ID)

70.

71. [Link](channel)

72. }

73.

74. val notification = [Link]()


75.

76. [Link](notifId, notification)

77.

78. }

79.

80. // Metode ini digunakan untuk menjalankan alarm one time

81.

82. fun setOneTimeAlarm(context: Context, type: String, date: String, time: String,
message: String) {

83.

84. // Validasi inputan date dan time terlebih dahulu

85. if (isDateInvalid(date, DATE_FORMAT) || isDateInvalid(time, TIME_FORMAT))


return

86.

87. val alarmManager = [Link](Context.ALARM_SERVICE) as


AlarmManager

88. val intent = Intent(context, AlarmReceiver::[Link])

89. [Link](EXTRA_MESSAGE, message)

90. [Link](EXTRA_TYPE, type)

91.

92. Log.e("ONE TIME", "$date $time")

93. val dateArray = [Link]("-").toTypedArray()

94. val timeArray = [Link](":").toTypedArray()

95.

96. val calendar = [Link]()

97. [Link]([Link], [Link](dateArray[0]))

98. [Link]([Link], [Link](dateArray[1]) - 1)

99. [Link](Calendar.DAY_OF_MONTH, [Link](dateArray[2]))

100. [Link](Calendar.HOUR_OF_DAY, [Link](timeArray[0]))


101. [Link]([Link], [Link](timeArray[1]))

102. [Link]([Link], 0)

103.

104. val pendingIntent = [Link](context, ID_ONETIME,


intent, 0)

105. [Link](AlarmManager.RTC_WAKEUP, [Link],


pendingIntent)

106.

107. [Link](context, "One time alarm set up",


Toast.LENGTH_SHORT).show()

108. }

109.

110. // Metode ini digunakan untuk menjalankan alarm repeating

111. fun setRepeatingAlarm(context: Context, type: String, time: String, message:


String) {

112.

113. // Validasi inputan waktu terlebih dahulu

114. if (isDateInvalid(time, TIME_FORMAT)) return

115.

116. val alarmManager = [Link](Context.ALARM_SERVICE)


as AlarmManager

117. val intent = Intent(context, AlarmReceiver::[Link])

118. [Link](EXTRA_MESSAGE, message)

119. val putExtra = [Link](EXTRA_TYPE, type)

120.

121. val timeArray = [Link](":".toRegex()).dropLastWhile { [Link]()


}.toTypedArray()

122.

123. val calendar = [Link]()

124. [Link](Calendar.HOUR_OF_DAY, [Link](timeArray[0]))

125. [Link]([Link], [Link](timeArray[1]))


126. [Link]([Link], 0)

127.

128. val pendingIntent = [Link](context, ID_REPEATING,


intent, 0)

129. [Link](AlarmManager.RTC_WAKEUP,
[Link], AlarmManager.INTERVAL_DAY, pendingIntent)

130.

131. [Link](context, "Repeating alarm set up",


Toast.LENGTH_SHORT).show()

132. }

133.

134. // Gunakan metode ini untuk mengecek apakah alarm tersebut sudah
terdaftar di alarm manager

135. fun isAlarmSet(context: Context, type: String): Boolean {

136. val intent = Intent(context, AlarmReceiver::[Link])

137. val requestCode = if ([Link](TYPE_ONE_TIME, ignoreCase = true))


ID_ONETIME else ID_REPEATING

138.

139. return [Link](context, requestCode, intent,


PendingIntent.FLAG_NO_CREATE) != null

140. }

141.

142. // Metode ini digunakan untuk validasi date dan time

143. private fun isDateInvalid(date: String, format: String): Boolean {

144. return try {

145. val df = SimpleDateFormat(format, [Link]())

146. [Link] = false

147. [Link](date)

148. false

149. } catch (e: ParseException) {

150. true
151. }

152. }

153. }

Selanjutnya di activity_main.xml kita tambahkan kode berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:orientation="vertical"

8. android:padding="16dp"

9. tools:context=".MainActivity">

10.

11. ...

12.

13. <TextView

14. android:layout_width="match_parent"

15. android:layout_height="wrap_content"

16. android:layout_marginBottom="16dp"

17. android:text="@string/repeating_alarm"

18. android:textSize="16sp"

19. android:textStyle="bold" />

20.

21. <LinearLayout

22. android:layout_width="match_parent"

23. android:layout_height="wrap_content"
24. android:gravity="center_vertical"

25. android:orientation="horizontal">

26.

27. <ImageButton

28. android:id="@+id/btn_repeating_time"

29. android:layout_width="50dp"

30. android:layout_height="wrap_content"

31. android:src="@drawable/ic_access_time_black" />

32.

33. <TextView

34. android:id="@+id/tv_repeating_time"

35. android:layout_width="wrap_content"

36. android:layout_height="wrap_content"

37. android:text="@string/alarm_time" />

38. </LinearLayout>

39.

40. <EditText

41. android:id="@+id/edt_repeating_message"

42. android:layout_width="match_parent"

43. android:layout_height="wrap_content"

44. android:layout_marginBottom="16dp"

45. android:hint="@string/alarm_message"

46. android:inputType="text" />

47.

48. <LinearLayout

49. android:layout_width="match_parent"

50. android:layout_height="wrap_content"
51. android:gravity="center"

52. android:orientation="horizontal">

53.

54. <Button

55. android:id="@+id/btn_set_repeating_alarm"

56. android:layout_width="wrap_content"

57. android:layout_height="wrap_content"

58. android:layout_weight="0.5"

59. android:text="@string/set_repeating_alarm" />

60. </LinearLayout>

61. </LinearLayout>

Maka activity_main.xml menjadi seperti ini:

1.  <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:orientation="vertical"

8. android:padding="16dp"

9. tools:context=".MainActivity">

10.

11. <TextView

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:layout_marginBottom="16dp"
15. android:text="@string/one_time_alarm"

16. android:textSize="16sp"

17. android:textStyle="bold" />

18.

19. <LinearLayout

20. android:layout_width="match_parent"

21. android:layout_height="wrap_content"

22. android:gravity="center_vertical"

23. android:orientation="horizontal">

24.

25. <ImageButton

26. android:id="@+id/btn_once_date"

27. style="@style/[Link]"

28. android:layout_width="50dp"

29. android:layout_height="wrap_content"

30. android:src="@drawable/ic_date_range_black" />

31.

32. <TextView

33. android:id="@+id/tv_once_date"

34. android:layout_width="wrap_content"

35. android:layout_height="wrap_content"

36. android:text="@string/alarm_date" />

37. </LinearLayout>

38.

39. <LinearLayout

40. android:layout_width="match_parent"

41. android:layout_height="wrap_content"
42. android:gravity="center_vertical"

43. android:orientation="horizontal">

44.

45. <ImageButton

46. android:id="@+id/btn_once_time"

47. android:layout_width="50dp"

48. android:layout_height="wrap_content"

49. android:src="@drawable/ic_access_time_black" />

50.

51. <TextView

52. android:id="@+id/tv_once_time"

53. android:layout_width="wrap_content"

54. android:layout_height="wrap_content"

55. android:text="@string/alarm_time" />

56. </LinearLayout>

57.

58. <EditText

59. android:id="@+id/edt_once_message"

60. android:layout_width="match_parent"

61. android:layout_height="wrap_content"

62. android:layout_marginBottom="16dp"

63. android:hint="@string/alarm_message"

64. android:inputType="text" />

65.

66. <Button

67. android:id="@+id/btn_set_once_alarm"

68. android:layout_width="match_parent"
69. android:layout_height="wrap_content"

70. android:layout_marginBottom="16dp"

71. android:text="@string/set_one_time_alarm" />

72.

73. <TextView

74. android:layout_width="match_parent"

75. android:layout_height="wrap_content"

76. android:layout_marginBottom="16dp"

77. android:text="@string/repeating_alarm"

78. android:textSize="16sp"

79. android:textStyle="bold" />

80.

81. <LinearLayout

82. android:layout_width="match_parent"

83. android:layout_height="wrap_content"

84. android:gravity="center_vertical"

85. android:orientation="horizontal">

86.

87. <ImageButton

88. android:id="@+id/btn_repeating_time"

89. android:layout_width="50dp"

90. android:layout_height="wrap_content"

91. android:src="@drawable/ic_access_time_black" />

92.

93. <TextView

94. android:id="@+id/tv_repeating_time"

95. android:layout_width="wrap_content"
96. android:layout_height="wrap_content"

97. android:text="@string/alarm_time" />

98. </LinearLayout>

99.

100. <EditText

101. android:id="@+id/edt_repeating_message"

102. android:layout_width="match_parent"

103. android:layout_height="wrap_content"

104. android:layout_marginBottom="16dp"

105. android:hint="@string/alarm_message"

106. android:inputType="text" />

107.

108. <LinearLayout

109. android:layout_width="match_parent"

110. android:layout_height="wrap_content"

111. android:gravity="center"

112. android:orientation="horizontal">

113.

114. <Button

115. android:id="@+id/btn_set_repeating_alarm"

116. android:layout_width="wrap_content"

117. android:layout_height="wrap_content"

118. android:layout_weight="0.5"

119. android:text="@string/set_repeating_alarm" />

120. </LinearLayout>

121. </LinearLayout>
Selanjutnya, setelah kita menambahkan beberapa View ke dalam
activity_main.xml, kita masukkan juga kode untuk memberikan aksi pada
komponen tersebut di dalam MainActivity.

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link],


[Link], [Link]
{

2.

3. ...

4.

5. override fun onCreate(savedInstanceState: Bundle?) {

6. [Link](savedInstanceState)

7.

8. ...

9.

10. // Listener repeating alarm

11. binding?.btnRepeatingTime?.setOnClickListener(this)

12. binding?.btnSetRepeatingAlarm?.setOnClickListener(this)

13.

14. alarmReceiver = AlarmReceiver()

15. }

16.

17. override fun onClick(v: View) {

18. when ([Link]) {

19.

20. ...

21.

22. [Link].btn_repeating_time -> {


23.

24. }

25. [Link].btn_set_repeating_alarm -> {

26.

27. }

28. }

29. }

30.

31. ...

32. }

Selanjutnya adalah kita tambahkan kode metode onClick(), menjadi seperti ini:

 Kotlin
 Java

1. override fun onClick(v: View) {

2. when ([Link]) {

3.

4. ...

5.

6. [Link].btn_repeating_time -> {

7. val timePickerFragmentRepeat = TimePickerFragment()

8. [Link](supportFragmentManager,
TIME_PICKER_REPEAT_TAG)

9. }

10. [Link].btn_set_repeating_alarm -> {

11. val repeatTime = binding?.tvRepeatingTime?.[Link]()

12. val repeatMessage =


binding?.edtRepeatingMessage?.[Link]()

13. [Link](this,
AlarmReceiver.TYPE_REPEATING,
14. repeatTime, repeatMessage)

15. }

16. }

17. }

Tambahkan juga kode repeating pada onDialogTimeSet:

 Kotlin
 Java

1. override fun onDialogTimeSet(tag: String?, hourOfDay: Int, minute: Int) {

2. ...

3.

4. when (tag) {

5. TIME_PICKER_ONCE_TAG -> binding?.tvOnceTime?.text =


[Link]([Link])

6. TIME_PICKER_REPEAT_TAG -> binding?.tvRepeatingTime?.text =


[Link]([Link])

7. else -> {

8. }

9. }

10. }

Sehingga kode MainActivity menjadi seperti berikut:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link],


[Link],
[Link] {

2.

3. private var binding: ActivityMainBinding? = null

4. private lateinit var alarmReceiver: AlarmReceiver

5.

6. companion object {
7. private const val DATE_PICKER_TAG = "DatePicker"

8. private const val TIME_PICKER_ONCE_TAG = "TimePickerOnce"

9. private const val TIME_PICKER_REPEAT_TAG = "TimePickerRepeat"

10. }

11.

12. override fun onCreate(savedInstanceState: Bundle?) {

13. [Link](savedInstanceState)

14.

15. binding = [Link](layoutInflater)

16. setContentView(binding?.root)

17.

18. // Listener one time alarm

19. binding?.btnOnceDate?.setOnClickListener(this)

20. binding?.btnOnceTime?.setOnClickListener(this)

21. binding?.btnSetOnceAlarm?.setOnClickListener(this)

22.

23. // Listener repeating alarm

24. binding?.btnRepeatingTime?.setOnClickListener(this)

25. binding?.btnSetRepeatingAlarm?.setOnClickListener(this)

26.

27. alarmReceiver = AlarmReceiver()

28. }

29.

30. override fun onClick(v: View) {

31. when ([Link]) {

32. [Link].btn_once_date -> {

33. val datePickerFragment = DatePickerFragment()


34. [Link](supportFragmentManager,
DATE_PICKER_TAG)

35. }

36. [Link].btn_once_time -> {

37. val timePickerFragmentOne = TimePickerFragment()

38. [Link](supportFragmentManager,
TIME_PICKER_ONCE_TAG)

39. }

40. [Link].btn_set_once_alarm -> {

41. val onceDate = binding?.tvOnceDate?.[Link]()

42. val onceTime = binding?.tvOnceTime?.[Link]()

43. val onceMessage = binding?.edtOnceMessage?.[Link]()

44.

45. [Link](this,
AlarmReceiver.TYPE_ONE_TIME,

46. onceDate,

47. onceTime,

48. onceMessage)

49. }

50. [Link].btn_repeating_time -> {

51. val timePickerFragmentRepeat = TimePickerFragment()

52. [Link](supportFragmentManager,
TIME_PICKER_REPEAT_TAG)

53. }

54. [Link].btn_set_repeating_alarm -> {

55. val repeatTime = binding?.tvRepeatingTime?.[Link]()

56. val repeatMessage =


binding?.edtRepeatingMessage?.[Link]()

57. [Link](this,
AlarmReceiver.TYPE_REPEATING,

58. repeatTime, repeatMessage)


59. }

60. }

61. }

62.

63. override fun onDialogDateSet(tag: String?, year: Int, month: Int,


dayOfMonth: Int) {

64.

65. // Siapkan date formatter-nya terlebih dahulu

66. val calendar = [Link]()

67. [Link](year, month, dayOfMonth)

68. val dateFormat = SimpleDateFormat("yyyy-MM-dd",


[Link]())

69.

70. // Set text dari textview once

71. binding?.tvOnceDate?.text = [Link]([Link])

72. }

73.

74. override fun onDialogTimeSet(tag: String?, hourOfDay: Int, minute: Int)


{

75.

76. // Siapkan time formatter-nya terlebih dahulu

77. val calendar = [Link]()

78. [Link](Calendar.HOUR_OF_DAY, hourOfDay)

79. [Link]([Link], minute)

80.

81. val dateFormat = SimpleDateFormat("HH:mm", [Link]())

82.

83. // Set text dari textview berdasarkan tag

84. when (tag) {


85. TIME_PICKER_ONCE_TAG -> binding?.tvOnceTime?.text =
[Link]([Link])

86. TIME_PICKER_REPEAT_TAG -> binding?.tvRepeatingTime?.text =


[Link]([Link])

87. else -> {

88. }

89. }

90. }

91.

92. override fun onDestroy() {

93. [Link]()

94.

95. binding = null

96. }

97. }
6. Setelah selesai, silakan jalankan aplikasi dan pilih waktu yang diinginkan
untuk menampilkan notifikasi harian seperti berikut:
7. Tidak sulit, kan? Saat ini anda sudah bisa membuat sebuah alarm
berulang yang berikutnya bisa Anda gunakan untuk scheduling
task, seperti melakukan sinkronisasi ke server. Thumbs Up!

Bedah Kode

Tidak ada perubahan yang signifikan ketika kita membuat metode


setRepeatingAlarm() dengan method setOneTimeAlarm(). Yang
membedakan adalah pada pemanggilan metode terkait. Pada
setOneTimeAlarm() kita menggunakan metode set() sedangkan pada
setRepeatingAlarm() kita menggunakan setInexactRepeating() .

setInexactRepeating

 Kotlin
 Java

1. fun setRepeatingAlarm(context: Context, type: String, time: String, message:


String) {

2. if (isDateInvalid(time, TIME_FORMAT)) return

3.

4. val alarmManager = [Link](Context.ALARM_SERVICE) as


AlarmManager
5. val intent = Intent(context, AlarmReceiver::[Link])

6. [Link](EXTRA_MESSAGE, message)

7. val putExtra = [Link](EXTRA_TYPE, type)

8.

9. val timeArray = [Link](":".toRegex()).dropLastWhile { [Link]()


}.toTypedArray()

10.

11. val calendar = [Link]()

12. [Link](Calendar.HOUR_OF_DAY, [Link](timeArray[0]))

13. [Link]([Link], [Link](timeArray[1]))

14. [Link]([Link], 0)

15.

16. val pendingIntent = [Link](context, ID_REPEATING,


intent, 0)

17. [Link](AlarmManager.RTC_WAKEUP,
[Link], AlarmManager.INTERVAL_DAY, pendingIntent)

18.

19. [Link](context, "Repeating alarm set up",


Toast.LENGTH_SHORT).show()

20. }

Secara umum proses yang disusun pada metode di atas adalah sama.
Yang membedakan hanya ide dan skenarionya. Di sini kita akan
mendemonstrasikan sebuah proses yang berulang dengan satuan waktu
yang ditentukan dengan memanfaatkan fitur AlarmManager.

 Kotlin
 Java

1. [Link](AlarmManager.RTC_WAKEUP,
[Link], AlarmManager.INTERVAL_DAY, pendingIntent)

Baris di atas akan menjalankan obyek PendingIntent pada setiap waktu


yang ditentukan dalam millissecond dengan interval per hari. Penggunaan
metode setInexactRepeating() adalah pilihan lebih tepat karena Android
akan menjalankan alarm ini secara bersamaan dengan alarm lain.
Meskipun waktu antara tiap alarm tersebut tidak sama persis. Ini penting
agar baterai peranti user jadi lebih hemat dan tidak cepat habis. Ingat!
Penggunaan alarm manager yang tidak tepat akan memakan daya baterai
yang cukup besar.
Latihan Membatalkan AlarmManager

Pada modul sebelumnya, kami belum menjelaskan bagaimana


membatalkan sebuah alarm manager yang sudah dipasang. Modul ini
akan mencoba membedah mekanisme tersebut.

1. Tambahkan kode berikut ke dalam AlarmReceiver:

o Kotlin
o Java

1. fun cancelAlarm(context: Context, type: String) {

2. val alarmManager =
[Link](Context.ALARM_SERVICE) as
AlarmManager

3. val intent = Intent(context, AlarmReceiver::[Link])

4. val requestCode = if ([Link](TYPE_ONE_TIME,


ignoreCase = true)) ID_ONETIME else ID_REPEATING

5. val pendingIntent = [Link](context,


requestCode, intent, 0)

6. [Link]()

7.

8. [Link](pendingIntent)

9.

10. [Link](context, "Repeating alarm dibatalkan",


Toast.LENGTH_SHORT).show()

11. }

Maka AlarmReceiver menjadi seperti berikut:

 Kotlin
 Java

1. class AlarmReceiver : BroadcastReceiver() {

2.

3. companion object {

4. const val TYPE_ONE_TIME = "OneTimeAlarm"


5. const val TYPE_REPEATING = "RepeatingAlarm"

6. const val EXTRA_MESSAGE = "message"

7. const val EXTRA_TYPE = "type"

8.

9. // Siapkan 2 id untuk 2 macam alarm, onetime dan repeating

10. private const val ID_ONETIME = 100

11. private const val ID_REPEATING = 101

12.

13. private const val DATE_FORMAT = "yyyy-MM-dd"

14. private const val TIME_FORMAT = "HH:mm"

15.

16. }

17.

18. override fun onReceive(context: Context, intent: Intent) {

19. val type = [Link](EXTRA_TYPE)

20. val message = [Link](EXTRA_MESSAGE)

21.

22. val title = if ([Link](TYPE_ONE_TIME, ignoreCase = true))


TYPE_ONE_TIME else TYPE_REPEATING

23. val notifId = if ([Link](TYPE_ONE_TIME, ignoreCase = true))


ID_ONETIME else ID_REPEATING

24.

25. showToast(context, title, message)

26.

27. if (message != null) {

28. showAlarmNotification(context, title, message, notifId)


29. }

30. }

31.

32. private fun showToast(context: Context, title: String, message: String?)


{

33. [Link](context, "$title : $message",


Toast.LENGTH_LONG).show()

34. }

35.

36. fun showAlarmNotification(context: Context, title: String,


private
message: String, notifId: Int) {

37.

38. val CHANNEL_ID = "Channel_1"

39. val CHANNEL_NAME = "AlarmManager channel"

40.

41. val notificationManagerCompat =


[Link](Context.NOTIFICATION_SERVICE) as
NotificationManager

42. val alarmSound =


[Link](RingtoneManager.TYPE_NOTIFICATION)

43. val builder = [Link](context, CHANNEL_ID)

44. .setSmallIcon([Link].ic_access_time_black)

45. .setContentTitle(title)

46. .setContentText(message)

47. .setColor([Link](context,
[Link]))

48. .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))

49. .setSound(alarmSound)

50.
51. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

52.

53. val channel = NotificationChannel(CHANNEL_ID,

54. CHANNEL_NAME,

55. NotificationManager.IMPORTANCE_DEFAULT)

56.

57. [Link](true)

58. [Link] = longArrayOf(1000, 1000, 1000, 1000,


1000)

59.

60. [Link](CHANNEL_ID)

61.

62.
[Link](channel)

63. }

64.

65. val notification = [Link]()

66.

67. [Link](notifId, notification)

68.

69. }

70.

71. fun setOneTimeAlarm(context: Context, type: String, date: String,


time: String, message: String) {

72.

73. if (isDateInvalid(date, DATE_FORMAT) || isDateInvalid(time,


TIME_FORMAT)) return
74.

75. val alarmManager =


[Link](Context.ALARM_SERVICE) as AlarmManager

76. val intent = Intent(context, AlarmReceiver::[Link])

77. [Link](EXTRA_MESSAGE, message)

78. [Link](EXTRA_TYPE, type)

79.

80. Log.e("ONE TIME", "$date $time")

81. val dateArray = [Link]("-").toTypedArray()

82. val timeArray = [Link](":").toTypedArray()

83.

84. val calendar = [Link]()

85. [Link]([Link], [Link](dateArray[0]))

86. [Link]([Link], [Link](dateArray[1]) - 1)

87. [Link](Calendar.DAY_OF_MONTH,
[Link](dateArray[2]))

88. [Link](Calendar.HOUR_OF_DAY,
[Link](timeArray[0]))

89. [Link]([Link], [Link](timeArray[1]))

90. [Link]([Link], 0)

91.

92. val pendingIntent = [Link](context,


ID_ONETIME, intent, 0)

93. [Link](AlarmManager.RTC_WAKEUP,
[Link], pendingIntent)

94.

95. [Link](context, "One time alarm set up",


Toast.LENGTH_SHORT).show()
96. }

97.

98. fun setRepeatingAlarm(context: Context, type: String, time: String,


message: String) {

99.

100. if (isDateInvalid(time, TIME_FORMAT)) return

101.

102. val alarmManager =


[Link](Context.ALARM_SERVICE) as AlarmManager

103. val intent = Intent(context, AlarmReceiver::[Link])

104. [Link](EXTRA_MESSAGE, message)

105. val putExtra = [Link](EXTRA_TYPE, type)

106.

107. val timeArray = [Link](":".toRegex()).dropLastWhile


{ [Link]() }.toTypedArray()

108.

109. val calendar = [Link]()

110. [Link](Calendar.HOUR_OF_DAY,
[Link](timeArray[0]))

111. [Link]([Link], [Link](timeArray[1]))

112. [Link]([Link], 0)

113.

114. val pendingIntent = [Link](context,


ID_REPEATING, intent, 0)

115.
[Link](AlarmManager.RTC_WAKEUP,
[Link], AlarmManager.INTERVAL_DAY, pendingIntent)

116.
117. [Link](context, "Repeating alarm set up",
Toast.LENGTH_SHORT).show()

118. }

119.

120.

121. fun cancelAlarm(context: Context, type: String) {

122. val alarmManager =


[Link](Context.ALARM_SERVICE) as AlarmManager

123. val intent = Intent(context, AlarmReceiver::[Link])

124. val requestCode = if ([Link](TYPE_ONE_TIME, ignoreCase


= true)) ID_ONETIME else ID_REPEATING

125. val pendingIntent = [Link](context,


requestCode, intent, 0)

126. [Link]()

127.

128. [Link](pendingIntent)

129.

130. [Link](context, "Repeating alarm dibatalkan",


Toast.LENGTH_SHORT).show()

131. }

132.

133. private fun isDateInvalid(date: String, format: String): Boolean {

134. return try {

135. val df = SimpleDateFormat(format, [Link]())

136. [Link] = false

137. [Link](date)

138. false
139. } catch (e: ParseException) {

140. true

141. }

142. }

143. }

Berkas activity_main.xml sekarang seharusnya berisi kode berikut yang sudah


ditambahkan dari modul sebelumnya:

1. <Button

2. android:id="@+id/btn_cancel_repeating_alarm"

3. android:layout_width="wrap_content"

4. android:layout_height="wrap_content"

5. android:layout_weight="0.5"

6. android:text="@string/cancel_alarm" />

Isi activity_main.xml secara keseluruhan menjadi seperti berikut ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/activity_main"

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:orientation="vertical"

8. android:padding="16dp"

9. tools:context="[Link]">

10.

11. <TextView
12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:layout_marginBottom="16dp"

15. android:text="@string/one_time_alarm"

16. android:textSize="16sp"

17. android:textStyle="bold" />

18.

19. <LinearLayout

20. android:layout_width="match_parent"

21. android:layout_height="wrap_content"

22. android:gravity="center_vertical"

23. android:orientation="horizontal">

24.

25. <ImageButton

26. android:id="@+id/btn_once_date"

27. style="@style/[Link]"

28. android:layout_width="50dp"

29. android:layout_height="wrap_content"

30. android:src="@drawable/ic_date_range_black" />

31.

32. <TextView

33. android:id="@+id/tv_once_date"

34. android:layout_width="wrap_content"

35. android:layout_height="wrap_content"

36. android:text="@string/alarm_date" />


37. </LinearLayout>

38.

39. <LinearLayout

40. android:layout_width="match_parent"

41. android:layout_height="wrap_content"

42. android:gravity="center_vertical"

43. android:orientation="horizontal">

44.

45. <ImageButton

46. android:id="@+id/btn_once_time"

47. android:layout_width="50dp"

48. android:layout_height="wrap_content"

49. android:src="@drawable/ic_access_time_black" />

50.

51. <TextView

52. android:id="@+id/tv_once_time"

53. android:layout_width="wrap_content"

54. android:layout_height="wrap_content"

55. android:text="@string/alarm_time" />

56. </LinearLayout>

57.

58. <EditText

59. android:id="@+id/edt_once_message"

60. android:layout_width="match_parent"

61. android:layout_height="wrap_content"
62. android:layout_marginBottom="16dp"

63. android:hint="@string/alarm_message"

64. android:inputType="text" />

65.

66. <Button

67. android:id="@+id/btn_set_once_alarm"

68. android:layout_width="match_parent"

69. android:layout_height="wrap_content"

70. android:layout_marginBottom="16dp"

71. android:text="@string/set_one_time_alarm" />

72.

73. <TextView

74. android:layout_width="match_parent"

75. android:layout_height="wrap_content"

76. android:layout_marginBottom="16dp"

77. android:text="@string/repeating_alarm"

78. android:textSize="16sp"

79. android:textStyle="bold" />

80.

81. <LinearLayout

82. android:layout_width="match_parent"

83. android:layout_height="wrap_content"

84. android:gravity="center_vertical"

85. android:orientation="horizontal">

86.
87. <ImageButton

88. android:id="@+id/btn_repeating_time"

89. android:layout_width="50dp"

90. android:layout_height="wrap_content"

91. android:src="@drawable/ic_access_time_black" />

92.

93. <TextView

94. android:id="@+id/tv_repeating_time"

95. android:layout_width="wrap_content"

96. android:layout_height="wrap_content"

97. android:text="@string/alarm_time" />

98. </LinearLayout>

99.

100. <EditText

101. android:id="@+id/edt_repeating_message"

102. android:layout_width="match_parent"

103. android:layout_height="wrap_content"

104. android:layout_marginBottom="16dp"

105. android:hint="@string/alarm_message"

106. android:inputType="text" />

107.

108. <LinearLayout

109. android:layout_width="match_parent"

110. android:layout_height="wrap_content"

111. android:gravity="center"
112. android:orientation="horizontal">

113.

114. <Button

115. android:id="@+id/btn_cancel_repeating_alarm"

116. android:layout_width="wrap_content"

117. android:layout_height="wrap_content"

118. android:layout_weight="0.5"

119. android:text="@string/cancel_alarm" />

120.

121. <Button

122. android:id="@+id/btn_set_repeating_alarm"

123. android:layout_width="wrap_content"

124. android:layout_height="wrap_content"

125. android:layout_weight="0.5"

126. android:text="@string/set_repeating_alarm" />

127. </LinearLayout>

128. </LinearLayout>

Selanjutnya pada MainActivity tambahkan aksi pada komponen button:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link],


[Link], [Link]
{

2.

3. ...

4.
5. override fun onCreate(savedInstanceState: Bundle?) {

6. [Link](savedInstanceState)

7.

8. ...

9.

10.
binding?.btnCancelRepeatingAlarm?.setOnClickListener(this)

11.

12. alarmReceiver = AlarmReceiver()

13. }

14.

15. override fun onClick(v: View) {

16. when ([Link]) {

17.

18. ...

19.

20. [Link].btn_cancel_repeating_alarm ->

21. }

22. }

23.

24. ...

25. }

Selanjutnya kita tambahkan kode seleksi baru pada metode onClick() yang akan
menjalankan proses pembatalan repeating alarm ketika tombol Batalkan
Alarm diklik.

 Kotlin
 Java
1. override fun onClick(v: View) {

2. when ([Link]) {

3.

4. ...

5.

6. [Link].btn_cancel_repeating_alarm ->
[Link](this,
AlarmReceiver.TYPE_REPEATING)

7. }

8. }

Sehingga kode MainActivity kita secara keseluruhan menjadi seperti berikut:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), [Link],


[Link],
[Link] {

2.

3. private var binding: ActivityMainBinding? = null

4. private lateinit var alarmReceiver: AlarmReceiver

5.

6. companion object {

7. private const val DATE_PICKER_TAG = "DatePicker"

8. private const val TIME_PICKER_ONCE_TAG = "TimePickerOnce"

9. private const val TIME_PICKER_REPEAT_TAG =


"TimePickerRepeat"

10. }

11.

12. override fun onCreate(savedInstanceState: Bundle?) {


13. [Link](savedInstanceState)

14.

15. binding = [Link](layoutInflater)

16. setContentView(binding?.root)

17.

18. // Listener one time alarm

19. binding?.btnOnceDate?.setOnClickListener(this)

20. binding?.btnOnceTime?.setOnClickListener(this)

21. binding?.btnSetOnceAlarm?.setOnClickListener(this)

22.

23. // Listener repeating alarm

24. binding?.btnRepeatingTime?.setOnClickListener(this)

25. binding?.btnSetRepeatingAlarm?.setOnClickListener(this)

26.
binding?.btnCancelRepeatingAlarm?.setOnClickListener(this)

27.

28. alarmReceiver = AlarmReceiver()

29. }

30.

31. override fun onClick(v: View) {

32. when ([Link]) {

33. [Link].btn_once_date -> {

34. val datePickerFragment = DatePickerFragment()

35.
[Link](supportFragmentManager,
DATE_PICKER_TAG)

36. }
37. [Link].btn_once_time -> {

38. val timePickerFragmentOne = TimePickerFragment()

39.
[Link](supportFragmentManager,
TIME_PICKER_ONCE_TAG)

40. }

41. [Link].btn_set_once_alarm -> {

42. val onceDate = binding?.tvOnceDate?.[Link]()

43. val onceTime = binding?.tvOnceTime?.[Link]()

44. val onceMessage =


binding?.edtOnceMessage?.[Link]()

45.

46. [Link](this,
AlarmReceiver.TYPE_ONE_TIME,

47. onceDate,

48. onceTime ,

49. onceMessage )

50. }

51. [Link].btn_repeating_time -> {

52. val timePickerFragmentRepeat = TimePickerFragment()

53.
[Link](supportFragmentManager,
TIME_PICKER_REPEAT_TAG)

54. }

55. [Link].btn_set_repeating_alarm -> {

56. val repeatTime =


binding?.tvRepeatingTime?.[Link]()

57. val repeatMessage =


binding?.edtRepeatingMessage?.[Link]()
58. [Link](this,
AlarmReceiver.TYPE_REPEATING,

59. repeatTime, repeatMessage)

60. }

61. [Link].btn_cancel_repeating_alarm ->


[Link](this, AlarmReceiver.TYPE_REPEATING)

62. }

63. }

64.

65. override fun onDialogDateSet(tag: String?, year: Int, month: Int,


dayOfMonth: Int) {

66.

67. // Siapkan date formatter-nya terlebih dahulu

68. val calendar = [Link]()

69. [Link](year, month, dayOfMonth)

70. val dateFormat = SimpleDateFormat("yyyy-MM-dd",


[Link]())

71.

72. // Set text dari textview once

73. binding?.tvOnceDate?.text =
[Link]([Link])

74. }

75.

76. override fun onDialogTimeSet(tag: String?, hourOfDay: Int,


minute: Int) {

77.

78. // Siapkan time formatter-nya terlebih dahulu

79. val calendar = [Link]()

80. [Link](Calendar.HOUR_OF_DAY, hourOfDay)


81. [Link]([Link], minute)

82.

83. val dateFormat = SimpleDateFormat("HH:mm",


[Link]())

84.

85. // Set text dari textview berdasarkan tag

86. when (tag) {

87. TIME_PICKER_ONCE_TAG -> binding?.tvOnceTime?.text


= [Link]([Link])

88. TIME_PICKER_REPEAT_TAG ->


binding?.tvRepeatingTime?.text =
[Link]([Link])

89. else -> {

90. }

91. }

92. }

93.

94. override fun onDestroy() {

95. [Link]()

96.

97. binding = null

98. }

99. }
4. Ketika aplikasi dijalankan, maka tampilannya kurang lebih menjadi seperti
ini:
Selesai! Sekarang mari kita uji kodenya. Skenarionya seperti berikut:

1. Ubah nilai pada variabel repeatingTime di MainActivity sesuai dengan


waktu yang mendekati saat ini (bedakan sekitar 5 menit dari sekarang).
Sebagai contoh, saat ini waktu dipasang pada pukul 01.45
2. Klik tombol SET REPEATING ALARM untuk menjalankan alarm untuk
waktu yang ditentukan.
3. Klik tombol BATALKAN ALARM untuk membatalkan alarm yang baru saja
dijalankan.
4. Jika sukses seharusnya pada waktu yang diberikan tidak tampil notifikasi
alarm seperti sebelumnya.

Bedah Kode
CancelAlarm

 Kotlin
 Java

1. fun cancelAlarm(context: Context, type: String) {

2. val alarmManager =
[Link](Context.ALARM_SERVICE) as AlarmManager

3. val intent = Intent(context, AlarmReceiver::[Link])


4. val requestCode = if ([Link](TYPE_ONE_TIME, ignoreCase =
true)) ID_ONETIME else ID_REPEATING

5. val pendingIntent = [Link](context,


requestCode, intent, 0)

6. [Link]()

7.

8. [Link](pendingIntent)

9.

10. [Link](context, "Repeating alarm dibatalkan",


Toast.LENGTH_SHORT).show()

11. }

Karena tipe alarm yang dikirimkan adalah TYPE_REPEATING, maka id yang


digunakan adalah NOTIF_ID_REPEATING yang bernilai 101. Setelah
memperoleh id tersebut, maka kita dapat mengetahui intent-nya. Dan
kemudian kita dapat membatalkan alarmnya.

Dan itulah esensi membatalkan alarm yang telah dipasang.

Di materi selanjutnya kita akan berkenalan dengan yang lebih seru lagi,
jobschedule untuk melakukan proses scheduling task/proses berulang
dengan cara yang lebih efisien dari sisi pemanfaatan sumber daya.

Untuk keseluruhan kode proyek ini bisa kamu unduh di:

 Source Code Latihan Alarm Manager


Teori Koneksi ke Web API & Parsing JSON

Tahukah Anda bahwa salah satu skill yang dibutuhkan seorang Android
Developer adalah memahami JSON API dan Networking. Mengapa
demikian? Tentunya karena kebanyakan aplikasi membutuhkan data yang
berasal dari server. Misalnya Anda akan membuat aplikasi cuaca, maka
tentu Anda perlu terhubung ke server untuk mendapatkan data cuaca
yang terbaru.

Nah, hal-hal yang perlu Anda perhatikan saat ingin mengambil data
secara online yaitu:

 Apa itu Web API?


 Apa itu JSON?
 Bagaimana cara menampilkannya ke dalam aplikasi?

Tak perlu bingung. Kita akan membahas ini satu per satu secara
mendetail. Tetap semangat!

Web API

Saat kita membutuhkan data cuaca terbaru, ada dua cara yang bisa Anda
lakukan. Pertama, membuat sensor cuaca dan menyimpannya ke
server sendiri. Kedua, Anda bisa memanfaatkan pihak ketiga yang sudah
menyediakan data cuaca. Nah, layanan data ini biasanya disebut dengan
Web API atau Web Service.

Web API (Application Programming Interface) adalah suatu layanan yang


mengizinkan dua aplikasi saling terhubung melalui jaringan, sehingga kita
dapat mengambil dan mengirim data ke layanan tersebut dengan mudah.

Kelebihan dari Web API adalah membuat proses development menjadi


lebih efisien. Mengapa? Karena cukup dengan sekali membuat API, Anda
dapat mengkonsumsinya di berbagai macam platform, seperti mobile,
desktop maupun web. Sebagai catatan, konsumsi API adalah istilah yang
sering dipakai untuk ketika client mengambil data dari Web API.
Yang menarik yaitu ada Web API yang bersifat public atau sering
disebut Public API. Karena sifatnya yang public, siapa pun pun bisa
memakainya. Tentu hal ini akan memudahkan Anda dalam
mengembangkan aplikasi. Beberapa kumpulan public API dapat Anda lihat
di sini:

 Any API
 Public APIs by ToddMotto
 Daftar API Lokal Indonesia by Farizdotid

Jika dilihat satu per satu Web API pada tautan di atas, semuanya hampir
memiliki pola yang sama. Hal ini karena terdapat gaya atau aturan
standar dalam perancangan Web API, salah satunya yaitu REST
(REpresentation State Transfer). Arsitektur ini terkenal karena lebih simpel
dan mudah dipelajari daripada arsitektur yang lainnya. Data yang
diberikan pun bisa berupa format text, JSON atau XML.

REST ini hampir selalu menggunakan HTTP (Hypertext Transfer Protocol)


untuk berkomunikasi data. HTTP adalah protokol yang memungkinkan
Anda saling bertukar data antar server dan client. Pada protokol ini,
terdapat beberapa request method yang biasa digunakan untuk proses
CRUD (Create Read Update Delete), di antaranya adalah:

 POST : Untuk membuat data (Create)


 GET : Untuk membaca data (Read)
 PUT : Untuk mengubah data (Update)
 DELETE : Untuk menghapus data (Delete)

Berikut ini adalah contoh URL yang merupakan API dan parameter-
parameternya:
[Link]

 Path : “users”
 Query 1 : “page” bernilai “1”
 Query 2 : “per_page” bernilai “10”
 Gunakan “?” sebagai separator sebelum parameter pertama
 Gunakan “&” sebagai separator untuk parameter selanjutnya
 Gunakan “=” untuk mengisi query dengan suatu nilai/value

Untuk memahami lebih lanjut tentang API, Anda dapat melihat video pada
tautan berikut:

 What is an API?

Akses Web API melalui Postman

Nah, sekarang kita akan mengakses salah satu contoh dummy API yang
ada di [Link]. Kemudian untuk uji coba proses CRUD, Anda dapat
memanfaatkan Postman yang dapat diunduh di [Link].
Postman adalah GUI API Caller yang dapat memasukkan parameter atau
body dengan mudah pada API melalui form tanpa perlu kode. Mungkin
Anda bisa mencoba API yang menggunakan method GET langsung melalui
browser, namun untuk method lainnya seperti POST, Anda akan
kesusahan. Tak mau kan? Di sinilah Postman hadir sebagai solusi.

Setelah menginstal Postman, buat request baru dengan method GET dan
masukkan URL berikut: [Link] Supaya lebih
mudah dibaca, ubah pula pengaturan pada response menjadi Pretty dan
formatnya menjadi JSON. Selanjutnya klik tombol Send.

Jika koneksi berhasil, maka Anda akan mendapatkan respon seperti ini:

1. {

2. "page": 1,

3. "per_page": 6,

4. "total": 12,

5. "total_pages": 2,

6. "data": [

7. {

8. "id": 1,

9. "email": "[Link]@[Link]",

10. "first_name": "George",

11. "last_name": "Bluth",


12. "avatar":
"[Link]

13. },

14. {

15. "id": 2,

16. "email": "[Link]@[Link]",

17. "first_name": "Janet",

18. "last_name": "Weaver",

19. "avatar":
"[Link]

20. },

21. {

22. "id": 3,

23. "email": "[Link]@[Link]",

24. "first_name": "Emma",

25. "last_name": "Wong",

26. "avatar":
"[Link]

27. },

28. {

29. "id": 4,

30. "email": "[Link]@[Link]",

31. "first_name": "Eve",

32. "last_name": "Holt",

33. "avatar":
"[Link]

34. },

35. {

36. "id": 5,
37. "email": "[Link]@[Link]",

38. "first_name": "Charles",

39. "last_name": "Morris",

40. "avatar":
"[Link]

41. },

42. {

43. "id": 6,

44. "email": "[Link]@[Link]",

45. "first_name": "Tracey",

46. "last_name": "Ramos",

47. "avatar":
"[Link]

48. }

49. ]

50. }

Dengan mengakses URL tersebut, Anda akan mendapatkan daftar biodata dalam
format JSON. Selanjutnya coba masukkan URL
berikut: [Link] Anda akan mendapatkan response biodata
dengan id 2.

1. {

2. "data": {

3. "id": 2,

4. "email": "[Link]@[Link]",

5. "first_name": "Janet",

6. "last_name": "Weaver",

7. "avatar":
"[Link]

8. }

9. }
Lalu bagaimana jika Anda ingin menambahkan data? Gunakan
method POST dan masukkan parameter di bagian Body → x-www-form-
urlencoded. Isikan key sesuai dengan parameter yang ada di
documentation. Jika berhasil Anda akan mendapatkan response seperti
ini:

JSON

Sebelumnya Anda sudah mencoba mengakses API dan mendapatkan


response JSON. Lalu sebenarnya apa JSON itu? JSON (Javascript Object
Notation) merupakan format pertukaran & penyimpanan data yang ringan
dan mudah dibaca (dibanding XML). Format ini dapat digunakan di hampir
semua bahasa pemrograman. Karena itulah kami sangat menyarankan
para developer memahami cara berinteraksi dengan format ini.

Sebagai contoh Anda memiliki database dengan tabel seperti berikut:

Maka format JSON dari tabel tersebut adalah seperti ini:


Dalam JSON, ada dua hal penting yang perlu diperhatikan, yaitu
JSONArray dan JSONObject. JSONArray direpresentasikan dengan tanda [
] (kurung siku). Jenis ini berisi kumpulan JSONObject dengan tanda
koma sebagai pemisahnya. Sedangkan JSONObject direpresentasikan
dengan tanda { } (kurung kurawal).

Pada contoh di atas, “data” merupakan JSONArray yang berisi dua


JSONObject. Object ke-0 berisi kumpulan data dengan id 1 dan Object ke-1
berisi kumpulan data dengan id 2.

Struktur dari JSON menggunakan format key-value untuk menampilkan


field data. Seperti contoh di atas, "page" adalah key dan "1" adalah
value. Berikut adalah cara mengetahui tipe data yang terdapat pada
value:

 String : teks (dibungkus dengan “ “)


 Integer : angka bulat (misal: 0, 3, 40)
 Double : angka desimal (misal 0.2, 3.14, 40.0)
 Boolean : true/false
 Array : [“value1”, “value2”]
 Object : { “key” : “value” }
 Null : null

Parsing JSON

Nah, sekarang Anda akan mempelajari cara mem-parsing JSON. Yang


dimaksud dengan parsing yaitu membaca dan mengubahnya sesuai
dengan bahasa pemrograman yang kita pakai. Contohnya yaitu seperti
berikut ini:

 JSON Field
Untuk method yang digunakan menyesuaikan dengan tipe data
yang diambil. Misalnya Anda ingin mengambil data “page” yang
bernilai 1, maka untuk mengambilnya Anda dapat menggunakan
kode:

o Kotlin
o Java

1. val jsonObject = JSONObject(response)

2. val page = [Link]("page")

Catatan :
Pastikan parameternya sama persis dengan key yang ada di Web API-nya.

JSON Array
Untuk mengambil data yang bertanda [ ] (kurung siku), Anda dapat
menggunakan getJSONArray.

 Kotlin
 Java

1.  val jsonObject = JSONObject(response)

2. val dataArray = [Link]("data")

JSON Object
Untuk mengambil data yang bertanda { } (kurung kurawal), Anda dapat
menggunakan getJSONObject(index).

Berikut adalah contoh mengambil data email pada Object pertama (ke-0):

 Kotlin
 Java

1. val jsonObject = JSONObject(response);

2. val dataArray = [Link]("data")

3. val dataObject = [Link](0)

4. val email = [Link]("email")

Jadi, untuk ntuk mengambil data first_name, Anda perlu mengambil JSON
Array "data" dan JSONObject-nya terlebih dulu.

Langkah di atas juga dapat disingkat menjadi seperti ini:

 Kotlin
 Java

1. val jsonObject = JSONObject(response);

2. val dataArray = [Link]("data")

3. val email = [Link](0).getString("email")

Catatan :
Jika diperhatikan JSON di atas diawali dan diakhiri dengan { }, sehingga
ada JSONObject di tiap awal kode.

Untuk lebih membuat kamu paham tentang JSON Anda bisa mempelajari
lebih lanjut pada URL ini:

 JSON
 JSON Parsing
 Dicoding Live : Journey to Parsing JSON in Android

Library untuk Networking

Untuk memudahkan proses transaksi data dengan web service, kita dapat
menggunakan beragam HTTP library yang tersedia seperti LoopJ, Retrofit,
dan Volley. Pada kesempatan ini kita akan menggunakan
library AsyncHttpClient (LoopJ).

Kita menggunakan gradle untuk mengintegrasikan AsyncHttpClient ke


aplikasi. Caranya dengan menambahkan satu baris gradle script pada
bagian dependency yang ada di [Link](Module: app):

1. dependencies {

2. ...

3. implementation '[Link]:android-async-http:1.4.9'

4. }

Kemudian kita cukup memilih sync now untuk mengunduh


library tersebut.

Selanjutnya bagaimana cara memakainya? Misalnya Anda mau


mengambil data dari [Link] maka Anda perlu
memakai method GET seperti berikut:

 Kotlin
 Java
1. val client = AsyncHttpClient()

2. val url = "[Link]

3. [Link](url, object : AsyncHttpResponseHandler() {

4. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

5. // called when response HTTP status is "200 OK"

6. }

7.

8. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {

9. // called when response HTTP status is "4XX" (eg. 401, 403, 404)

10. }

11. })

Di atas kita menggunakan AsyncHttpClient, berarti kita akan


menggunakan client yang bertanggung jawab untuk koneksi data dan
sifatnya adalah asynchronous. Jika tidak dilakukan secara asynchronous,
Anda akan mengalami eror networkOnMainThreadException.

Callback onSuccess() dipanggil ketika response memberikan kode status


200, yang artinya koneksi berhasil. Di dalamnya Anda dapat melakukan
proses parsing JSON dan menampilkan data ke aplikasi. Sementara itu,
callback onFailure() dipanggil ketika response memberikan kode status
4xx (seperti 401, 403, 404, dll), yang artinya koneksi gagal.

Berikut adalah penjelasan arti dari status kode yang sering ditemui:

Status
Arti Penjelasan
Kode
200 OK Request berhasil dipenuhi oleh server.
Bad Server tidak mengerti request yang dikirimkan
400
Request client, misal karena kesalahan syntax.
Unauthoriz
401 Request membutuhkan authorization.
ed
Server mengerti request dari client tapi menolak
memprosesnya. Hal ini terjadi karena
403 Forbidden
resource tersebut memang tidak boleh diakses
secara permanen atau dibatasi oleh waktu.
404 Not Found Resource yang diminta tidak ditemukan.
500 Server Server mengalami kesalahan yang tidak diketahui
Error saat melakukan pemrosesan terhadap request yang
Status
Arti Penjelasan
Kode
dikirimkan oleh client.

Lalu bagaimana jika Anda ingin mengirim data dengan parameter ke


server? Anda bisa menggunakan method post() dan menambahkan
RequestParams untuk memasukkan parameter seperti berikut:

 Kotlin
 Java

1. val client = AsyncHttpClient()

2. val params = RequestParams()

3. [Link]("name", "arif faizin")

4. [Link]("job", "academy content writer")

5. val url = "[Link]

6. [Link](url, params, object : AsyncHttpResponseHandler() {

7. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

8. // called when response HTTP status is "200 OK"

9. }

10.

11. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {

12. // called when response HTTP status is "4XX" (eg. 401, 403, 404)

13. }

14. })

Selanjutnya ini adalah contoh penggunaan method put() untuk update:

 Kotlin
 Java

1. val client = AsyncHttpClient()

2. val params = RequestParams()


3. [Link]("name", "arif faizin")

4. [Link]("job", "academy content writer")

5. val url = "[Link]

6. [Link](url, object : AsyncHttpResponseHandler() {

7. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

8. // called when response HTTP status is "200 OK"

9. }

10.

11. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {

12. // called when response HTTP status is "4XX" (eg. 401, 403, 404)

13. }

14. })

Dan ini adalah contoh penggunaan method delete() untuk menghapus


data:

 Kotlin
 Java

1. val client = AsyncHttpClient()

2. val params = RequestParams()

3. [Link]("id", "21");

4. val url = "[Link]

5. [Link](url, object : AsyncHttpResponseHandler() {

6. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

7. // called when response HTTP status is "200 OK"

8. }

9.

10. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {
11. // called when response HTTP status is "4XX" (eg. 401, 403, 404)

12. }

13. })

Kalau dilihat dari semua kode di atas, maka pembedanya hanyalah di


bagian method-nya.

Selamat! Anda telah mempelajari dasar-dasar untuk mengambil dan


mengirim data ke server. Mulai dari memahami Web API, JSON dan cara
parsing-nya, serta library yang digunakan untuk networking di Android.
Pada modul selanjutnya kita akan latihan bersama membuat aplikasi yang
dapat menampilkan data dari API. Tetap semangat ya!
Latihan Networking dengan LoopJ & Parsing JSON
Tujuan

Pada codelab kali ini Anda akan belajar menerapkan library LoopJ untuk
mengambil data dari Web API. Target Anda adalah menghasilkan
output seperti di bawah ini:

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama MyQuote.


2. Menambahkan library ke dalam project.
3. Mengatur tampilan pada berkas layout XML.
4. Membuat fungsi getRandomQuote untuk mengambil data dari API.
5. Mengimplementasikan fungsi getRandomQuote ke dalam Activity.
6. Menjalankan aplikasi.

Codelab Web API di Android

1. Buat project baru di Android Studio dengan kriteria sebagai berikut:

Nama Project MyQuote


Target & Minimum Target SDK Phone and Tablet, Api level 21
Tipe Activity Empty Activity
Activity Name MainActivity
Use AndroidX Artifacts True
Language Kotlin/Java

2. Di awal telah dijelaskan bahwa Anda akan membuat sebuah aplikasi


yang akan menampilkan random quote saat ini dengan library
LoopJ. Di sini Anda akan menggunakan data API dari [Link]
[Link] (Alternatif). API yang akan kita gunakan yaitu:
o Random Quote : [Link]
o List Quotes : [Link]
3. Cobalah buka URL tersebut di browser. Maka hasilnya seperti ini:

1. {"_id":"5a9b22c72bad9600044b700b","en":"Reliable computations are obtainable


from buggy programs, which after all, are the only kind of programs there
are.","author":"Daniel T. Barry","id":"5a9b22c72bad9600044b700b"}

Supaya terlihat rapi, Anda bisa menambahkan extension JSON Formatter


pada Chrome sehingga tampilannya akan mudah dibaca seperti ini:

1. {

2. "_id": "5a9ab0372bad9600044b6fbf",

3. "en": "When I invented the web, I didn't have to ask anyone's


permission.",

4. "author": "Tim Berners-Lee",

5. "id": "5a9ab0372bad9600044b6fbf"

6. }

3. Selanjutnya Anda akan menggunakan library AsyncHttpClient


(LoopJ) yang terdapat pada website [Link]. Untuk
menambahkannya, buka berkas [Link](Module: app).
Kemudian tambahkan baris berikut:

1. dependencies {

2. ...

3. implementation '[Link]:android-async-http:1.4.9'

4. }

Lalu klik pilihan Sync Now di pojok kanan atas Android Studio
seperti ini:
Ketika proses synchronize selesai, maka library telah berhasil
dipasang.

4. Okay, pertama kita mulai dengan membuat desain aplikasinya


terlebih dahulu. Buka activity_main.xml dan buat layout
menggunakan Constraint Layout seperti ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <[Link] xmlns:android="http://
[Link]/apk/res/android"

3. xmlns:app="[Link]

4. xmlns:tools="[Link]

5. android:id="@+id/activity_main"

6. android:layout_width="match_parent"

7. android:layout_height="match_parent"

8. android:padding="16dp"

9. android:orientation="vertical"

10. tools:context="[Link]">

11.

12. <TextView

13. android:id="@+id/tvQuote"

14. android:layout_width="match_parent"

15. android:layout_height="wrap_content"

16. android:layout_margin="16dp"

17. android:textAlignment="center"

18. android:textSize="18sp"

19. android:textStyle="bold"
20. app:layout_constraintBottom_toBottomOf="parent"

21. app:layout_constraintEnd_toEndOf="parent"

22. app:layout_constraintStart_toStartOf="parent"

23. app:layout_constraintTop_toTopOf="parent"

24. tools:text="@string/quote" />

25.

26. <TextView

27. android:id="@+id/tvAuthor"

28. android:layout_width="match_parent"

29. android:layout_height="wrap_content"

30. android:layout_margin="16dp"

31. android:textAlignment="center"

32. android:textSize="14sp"

33. android:textStyle="italic"

34. app:layout_constraintTop_toBottomOf="@+id/tvQuote"

35. tools:layout_editor_absoluteX="16dp"

36. tools:text="@string/author" />

37.

38. <ProgressBar

39. android:id="@+id/progressBar"

40. style="?android:attr/progressBarStyle"

41. android:layout_width="wrap_content"

42. android:layout_height="wrap_content"

43. app:layout_constraintBottom_toBottomOf="parent"

44. app:layout_constraintEnd_toEndOf="parent"

45. app:layout_constraintStart_toStartOf="parent"

46. app:layout_constraintTop_toTopOf="parent" />


47.

48. <Button

49. android:id="@+id/btnAllQuotes"

50. android:layout_width="wrap_content"

51. android:layout_height="wrap_content"

52. android:layout_margin="16dp"

53. android:text="@string/show_list_quotes"

54. app:layout_constraintBottom_toBottomOf="parent"

55. app:layout_constraintEnd_toEndOf="parent"

56. app:layout_constraintStart_toStartOf="parent" />

57.

58. </[Link]>

Tambahkan juga resource di [Link] seperti berikut:

1. <resources>

2. <string name="app_name">My Quote</string>

3. <string name="quote">When do you think people die? When


they are shot through the heart by the bullet of a pistol? No.
When they are ravaged by an incurable disease? No. When they
drink a soup made from a poisonous mushroom!? No! It’s when…
they are FORGOTTEN.</string>

4. <string name="author">- Dr. Hiluluk</string>

5. <string name="show_list_quotes">Show me list of


quotes</string>

6. </resources>

Sehingga desain dari aplikasi utamanya akan menjadi seperti ini:


5. Selanjutnya buka MainActivity dan buat method dengan nama
getRandomQuote().

o Kotlin
o Java

1. class MainActivity : AppCompatActivity() {

2.

3. companion object {

4. private val TAG = MainActivity::[Link]

5. }

6.

7. private lateinit var binding: ActivityMainBinding

8.

9. override fun onCreate(savedInstanceState: Bundle?) {

10. [Link](savedInstanceState)

11. binding = [Link](layoutInflater)

12. setContentView([Link])
13.

14. getRandomQuote()

15. }

16.

17. private fun getRandomQuote() {

18.

19. }

20. }

Jangan lupa untuk mengaktifkan ViewBinding terlebih dahulu pada


[Link](module: app) dengan menambahkan kode berikut:

1. android {

2. ...

3. buildFeatures {

4. viewBinding = true

5. }

6. }

6. Selanjutnya kita mulai untuk mengimplementasikan LoopJ untuk


mengambil data dari Web API seperti berikut:

o Kotlin
o Java

1. private fun getRandomQuote() {

2. [Link] = [Link]

3. val client = AsyncHttpClient()

4. val url = "[Link]

5. [Link](url, object : AsyncHttpResponseHandler() {

6. override fun onSuccess(statusCode: Int, headers:


Array<Header>, responseBody: ByteArray) {

7. // Jika koneksi berhasil

8. }
9.

10. override fun onFailure(statusCode: Int, headers:


Array<Header>, responseBody: ByteArray, error: Throwable) {

11. // Jika koneksi gagal

12. }

13. })

14. }

Di sini terdapat ProgressBar yang ditampilkan untuk


mengindikasikan proses loading data. Loading ini akan hilang ketika
data sudah ditampilkan.

7. Kemudian lihat kembali API untuk mengambil random quote.

Di sini terdapat 1 JSONObject yang berisi 4 data. Namun hanya 2


data yang diperlukan, yaitu data dengan key “en” dan “author”.
Berikut ini adalah cara untuk mem-parsing data tersebut di dalam
method onSuccess:

o Kotlin
o Java

1. private fun getRandomQuote() {

2. [Link] = [Link]

3. val client = AsyncHttpClient()

4. val url = "[Link]

5. [Link](url, object : AsyncHttpResponseHandler() {

6. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

7. // Jika koneksi berhasil

8. [Link] = [Link]
9.

10. val result = String(responseBody)

11. Log.d(TAG, result)

12. try {

13. val responseObject = JSONObject(result)

14.

15. val quote = [Link]("en")

16. val author = [Link]("author")

17.

18. [Link] = quote

19. [Link] = author

20.

21. } catch (e: Exception) {

22. [Link](this@MainActivity, [Link],


Toast.LENGTH_SHORT).show()

23. [Link]()

24. }

25.

26. }

27.

28. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {

29. // Jika koneksi gagal

30. [Link] = [Link]

31.

32. val errorMessage = when (statusCode) {

33. 401 -> "$statusCode : Bad Request"

34. 403 -> "$statusCode : Forbidden"


35. 404 -> "$statusCode : Not Found"

36. else -> "$statusCode : ${[Link]}"

37. }

38. [Link](this@MainActivity, errorMessage,


Toast.LENGTH_SHORT).show()

39. }

40. })

41. }

Data kemudian ditampilkan di TextView jika berhasil. Jika tidak, akan


muncul Toast dan memberitahukan eror apa yang terjadi.

8. Terakhir, karena Anda menggunakan koneksi internet, Anda perlu


menambahkan permission di AndroidManifest seperti berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. xmlns:tools="[Link]

4. package="[Link]">

5.

6. <uses-permission
android:name="[Link]" />

7.

8. <application

9. ...

10. </application>

11. </manifest>

9. Setelah semuanya selesai, jalankan aplikasi yang Anda buat. Maka


hasilnya akan menjadi seperti ini:
10. Selamat! Anda sudah menerapkan LoopJ untuk mengakses
Web API yang berbentuk satu JSONObject. Lalu, bagaimana jika
datanya berbentuk JSONArray yang berisi banyak data? Kita
lanjutkan di langkah berikutnya. Semangat!
11. Buatlah Activity baru dengan cara klik kanan pada nama
package → new → activity → empty activity. Kemudian beri
nama ListQuotesActivity.
12. Buka activit_list_quotes.xml, kemudian tambahkan
RecyclerView dan ProgressBar di dalamnya seperti ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <[Link]
xmlns:android="[Link]

3. xmlns:app="[Link]

4. xmlns:tools="[Link]

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. tools:context=".ListQuotesActivity">

8.

9. <[Link]

10. android:id="@+id/listQuotes"

11. android:layout_width="match_parent"

12. android:layout_height="match_parent"

13. app:layout_constraintBottom_toTopOf="parent"

14. app:layout_constraintEnd_toEndOf="parent"

15. app:layout_constraintStart_toStartOf="parent"

16. app:layout_constraintTop_toTopOf="parent" />

17.

18. <ProgressBar

19. android:id="@+id/progressBar"

20. style="?android:attr/progressBarStyle"

21. android:layout_width="wrap_content"

22. android:layout_height="wrap_content"

23. app:layout_constraintBottom_toBottomOf="parent"

24. app:layout_constraintEnd_toEndOf="parent"

25. app:layout_constraintStart_toStartOf="parent"
26. app:layout_constraintTop_toTopOf="parent" />

27. </[Link]>

12. Sebelum melangkah lebih jauh, buatlah terlebih dahulu layout


untuk item RecyclerView dengan cara klik kanan pada folder
layout → New → Layout resource file dan beri nama
item_quote. Lalu buat desain seperti berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <TextView xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/tvItem"

5. android:layout_width="match_parent"

6. android:layout_height="wrap_content"

7. android:gravity="center_vertical"

8. android:minHeight="?android:attr/listPreferredItemHeightSmall"

9. android:paddingStart="?android:attr/listPreferredItemPaddingStart"

10. android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"

11. android:textAppearance="?android:attr/textAppearanceListItemSmall"

12. tools:text="Quote" />

Kemudian buat class baru untuk adapter RecyclerView dengan nama


QuoteAdapter dan tuliskan kode berikut:

 Kotlin
 Java

1.  class QuoteAdapter(private val listReview: ArrayList<String>) :


[Link]<[Link]>() {

2. override fun onCreateViewHolder(viewGroup: ViewGroup, viewType:


Int): ViewHolder {

3. val view =
[Link]([Link]).inflate([Link].item_quote,
viewGroup, false)

4. return ViewHolder(view)
5. }

6.

7. override fun onBindViewHolder(viewHolder: ViewHolder, position: Int)


{

8. [Link] = listReview[position]

9. }

10.

11. override fun getItemCount(): Int {

12. return [Link]

13. }

14.

15. class ViewHolder(view: View) : [Link](view) {

16. val tvItem: TextView = [Link]([Link])

17. }

18. }

Kemudian buka ListQuotesActivity dan buat method getListQuotes


untuk mengambil data list quotes.

 Kotlin
 Java

1. class ListQuotesActivity : AppCompatActivity() {

2.

3. companion object {

4. private val TAG = ListQuotesActivity::[Link]

5. }

6.

7. private lateinit var binding: ActivityListQuotesBinding

8.
9. override fun onCreate(savedInstanceState: Bundle?) {

10. [Link](savedInstanceState)

11. binding = [Link](layoutInflater)

12. setContentView([Link])

13.

14. supportActionBar?.title = "List of Quotes"

15.

16. val layoutManager = LinearLayoutManager(this)

17. [Link](layoutManager)

18. val itemDecoration = DividerItemDecoration(this,


[Link])

19. [Link](itemDecoration)

20.

21. getListQuotes()

22. }

23.

24. private fun getListQuotes() {

25. [Link] = [Link]

26. val client = AsyncHttpClient()

27. val url = "[Link]

28. [Link](url, object : AsyncHttpResponseHandler() {

29. override fun onSuccess(statusCode: Int, headers:


Array<Header>, responseBody: ByteArray) {

30. // Jika koneksi berhasil

31. [Link] = [Link]

32. }

33.

34. override fun onFailure(statusCode: Int, headers:


Array<Header>, responseBody: ByteArray, error: Throwable) {
35. // Jika koneksi gagal

36. [Link] = [Link]

37. val errorMessage = when (statusCode) {

38. 401 -> "$statusCode : Bad Request"

39. 403 -> "$statusCode : Forbidden"

40. 404 -> "$statusCode : Not Found"

41. else -> "$statusCode : ${[Link]}"

42. }

43. [Link](this@ListQuotesActivity, errorMessage,


Toast.LENGTH_SHORT).show()

44. }

45. })

46. }

47. }

13. Sebelum menulis kode yang ada di onSuccess, lihat kembali


API untuk mengambil list quotes.

Di sini kita lihat terdapat JSONArray yang berisi banyak JSON Object.
Maka Anda perlu melakukan perulangan untuk mendapatkan data
tersebut dengan cara seperti ini:
o Kotlin
o Java

1. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

2. // Jika koneksi berhasil

3. [Link] = [Link]

4.

5. val listQuote = ArrayList<String>()

6.

7. val result = String(responseBody)

8. Log.d(TAG, result)

9. try {

10. val jsonArray = JSONArray(result)

11.

12. for (i in 0 until [Link]()) {

13. val jsonObject = [Link](i)

14. val quote = [Link]("en")

15. val author = [Link]("author")

16. [Link]("\n$quote\n — $author\n")

17. }

18.

19. val adapter = QuoteAdapter(listQuote)

20. [Link] = adapter

21. } catch (e: Exception) {

22. [Link](this@ListQuotesActivity, [Link],


Toast.LENGTH_SHORT).show()

23. [Link]()

24. }

25.
26. }

14. Sekarang ListQuoteActivity sudah siap diakses. Tambahkan


Button di activity_main.xml untuk navigasi ke halaman list quotes.

1. <?xml version="1.0" encoding="utf-8"?>

2. <[Link]
xmlns:android="[Link]

3. xmlns:app="[Link]

4. xmlns:tools="[Link]

5. android:id="@+id/activity_main"

6. android:layout_width="match_parent"

7. android:layout_height="match_parent"

8. android:padding="16dp"

9. android:orientation="vertical"

10. tools:context="[Link]">

11.

12. <TextView

13. android:id="@+id/tvQuote"

14. android:layout_width="match_parent"

15. android:layout_height="wrap_content"

16. android:layout_margin="16dp"

17. android:textAlignment="center"

18. android:textSize="18sp"

19. android:textStyle="bold"

20. app:layout_constraintBottom_toBottomOf="parent"

21. app:layout_constraintEnd_toEndOf="parent"

22. app:layout_constraintStart_toStartOf="parent"

23. app:layout_constraintTop_toTopOf="parent"

24. tools:text="@string/quote" />


25.

26. <TextView

27. android:id="@+id/tvAuthor"

28. android:layout_width="match_parent"

29. android:layout_height="wrap_content"

30. android:layout_margin="16dp"

31. android:textAlignment="center"

32. android:textSize="14sp"

33. android:textStyle="italic"

34. app:layout_constraintTop_toBottomOf="@+id/tvQuote"

35. tools:layout_editor_absoluteX="16dp"

36. tools:text="@string/author" />

37.

38. <ProgressBar

39. android:id="@+id/progressBar"

40. style="?android:attr/progressBarStyle"

41. android:layout_width="wrap_content"

42. android:layout_height="wrap_content"

43. app:layout_constraintBottom_toBottomOf="parent"

44. app:layout_constraintEnd_toEndOf="parent"

45. app:layout_constraintStart_toStartOf="parent"

46. app:layout_constraintTop_toTopOf="parent" />

47.

48. <Button

49. android:id="@+id/btnAllQuotes"

50. android:layout_width="wrap_content"

51. android:layout_height="wrap_content"
52. android:layout_margin="16dp"

53. android:text="@string/show_list_quotes"

54. app:layout_constraintBottom_toBottomOf="parent"

55. app:layout_constraintEnd_toEndOf="parent"

56. app:layout_constraintStart_toStartOf="parent" />

57. </[Link]>

15. Kemudian pada MainActivity tambahkan aksi pada Button


tersebut seperti ini:

o Kotlin
o Java

1. class MainActivity : AppCompatActivity() {

2.

3. ...

4.

5. override fun onCreate(savedInstanceState: Bundle?) {

6. [Link](savedInstanceState)

7. setContentView([Link].activity_main)

8.

9. getRandomQuote()

10.

11. [Link] {

12. startActivity(Intent(this@MainActivity,
ListQuotesActivity::[Link]))

13. }

14. }

15. ...

16. }
16. Setelah semuanya selesai, jalankan kembali aplikasi yang
Anda buat. Maka hasilnya akan menjadi seperti ini:

Bedah Kode
LoopJ

Perhatikan kode berikut pada MainActivity:

 Kotlin
 Java

1. private fun getRandomQuote() {

2. [Link] = [Link]

3. val client = AsyncHttpClient()

4. val url = "[Link]

5. [Link](url, object : AsyncHttpResponseHandler() {

6. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {
7. // Jika koneksi berhasil

8. [Link] = [Link]

9.

10. ...

11. }

12.

13. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {

14. // Jika koneksi gagal

15. [Link] = [Link]

16.

17. ...

18. }

19. })

20. }

Untuk menggunakan library LoopJ, Anda cukup menggunakan


AsyncHttpClient untuk membuat koneksi ke server secara asynchronous.
Kemudian karena kita hanya mengambil data (READ), maka Anda
menggunakan kode [Link](). Ada 2 object yang dihasilkan oleh
AsyncHttpResponseHandler yaitu onSuccess dan onFailure. onSuccess
digunakan jika koneksi berhasil dan onFailure digunakan jika koneksi
gagal. ProgressBar digunakan untuk memberitahu proses yang sedang
berjalan, ketika data sudah tampil ProgressBar akan hilang.

Status Code

Lihatlah kode di dalam method onFailure pada LoopJ.

 Kotlin
 Java

1. val errorMessage = when (statusCode) {

2. 401 -> "$statusCode : Bad Request"

3. 403 -> "$statusCode : Forbidden"

4. 404 -> "$statusCode : Not Found"


5. else -> "$statusCode : ${[Link]}"

6. }

7. [Link](this@MainActivity, errorMessage,
Toast.LENGTH_SHORT).show()

Di sini Anda menggunakan percabangan untuk mengganti pesan yang


ditampilkan jika terjadi eror saat koneksi ke server. Jika status code
memberikan hasil 401, maka pesan yang tampil yaitu “Bad Request”.
Jika 403 tampil pesan “Forbidden”. Dan jika 404 tampil pesan “Not
Found”. Lalu jika selain kode di atas Anda dapat
menggunakan [Link]() untuk mendapatkan pesan eror.

Parsing JSON Quote Random

Selanjutnya perhatikan cara mem-parsing JSON dari [Link]


[Link]/random pada method onSuccess di MainActivity:

 Kotlin
 Java

1. val result = String(responseBody)

2. Log.d(TAG, result)

3. try {

4. val responseObject = JSONObject(result)

5.

6. val quote = [Link]("en")

7. val author = [Link]("author")

8.

9. [Link] = quote

10. [Link] = author

11.

12. } catch (e: Exception) {

13. [Link](this@MainActivity, [Link], Toast.LENGTH_SHORT).show()

14. [Link]()

15. }
Di sini Anda membuat Log terlebih dahulu untuk menampilkan
response di Logcat. Hal ini penting untuk mengecek masalah apabila data
tidak tampil. Jika data yang di Logcat ada tapi list tidak tampil, maka
kemungkinan kesalahan terjadi pada saat parsing JSON atau pada saat
menampilkan RecyclerView.

Selanjutnya lihat format JSON pada API berikut:

Karena dimulai dengan { }, artinya ia bertipe JSONObject. maka untuk


mengambil datanya Anda bisa menggunakan kode di bawah ini.

 Kotlin
 Java

1. val responseObject = JSONObject(result)

Selanjutnya untuk mengambil data quote dan author, sesuai dengan tipe
data dari value tersebut.

 Kotlin
 Java

1. val quote = [Link]("en")

2. val author = [Link]("author")

Pastikan juga parameter yang digunakan sama persis dengan key yang
ada di web API.

Parsing JSON List Quote

Selanjutnya perhatikan cara mem-parsing JSON dari [Link]


[Link]/list pada method onSuccess di ListQuotesActivity:

 Kotlin
 Java

1. override fun onSuccess(statusCode: Int, headers: Array<Header>, responseBody:


ByteArray) {

2. // Jika koneksi berhasil

3. [Link] = [Link]
4.

5. val listQuote = ArrayList<String>()

6.

7. val result = String(responseBody)

8. Log.d(TAG, result)

9. try {

10. val jsonArray = JSONArray(result)

11.

12. for (i in 0 until [Link]()) {

13. val jsonObject = [Link](i)

14. val quote = [Link]("en")

15. val author = [Link]("author")

16. [Link]("\n$quote\n — $author\n")

17. }

18.

19. val adapter = QuoteAdapter(listQuote)

20. [Link] = adapter

21. } catch (e: Exception) {

22. [Link](this@ListQuotesActivity, [Link],


Toast.LENGTH_SHORT).show()

23. [Link]()

24. }

25.

26. }

Sama seperti sebelumnya, lihat terlebih dahulu hasil JSON dari API-nya.
Karena dimulai dengan [ ], artinya ia bertipe JSONArray. Karena itu, untuk
mengambil datanya Anda menggunakan:

 Kotlin
 Java

1. val jsonArray = JSONArray(result)

Selanjutnya kita menggunakan perulangan untuk mendapatkan


JSONObject yang ada di dalamnya seperti ini:

 Kotlin
 Java

1. val listQuote = ArrayList<String>()

2.

3. for (i in 0 until [Link]()) {

4. val jsonObject = [Link](i)

5. val quote = [Link]("en")

6. val author = [Link]("author")

7. [Link]("\n$quote\n — $author\n")

8. }

Setelah mendapatkan data dalam bentuk ArrayList, maka Anda dapat


memasukkannya ke adapter RecyclerView seperti biasa seperti ini:

 Kotlin
 Java

1. val adapter = QuoteAdapter(listQuote)


2. [Link] = adapter

Selamat Anda sudah belajar tentang menggunakan LoopJ dan Parsing


JSON yang merupakan skill dasar seorang Android Developer. Pahami
baik-baik dan baca ulang jika kurang mengerti. Semangat!

Source code dapat Anda unduh di:

 Source Code Latihan LoopJ


Teori WorkManager

Sebelumnya Anda sudah mempelajari JobScheduler untuk melakukan


proses di background dengan serangkaian kondisi yang ditentukan.
Sayangnya, JobScheduler hanya dapat dijalankan di Android dengan
minimum API versi 21. Sedangkan untuk di bawahnya Anda harus
menggunakan FirebaseJobDispatcher, namun permasalahannya yaitu
komponen tersebut sudah deprecated. Tapi tunggu dulu, ada kabar
gembiranya! Terdapat komponen baru yang mengatasi permasalahan ini,
yaitu WorkManager.

Sekarang kita kenalan dulu yuk dengan komponen baru yang super keren
ini. WorkManager adalah sebuah komponen baru yang termasuk di dalam
paket Jetpack. Ia menjanjikan proses background yang pasti dieksekusi
(deferrable) walaupun aplikasi ditutup atau bahkan device di-restart. Dan
yang lebih penting lagi yaitu komponen ini sudah ramah baterai, sehingga
tidak menghabiskan banyak baterai.

Kelebihan WorkManager

Berikut adalah beberapa kelebihan dari WorkManager:

 Mendukung One-Time Task maupun Periodic Task.


 Bisa membuat chain (proses berkelanjutan) antar task.
 Mendukung sampai API Level 14.
 Tetap dapat berjalan walaupun tanpa Google Play Service.
 Berjalan dengan mulus dengan doze mode.
 Mendukung LiveData untuk melihat status Work.

Kapan menggunakan WorkManager

WorkManager cocok digunakan untuk aksi-aksi yang


bersifat deferrable (tetap berguna walaupun tidak langsung dilakukan),
seperti berikut ini:

 Mengirim log ke server.


 Mengirim media ke server.
 Sync database secara periodic dari server.
 Menerapkan filter ke image.

Berikut adalah flow untuk memilih komponen mana yang cocok


digunakan berdasarkan guide dari dokumentasi Android:
Jadi, jika aplikasi yang ingin Anda buat bersifat deferrable dan ada fitur
Sync, maka WorkManager merupakan pilihan yang paling tepat. Namun
jika proses tersebut terpantik(triggered) secara online, Anda bisa
menggunakan Firebase Cloud Messaging dan WorkManager. Namun jika
task tersebut harus segera dilakukan, maka Foreground Service cocok
untuk Anda gunakan. Dan jika task tersebut perlu dieksekusi pada waktu
yang tepat, maka AlarmManager adalah pilihan yang paling sesuai.

Komponen di WorkManager

Berikut ini adalah beberapa komponen yang Anda perlu ketahui sebelum
menggunakan WorkManager.

 Worker, adalah sebuah kelas dengan method doWork(). Di sinilah aksi


yang akan dieksekusi di background thread diletakkan.
 WorkRequest, ada dua jenis, yaitu OneTimeWorkRequest untuk sekali
eksekusi dan PeriodicWorkRequest untuk eksekusi yang dilakukan secara
periodik.
 Constraint, digunakan untuk mengatur kapan proses tersebut dieksekusi,
misal: ketika mendapatkan koneksi internet, ketika peranti sedang diisi
dayanya, atau ketika dalam kondisi idle.
 Data, digunakan untuk mengirim data ke Worker dengan format Key-
Value.
 WorkManager, adalah kelas yang menjalankan WorkRequest dan juga
bisa digunakan untuk mengatur task-chaining.
 Result, digunakan untuk melihat status worker, apakah masih menunggu,
gagal, atau berhasil.

Berikut adalah contoh kelas yang mengimplementasi Worker

 Kotlin
 Java

1. class MyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {


2. override fun doWork(): Result {

3. try{

4. //proses di background

5. return [Link]()

6. }catch(e: Exception){

7. return [Link]()

8. }

9. }

10. }

Kemudian untuk memanggil kelas Worker bisa dengan cara berikut:

 Kotlin
 Java

1. val myData= [Link]()

2. [Link](LETTER_ID, letterId)

3. [Link]()

4. val myConstraints = [Link]()

5. .setRequiredNetworkType([Link])

6. .build()

7. val myRequest = OneTimeWorkRequestBuilder<NotificationWorker>()

8. .setInputData(myData)

9. .setConstraints(myConstraints)

10. .addTag(NOTIFICATION_CHANNEL_ID)

11. .build()

12. [Link]().enqueue(myRequest)

Anda juga bisa menerapkan task-chaining dengan cara berikut:

 Kotlin
 Java
1. [Link]().beginWith(myRequest).then(uploadRequest).enqueue
()

Berikut adalah alur WorkManager bekerja:.

Seperti biasa kami menyarankan Anda untuk menyimak lebih jauh


tentang WorkManager melalui tautan di bawah ini:

 Introducing WorkManager
 Codelabs WorkManager
 WorkManager
 Guide to Background Processing

Mari kita mulai mengimplementasikan WorkManager dengan mengikuti


latihan pada modul berikutnya.
Latihan Scheduler dengan WorkManager
Tujuan

Pada materi kali ini Anda akan belajar memanfaatkan komponen


WorkManager. Aplikasi buatan Anda akan menjadi seperti ini:

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama MyWorkManager.


2. Menambahkan library ke dalam project.
3. Membuat kelas MyWorker untuk mengatur konfigurasi WorkManager
pada Aplikasi.
4. Mengatur tampilan pada berkas layout XML.
5. Mengimplementasikan MyWorker ke dalam Activity.
6. Menjalankan aplikasi.

Codelab WorkManager

Pada codelab latihan kali ini Anda akan membuat proses untuk
mengunduh data cuaca baik secara langsung maupun secara periodik
(per 15 menit sekali). Aplikasi akan menampilkan notifikasi cuaca saat ini.

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:


Nama Project MyWorkManager

Target & Minimum Target Phone and Tablet, API


SDK level 21

Tipe Activity Empty Activity

Activity Name MainActivity

Use AndroidX Artifacts True

Language Kotlin/Java

2. Di awal telah dijelaskan bahwa Anda akan membuat sebuah aplikasi yang
akan menampilkan cuaca saat ini dengan library WorkManager. Di sini
Anda akan menggunakan data API dari
[Link] Ini berarti aplikasi Anda butuh
mengambil data dari Internet. Untuk mempermudah urusan tersebut,
Anda akan menggunakan library AsyncHttpClient (LoopJ) yang terdapat
pada website [Link]

Untuk menambahkannya, buka berkas [Link](Module: app).


Kemudian tambahkan baris berikut:

1. dependencies {

2. ...

3. implementation "[Link]:work-runtime:2.5.0"

4. implementation '[Link]:android-async-http:1.4.10'

5. }

Lalu klik pilihan Sync Now di pojok kanan atas Android Studio seperti ini:

Ketika proses synchronize selesai, maka kedua library telah berhasil terpasang.

Selanjutnya, buat kelas baru dengan nama MyWorker dengan cara klik kanan di
package utama pada proyek aplikasi → New → Kotlin Class/Java Class, lalu
tambahkan extends dan constructor dengan cara menambahkan kode berikut:

 Kotlin
 Java
1. class MyWorker(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {

2.

3. }

Akan muncul garis merah pada kode di atas. Anda diharuskan untuk
menerapkan metode bawaan yang terdapat pada superclass Worker. Cukup klik
lampu warna merah atau tekan alt+enter untuk membuat metode yang
dibutuhkan.
Contohnya seperti ini:

 Kotlin
 Java

Seharusnya MyWorker yang Anda miliki sekarang menjadi seperti di bawah ini:

 Kotlin
 Java

1. class MyWorker(context: Context, workerParams: WorkerParameters) :


Worker(context, workerParams) {

2. override fun doWork(): Result {

3. TODO("Not yet implemented")

4. }

5. }

Ok, langkah selanjutnya Anda harus membuat akun di


website [Link] untuk mendapatkan api key. Anda dapat
registrasi menggunakan tautan ini.
Setelah proses registrasi selesai, Anda dapat masuk ke dalam
dashboard OpenWeatherMap. Selanjutnya Anda akan memperoleh application id
atau AppId. Nilai ini akan Anda gunakan untuk mengakses data pada
OpenWeatherMap. Sementara itu, API Key dapat ditemukan di tab API keys
seperti ini:

Kemudian cobalah untuk mengambil data cuaca pada browser dengan


memanggil url [Link]
q=Bandung&appid=YOUR_API_KEY. Ganti YOUR_API_KEY dengan api key
yang sudah Anda dapatkan di langkah sebelumnya. Sehingga, akan tampil data
JSON seperti berikut:
Supaya terlihat rapi, Anda bisa menambahkan extension JSON Formatter pada
Chrome melalui tautan ini sehingga tampilannya akan mudah dibaca seperti ini:

Perlu diketahui dari data di atas yaitu ada dua macam JSON, yaitu JSON Array
yang ditandai dengan tanda kurung siku "[]" dan JSON Object yang ditandai
dengan kurung kurawal "{}".

Okay sekarang saatnya mengimplementasikannya pada kelas MyWorker. Anda


akan menggunakan LoopJ untuk memanggil API OpenWeather, kemudian mem-
parsing data JSON-nya. Setelah itu tampilkan datanya di Notification seperti
berikut ini:

 Kotlin
 Java

1. ...

2. import [Link]

3. import [Link]

4.

5. class MyWorker(context: Context, workerParams: WorkerParameters) :


Worker(context, workerParams) {

6.

7. companion object {

8. private val TAG = MyWorker::[Link]


9. const val APP_ID = "YOUR_KEY_HERE"

10. const val EXTRA_CITY = "city"

11. const val NOTIFICATION_ID = 1

12. const val CHANNEL_ID = "channel_01"

13. const val CHANNEL_NAME = "dicoding channel"

14. }

15.

16. private var resultStatus: Result? = null

17.

18. override fun doWork(): Result {

19. val dataCity = [Link](EXTRA_CITY)

20. return getCurrentWeather(dataCity)

21. }

22.

23. private fun getCurrentWeather(city: String?): Result {

24. Log.d(TAG, "getCurrentWeather: Mulai.....")

25. [Link]()

26. val client = SyncHttpClient()

27. val url = "[Link]


q=$city&appid=$APP_ID"

28. Log.d(TAG, "getCurrentWeather: $url")

29. [Link](url, object : AsyncHttpResponseHandler() {

30. override fun onSuccess(statusCode: Int, headers: Array<Header?


>?, responseBody: ByteArray) {

31. val result = String(responseBody)

32. Log.d(TAG, result)

33. try {

34. val responseObject = JSONObject(result)


35. val currentWeather: String =
[Link]("weather").getJSONObject(0).getString("
main")

36. val description: String =


[Link]("weather").getJSONObject(0).getString("d
escription")

37. val tempInKelvin =


[Link]("main").getDouble("temp")

38. val tempInCelsius = tempInKelvin - 273

39. val temperature: String =


DecimalFormat("##.##").format(tempInCelsius)

40. val title = "Current Weather in $city"

41. val message = "$currentWeather, $description with


$temperature celsius"

42. showNotification(title, message)

43. Log.d(TAG, "onSuccess: Selesai.....")

44. resultStatus = [Link]()

45. } catch (e: Exception) {

46. showNotification("Get Current Weather Not Success",


[Link])

47. Log.d(TAG, "onSuccess: Gagal.....")

48. resultStatus = [Link]()

49. }

50. }

51.

52. override fun onFailure(statusCode: Int, headers: Array<Header?


>?, responseBody: ByteArray?, error: Throwable) {

53. Log.d(TAG, "onFailure: Gagal.....")

54. // ketika proses gagal, maka jobFinished diset dengan


parameter true. Yang artinya job perlu di reschedule

55. showNotification("Get Current Weather Failed",


[Link])

56. resultStatus = [Link]()


57. }

58. })

59. return resultStatus as Result

60. }

61.

62. private fun showNotification(title: String, description: String?) {

63. val notificationManager =


[Link](Context.NOTIFICATION_SERVICE)
as NotificationManager

64. val notification: [Link] =


[Link](applicationContext, CHANNEL_ID)

65. .setSmallIcon([Link].ic_notifications_black_24dp)

66. .setContentTitle(title)

67. .setContentText(description)

68. .setPriority(NotificationCompat.PRIORITY_HIGH)

69. .setDefaults(NotificationCompat.DEFAULT_ALL)

70. if ([Link].SDK_INT >= Build.VERSION_CODES.O) {

71. val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME,


NotificationManager.IMPORTANCE_HIGH)

72. [Link](CHANNEL_ID)

73. [Link](channel)

74. }

75. [Link](NOTIFICATION_ID, [Link]())

76. }

77. }

Catatan:

 Ganti teks pada YOUR_KEY_HERE dengan api key yang Anda dapatkan di
langkah sebelumnya.
 Pada saat import pilihlah [Link]; dan
[Link].
 Untuk kebutuhan asset gambar drawable, Anda bisa mendapatkannya
dengan cara klik kanan pada folder drawable → New → Vector Asset →
Pilih Clip Art dan cari dengan kata kunci "notifications" → OK → Next
→ Finish. Selain itu Anda juga bisa mengunduhnya secara manual di sini
dan menyalinnya ke direktori drawable pada project di Android Studio.

Berikutnya, lengkapi kode pada activity_main.xml sehingga menjadi seperti


ini:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:app="[Link]

4. xmlns:tools="[Link]

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. android:padding="8dp"

8. android:orientation="vertical"

9. tools:context=".MainActivity">

10.

11. <TextView

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/enter_city_name" />

15.

16. <EditText

17. android:id="@+id/editCity"

18. android:layout_width="match_parent"

19. android:layout_height="wrap_content"

20. android:ems="10"

21. android:inputType="textPersonName" />

22.

23. <Button

24. android:id="@+id/btnOneTimeTask"
25. android:layout_width="match_parent"

26. android:layout_height="wrap_content"

27. android:layout_margin="4dp"

28. android:text="@string/run_one_time_task" />

29.

30. <TextView

31. android:id="@+id/textStatus"

32. android:layout_width="wrap_content"

33. android:layout_height="wrap_content"

34. android:text="@string/status" />

35.

36. </LinearLayout>

Jangan lupa menambahkan teks yang ada di layout ke dalam [Link].

1. <resources>

2. <string name="app_name">MyWorkManager</string>

3. <string name="enter_city_name">Enter city name</string>

4. <string name="run_one_time_task">Run One Time Task</string>

5. <string name="run_periodic_task">Run Periodic Task</string>

6. <string name="cancel_periodic_task">Cancel Periodic Task</string>

7. <string name="status">Status :</string>

8. </resources>

Resource string ini akan Anda gunakan selama latihan ini.

Pada kode tersebut, Anda menambahkan EditText untuk memasukkan nama


kota yang akan dipantau cuacanya. Kemudian ada satu button untuk
menjalankan WorkManager. Sehingga tampilan aplikasi Anda saat ini adalah
seperti berikut:
Selanjutnya, pada MainActivity tambahkan aksi klik pada button seperti ini:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), OnClickListener {

2.

3. private lateinit var workManager: WorkManager

4. private lateinit var binding: ActivityMainBinding

5.

6. override fun onCreate(savedInstanceState: Bundle?) {

7. [Link](savedInstanceState)

8. binding = [Link](layoutInflater)

9. setContentView([Link])

10.

11. workManager = [Link](this)

12. [Link](this)
13. }

14.

15. override fun onClick(view: View) {

16.

17. }

18. }

Jangan lupa untuk menambahkan konfigurasi untuk mengaktifkan ViewBinding


pada [Link] (module: app) seperti berikut:

1. android {

2.

3. ...

4.

5. buildFeatures {

6. viewBinding = true

7. }

8. }

Kemudian sempurnakan kode untuk memulai WorkManager sehingga kelas


MainActivity menjadi seperti berikut:

 Kotlin
 Java

1.  ...

2. import [Link]

3.

4. class MainActivity : AppCompatActivity(), OnClickListener {

5.

6. ...

7.

8. override fun onClick(view: View) {


9. when ([Link]) {

10. [Link] -> startOneTimeTask()

11. }

12. }

13.

14. private fun startOneTimeTask() {

15. [Link] = getString([Link])

16. val data = [Link]()

17. .putString(MyWorker.EXTRA_CITY,
[Link]())

18. .build()

19. val oneTimeWorkRequest =


[Link](MyWorker::[Link])

20. .setInputData(data)

21. .build()

22. [Link](oneTimeWorkRequest)

23. [Link]([Link])

24. .observe(this@MainActivity, { workInfo ->

25. val status = [Link]

26. [Link]("\n" + status)

27. })

28. }

29. }

Terakhir, tambahkan permission internet dan ijinkan clear text traffic di


dalam berkas [Link] seperti berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4. <uses-permission android:name="[Link]"/>
5. <application

6. android:allowBackup="true"

7. android:icon="@mipmap/ic_launcher"

8. android:label="@string/app_name"

9. android:supportsRtl="true"

10. android:usesCleartextTraffic="true"

11. android:theme="@style/AppTheme">

12. <activity android:name=".MainActivity">

13. <intent-filter>

14. <action android:name="[Link]" />

15. <category android:name="[Link]" />

16. </intent-filter>

17. </activity>

18. </application>

19. </manifest>

android:usesCleartextTraffic="true" berfungsi supaya tidak terjadi error pada


perangkat dengan Android versi Pie ke atas. Hal ini karena Anda mengakses
API dengan awalan http, bukan https. Pada perangkat Android P ke atas
penggunaan ClearTextTraffic bernilai false secara default untuk keamanan.
Untuk itu, Anda perlu mengaktifkannya supaya proses pengambilan data dari API
berjalan lancar.

Selesai! Sekarang jalankan aplikasinya dan tekan tombol RUN ONE TIME TASK.
Sehingga, data cuaca akan ditampilkan pada panel notifikasi seperti contoh di
bawah ini:
Dengan menggunakan WorkManager Anda bisa mengatur kapan task Anda
dieksekusi, misal saat ada koneksi internet, saat sedang diisi dayanya, maupun
saat memori tidak penuh. Caranya yaitu dengan menambahkan constraints.
Keren bukan! Nah, sekarang coba tambahkan kode berikut pada saat
mengkonfigurasi WorkManager pada MainActivity:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), OnClickListener {

2.

3. ...

4.

5. private fun startOneTimeTask() {

6. [Link] = getString([Link])

7. val data = [Link]()

8. .putString(MyWorker.EXTRA_CITY,
[Link]())

9. .build()

10. val constraints = [Link]()


11. .setRequiredNetworkType([Link])

12. .build()

13. val oneTimeWorkRequest =


[Link](MyWorker::[Link])

14. .setInputData(data)

15. .setConstraints(constraints)

16. .build()

17. [Link](oneTimeWorkRequest)

18. [Link]([Link])

19. .observe(this@MainActivity, { workInfo ->

20. val status = [Link]

21. [Link]("\n" + status)

22. })

23. }

24. }

Codelab Periodic Task

Sekarang Anda akan membuat periodic task yang berfungsi untuk


mengambil data cuaca setiap 15 menit, sehingga data akan selalu ter-
update. Pertanyaanya mengapa 15 menit? Hal itu karena waktu tersebut
adalah batas minimal interval yang diperbolehkan di dalam WorkManager.
Selain itu, Anda juga akan belajar membatalkan task yang sudah berjalan.
Yuk, lanjutkan belajarnya!

1. Pertama, tambahkan button pada activity_main.xml menggunakan kode


berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="[Link]

3. xmlns:app="[Link]

4. xmlns:tools="[Link]

5. android:layout_width="match_parent"
6. android:layout_height="match_parent"

7. android:padding="8dp"

8. android:orientation="vertical"

9. tools:context=".MainActivity">

10.

11. <TextView

12. android:layout_width="match_parent"

13. android:layout_height="wrap_content"

14. android:text="@string/enter_city_name" />

15.

16. <EditText

17. android:id="@+id/editCity"

18. android:layout_width="match_parent"

19. android:layout_height="wrap_content"

20. android:ems="10"

21. android:inputType="textPersonName" />

22.

23. <Button

24. android:id="@+id/btnOneTimeTask"

25. android:layout_width="match_parent"

26. android:layout_height="wrap_content"

27. android:layout_margin="4dp"

28. android:text="@string/run_one_time_task" />

29.

30. <LinearLayout

31. android:layout_width="match_parent"

32. android:layout_height="wrap_content"
33. android:orientation="horizontal">

34.

35. <Button

36. android:id="@+id/btnPeriodicTask"

37. android:layout_width="wrap_content"

38. android:layout_height="wrap_content"

39. android:layout_margin="4dp"

40. android:layout_weight="1"

41. android:text="@string/run_periodic_task" />

42.

43. <Button

44. android:id="@+id/btnCancelTask"

45. android:layout_width="wrap_content"

46. android:layout_height="wrap_content"

47. android:layout_margin="4dp"

48. android:layout_weight="1"

49. android:enabled="false"

50. android:text="@string/cancel_periodic_task" />

51. </LinearLayout>

52.

53. <TextView

54. android:id="@+id/textStatus"

55. android:layout_width="wrap_content"

56. android:layout_height="wrap_content"

57. android:text="@string/status" />

58.

59. </LinearLayout>
Kemudian tambahkan kode berikut di MainActivity untuk membuat periodic
task dan membatalkan task:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity(), OnClickListener {

2.

3. private lateinit var workManager: WorkManager

4. private lateinit var periodicWorkRequest: PeriodicWorkRequest

5. private lateinit var binding: ActivityMainBinding

6.

7. override fun onCreate(savedInstanceState: Bundle?) {

8. [Link](savedInstanceState)

9. binding = [Link](layoutInflater)

10. setContentView([Link])

11.

12. workManager = [Link](this)

13.

14. [Link](this)

15. [Link](this)

16. [Link](this)

17. }

18.

19. override fun onClick(view: View) {

20. when ([Link]) {

21. [Link] -> startOneTimeTask()

22. [Link] -> startPeriodicTask()

23. [Link] -> cancelPeriodicTask()

24. }
25. }

26.

27. private fun startOneTimeTask() {

28. [Link] = getString([Link])

29. val data = [Link]()

30. .putString(MyWorker.EXTRA_CITY,
[Link]())

31. .build()

32. val constraints = [Link]()

33. .setRequiredNetworkType([Link])

34. .build()

35. val oneTimeWorkRequest =


[Link](MyWorker::[Link])

36. .setInputData(data)

37. .setConstraints(constraints)

38. .build()

39. [Link](oneTimeWorkRequest)

40. [Link]([Link])

41. .observe(this@MainActivity, { workInfo ->

42. val status = [Link]

43. [Link]("\n" + status)

44. })

45. }

46.

47. private fun startPeriodicTask() {

48. [Link] = getString([Link])

49. val data = [Link]()

50. .putString(MyWorker.EXTRA_CITY,
[Link]())
51. .build()

52. val constraints = [Link]()

53. .setRequiredNetworkType([Link])

54. .build()

55. periodicWorkRequest =
[Link](MyWorker::[Link], 15,
[Link])

56. .setInputData(data)

57. .setConstraints(constraints)

58. .build()

59. [Link](periodicWorkRequest)

60. [Link](periodicWorkReques
[Link])

61. .observe(this@MainActivity, { workInfo ->

62. val status = [Link]

63. [Link]("\n" + status)

64. [Link] = false

65. if ([Link] == [Link]) {

66. [Link] = true

67. }

68. })

69. }

70.

71. private fun cancelPeriodicTask() {

72. [Link]([Link])

73. }

74. }
2. Selanjutkan jalankan kembali aplikasi Anda. Cobalah untuk jalankan task
dengan menekan tombol RUN PERIODIC TASK dan kemudian
membatalkannya dengan menekan CANCEL PERIODIC TASK.
3. Dapat dilihat setelah aplikasi menjalankan task (RUNNING), ia akan
masuk ke state ENQUEUED untuk menunggu proses selanjutnya 15 menit
kemudian. Selanjutnya jika Anda membatalkan task, maka ia masuk ke
state CANCELLED.
Menggunakan Background Task Inspector

Pada Android Studio versi Arctic Fox ke atas, terdapat fitur bernama
Background Task Inspector untuk melihat task WorkManager yang sedang
berjalan. Anda dapat memanfaatkan fitur ini jika menggunakan
WorkManager versi 2.5.0 ke atas. Dengan menggunakan fitur ini, Anda
dapat memonitor data setiap task dan hubungan setiap WorkManager.
Untuk memunculkan fitur ini, pilih View → Tool Window → App
Inspection.

Setelah memilih menu tersebut, akan muncul panel baru di bawah dengan
nama App Inspection. Buka panel tersebut dan pilih tab Background
Task Inspector. Jalankan aplikasi dan isi nama kota serta klik Run One
Time Task untuk menjalankan WorkManager. Jika data aplikasi tidak
otomatis muncul, pilih nama aplikasi pada dropdown menu. Berikut
contoh tampilan Background Task Inspector dari latihan ini:

Anda dapat melihat detail task seperti status, waktu task dimulai, berapa
kali percobaan, dan output dari sebuah WorkManager. Anda juga bisa klik
baris tersebut tersebut untuk melihat detail setiap task seperti ini:
Panel ini memberikan informasi yang lebih detail terkait description,
execution, continuation, dan result. Pada bagian description terdapat
informasi nama package kelas Worker, tags, dan UUID. Kemudian pada
bagian execution terdapat informasi constraint (jika ada), frekuensi, dan
status task. Pada bagian continuation, menunjukkan lokasi task tersebut
pada sebuah chaining. Dan terakhir pada bagian Result, menunjukkan
waktu mulai task, berapa kali perobaaan, dan output dari sebuah
WorkManager.

Anda juga bisa klik Show in graph untuk melihat hubungan antar
WorkManager dalam bentuk grafik:

Namun, karena di sini hanya ada satu WorkManager, maka grafik tidak
terlalu terlihat. Untuk memberikan gambaran, berikut adalah contoh
grafik yang lebih kompleks dengan kode berikut:
Anda dapat melihat dan memonitor task pada WorkManager dengan lebih
mudah menggunakan Background Task Inspector. Mantap!

Bedah Kode
UsesClearTextTraffic

Pada AndroidManifest kita menambahkan kode berikut:

1. android:usesCleartextTraffic="true"

Kode di atas berfungsi supaya tidak terjadi error pada perangkat dengan
Android versi Pie ke atas. Hal ini karena kita mengakses API dengan
awalan http, bukan https, dan pada perangkat Android P ke atas
penggunaan CleartextTraffic bernilai false secara default untuk
keamanan. Untuk itulah kita perlu mengaktifkannya supaya proses
pengambilan data dari API berjalan lancar.

WorkManager
Worker

Kita mulai dengan melihat kode pada kelas MyWorker berikut:

 Kotlin
 Java

1. class MyWorker(context: Context, workerParams: WorkerParameters) :


Worker(context, workerParams) {

2.

3. override fun doWork(): Result {


4. val dataCity = [Link](EXTRA_CITY)

5. return getCurrentWeather(dataCity)

6. }

7. }

Metode doWork adalah metode yang akan dipanggil ketika WorkManager


berjalan. Kode di dalamnya akan dijalankan di background thread secara
otomatis. Metode ini juga mengembalikan nilai berupa Result yang
berfungsi untuk mengetahui status WorkManager yang berjalan. Ada
beberapa status yang bisa dikembalikan yaitu:

 [Link](), result yang menandakan berhasil.


 [Link](), result yang menandakan gagal.
 [Link](), result yang menandakan untuk mengulang task lagi.

Di kode Anda, Anda membuat method getCurrentWeather yang


mengembalikan nilai Result. Perhatikan kode yang dicetak tebal berikut:

 Kotlin
 Java

1. private var resultStatus: Result? = null

2.

3. private fun getCurrentWeather(city: String?): Result {

4. ...

5. [Link](url, object : AsyncHttpResponseHandler() {

6. override fun onSuccess(statusCode: Int, headers: Array<Header?>?,


responseBody: ByteArray) {

7. val result = String(responseBody)

8. Log.d(TAG, result)

9. try {

10. ...

11. resultStatus = [Link]()

12. } catch (e: Exception) {

13. ...

14. resultStatus = [Link]()


15. }

16. }

17.

18. override fun onFailure(statusCode: Int, headers: Array<Header?>?,


responseBody: ByteArray?, error: Throwable) {

19. ...

20. resultStatus = [Link]()

21. }

22. })

23. return resultStatus as Result

24. }

Dengan begitu, kita bisa menentukan kembalian dari proses ini apakah
berhasil atau gagal dengan menggunakan kode tersebut.

WorkRequest

Selanjutnya, mari kita bedah kode untuk membuat WorkManager di kelas


MainActivity, Ada dua macam request yang bisa dibuat, yaitu:

 OneTimeWorkRequest untuk menjalankan task sekali saja, untuk


membuatnya Anda menggunakan kode berikut:

o Kotlin
o Java

1. val oneTimeWorkRequest =
[Link](MyWorker::[Link]).build()

2. [Link](oneTimeWorkRequest)

PeriodicWorkRequest untuk menjalankan task secara periodic, untuk


membuatnya Anda menggunakan kode berikut:

 Kotlin
 Java

1. val periodicWorkRequest =
[Link](MyWorker::[Link], 15,
[Link]).build()

2. [Link](periodicWorkRequest)
Kode di atas digunakan untuk menjalankan PeriodicWorkRequest dengan
interval 15 menit. Anda bisa mengaturnya dengan mengganti parameter
kedua dan ketiga.

Catatan:
Batas minimal interval yang diperbolehkan yaitu 15 menit, sama dengan
JobScheduler.

Kirim Data ke Worker

Selanjutnya, mari kita bedah kode untuk mengirim data di WorkManager,


perhatikan kode di dalam MainActivity.

 Kotlin
 Java

1. val data = [Link]()

2. .putString(MyWorker.EXTRA_CITY, [Link]())

3. .build()

4.

5. val oneTimeWorkRequest = [Link](MyWorker::[Link])

6. .setInputData(data)

7. .build()

8. [Link](oneTimeWorkRequest)

Fungsi di atas digunakan untuk membuat one-time request. Saat


membuat request, Anda bisa menambahkan data untuk dikirimkan
dengan membuat object Data yang berisi data key-value, key yang
dipakai di sini yaitu MyWorker.EXTRA_CITY. Setelah itu dikirimkan melalui
setInputData. Kemudian untuk mendapatkan datanya di kelas Worker,
Anda perlu menggunakan kode berikut:

 Kotlin
 Java

1. val dataCity = [Link](EXTRA_CITY)

Yang perlu diperhatikan yaitu tipe data dan key yang dimasukkan harus
sama persis. Di sini tipe data yang digunakan yaitu String dan key yang
dipakai yaitu EXTRA_CITY.
Constraint

Constraint digunakan untuk memberikan syarat kapan task ini dieksekusi,


perhatikan kode di dalam MainActivity.

 Kotlin
 Java

1. val constraints = [Link]()

2. .setRequiredNetworkType([Link])

3. .build()

4. val oneTimeWorkRequest = [Link](MyWorker::[Link])

5. .setInputData(data)

6. .setConstraints(constraints)

7. .build()

Ada banyak kondisi yang bisa kita set di sini. Misalnya adalah:

 setRequiredNetworkType, ketika bernilai CONNECTED berarti dia harus


terhubung ke koneksi internet, apa pun jenisnya. Bila kita ingin memasang
ketentuan bahwa job hanya akan berjalan ketika perangkat terhubung ke
network Wi-fi, maka kita perlu memberikan nilai UNMETERED.
 setRequiresDeviceIdle, menentukan apakah task akan dijalankan ketika
perangkat dalam keadaan sedang digunakan atau tidak. Secara default,
parameter ini bernilai false. Bila kita ingin task dijalankan ketika
perangkat dalam kondisi tidak digunakan, maka kita beri nilai true.
 setRequiresCharging, menentukan apakah task akan dijalankan ketika
baterai sedang diisi atau tidak. Nilai true akan mengindikasikan bahwa
task hanya berjalan ketika baterai sedang diisi. Kondisi ini dapat
digunakan bila task yang dijalankan akan memakan waktu yang lama.
 setRequiresStorageNotLow, menentukan apakah task yang dijalankan
membutuhkan ruang storage yang tidak sedikit. Secara default, nilainya
bersifat false.
 Dan ketentuan lainnya yang bisa kita gunakan.

WorkInfo

WorkInfo digunakan untuk mengetahui status task yang dieksekusi,


perhatikan kode di dalam MainActivity.

 Kotlin
 Java

1. [Link]([Link])

2. .observe(this@MainActivity, { workInfo ->


3. val status = [Link]

4. [Link]("\n" + status)

5. [Link] = false

6. if ([Link] == [Link]) {

7. [Link] = true

8. }

9. })

Anda dapat membaca status secara live dengan menggunakan


getWorkInfoByIdLiveData. Anda juga bisa memberikan aksi pada state
tertentu dengan mengambil data state dan membandingkannya dengan
konstanta yang bisa didapat di [Link]. Misalnya, pada kode di
atas kita mengatur tombol Cancel task aktif jika task dalam
state ENQUEUED.

Cancel Work

Anda dapat membatalkan task yang berjalan dengan cara berikut:

 Kotlin
 Java

1. [Link]([Link])

Kode di atas digunakan untuk membatalkan task berdasarkan id request.


Selain menggunakan id, Anda juga bisa menambahkan tag pada request.
Kelebihan dari penggunaan tag yaitu Anda bisa membatalkan task lebih
dari satu task sekaligus seperti ini:

 Kotlin
 Java

1. val oneTimeWorkRequest = [Link](MyWorker::[Link])

2. .setInputData(data)

3. .setConstraints(constraints)

4. .addTag("MY_TAG")

5. .build()

6. ...

7. periodicWorkRequest = [Link]([Link], 15,


[Link])
8. .setInputData(data)

9. .setConstraints(constraints)

10. .addTag("MY_TAG")

11. .build()

12. ...

13. [Link]("MY_TAG")

Work Chaining

Selain menjalankan single task seperti pada latihan, Anda juga bisa
membuat task-chaining, baik secara paralel maupun sekuensial. Berikut
adalah contohnya:

 Kotlin
 Java

1. workManager

2. .beginWith(workA1, workA2, workA3)

3. .then(workB)

4. .then(workC1, workC2)

5. .enqueue()

Anda dapat mendalami lebih lanjut topik ini dengan membaca tautan
berikut:

 WorkManager
 Codelab WorkManager
 Introducing WorkManager

Parsing JSON API dengan LoopJ


LoopJ

LoopJ adalah salah library untuk menangani koneksi ke API. Selain LoopJ
ada Retrofit, Volley, FastAndroidNetworking dsb. Di sini kita menggunakan
LoopJ karena dinilai mudah untuk pemula dan cocok untuk memahami
parsing data JSON.

 Kotlin
 Java

1. private fun getCurrentWeather(job: JobParameters) {


2. ...

3. [Link]()

4. val client = SyncHttpClient()

5. val url = "[Link]


q=$CITY&appid=$YOUR_KEY_HERE"

6. [Link](url, object : AsyncHttpResponseHandler() {

7. override fun onSuccess(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray) {

8. ...

9. }

10.

11. override fun onFailure(statusCode: Int, headers: Array<Header>,


responseBody: ByteArray, error: Throwable) {

12. ...

13. }

14. })

Catatan:
Di sini kita menggunakan SyncHttpClientuntuk koneksi data yang sifatnya
synchronous. Selain itu, ada AsyncHttpClient untuk koneksi secara
asynchronous. Khusus di WorkManager Anda perlu menjalankan proses
secara synchronous supaya bisa mendapatkan result success. Selain itu
WorkManager sudah berjalan di background thread, jadi aman saja
menjalankan secara langsung. Namun jika Anda ingin menggunakan LoopJ
di Activity, maka gunakanlah AsyncHttpClient supaya tidak terjadi
error NetworkOnMainThread.

Untuk terhubung dengan [Link], kita cukup memanggil


get() dengan url yang dibutuhkan. Metode-metode yang ada di
AsyncHttpClient merepresentasikan metode HTTP seperti POST, GET, PUT,
HEAD dan DELETE.

Target dari url webservice yang akan diakses adalah


[Link]
q="+CITY+"&appid="+YOUR_KEY_HERE.

Pada bagian CITY, kita beri nilai sesuai dari inputan EditText. Sementara
pada bagian YOUR_KEY_HERE, kita masukkan nilai API_KEY yang telah
disediakan oleh [Link].
Kita dapat memanfaatkan callback onSuccess() untuk menandakan
bahwa proses berhasil. Sementara itu, untuk mengindikasikan kegagalan,
kita dapat menggunakan callback onFailure().

JsonArray dan JsonObject

JSON adalah kepanjangan dari JavaScript Object Notation. Ia


merupakan format pertukaran data yang ringan, mudah dibaca dan ditulis
oleh manusia, serta mudah diterjemahkan dan dibuat (generate) oleh
komputer. Ada dua macam JSON, yaitu JSON Array yang ditandai dengan
tanda kurung siku / "[]" dan JSON Object yang ditandai dengan kurung
kurawal / "{}".

Misalnya Anda ingin mengambil data description maka Anda perlu


melewati JsonArray weather dulu, kemudian ambil urutan ke-0 dan Anda
baru bisa mengambil data description. Contoh untuk kodenya seperti ini:
.getJSONArray("weather").getJSONObject(0).getString("description") dan
jika Anda ingin mengambil data suhu maka kodenya seperti ini:
.getJSONObject("main").getDouble("temp"). Sehingga, untuk
mendapatkan data secara keseluruhan akan seperti ini:

 Kotlin
 Java

1. val result = String(responseBody)

2. Log.d(TAG, result)

3. try {
4. val responseObject = JSONObject(result)

5.

6. val currentWeather =
[Link]("weather").getJSONObject(0).getString("
main")

7. val description =
[Link]("weather").getJSONObject(0).getString("d
escription")

8. val tempInKelvin =
[Link]("main").getDouble("temp")

9.

10. val tempInCelsius = tempInKelvin - 273

11. val temperature = DecimalFormat("##.##").format(tempInCelsius)

12.

13. val title = "Current Weather"

14. val message = "$currentWeather, $description with $temperature celsius"

15. val notifId = 100

16.

17. showNotification(applicationContext, title, message, notifId)

18. ...

19. } catch (e: Exception) {

20. ...

21. [Link]()

22. }

Kode di atas memproses respons yang diterima dari webservice. Karena


nilai awal respons adalah data byte, maka kita harus mengubahnya
menjadi string. Hal ini akan memudahkan kita untuk mengubahnya
menjadi bentuk JSON.

Catatan:
Perhatikan saat getJsonArray atau getString, pastikan teks yang diambil
sesuai dengan yang berasal dari API dan harus sama persis.
Untuk lebih membuat kamu paham tentang JSON kamu bisa mempelajari
lebih lanjut pada url ini:

 JSON
 JSON Parsing

Selamat Anda sudah belajar tentang WorkManager dan Parsing JSON yang
merupakan skill dasar sebagai Android developer. Pahami baik-baik dan
baca ulang jika kurang mengerti. Hal ini karena akan sangat penting
untuk Anda. Semangat!

Bonus Codelab : Parsing JSON dengan Moshi

Sejauh ini, sejak Anda pertama kali belajar tentang koneksi ke web API,
Anda selalu melakukan parsing JSON secara manual, yaitu dengan
menggunakan JsonObject dan JsonArray. Nah, perlu Anda ketahui bahwa
sebenarnya Anda juga bisa mem-parsing JSON secara otomatis tanpa
perlu harus menulis secara manual key yang ingin didapat. Anda dapat
melakukannya dengan bantuan library pihak ketiga, seperti Moshi, GSON,
maupun Jackson. Sebenarnya ketiganya memiliki konsep yang sama,
namun pada codelab bonus ini kita akan menggunakan Moshi sebagai
contohnya. Yuk kita coba!

1. Pertama, tambahkan terlebih dahulu library Moshi pada [Link]


(module: app) seperti berikut:

o Kotlin
o Java

1. plugins {

2. id '[Link]'

3. id 'kotlin-android'

4. id 'kotlin-kapt'

5. }

6. ...

7. dependencies {

8. ...

9. implementation "[Link]:moshi-kotlin:1.11.0"

10. kapt '[Link]:moshi-kotlin-codegen:1.8.0'

11. }
Selanjutnya buatlah beberapa class yang merepresentasikan susunan pada
JSON. Sebagai contoh dengan bentuk JSON yang didapat dari API
OpenWeatherMapseperti ini:

1. {

2. "coord":{

3. "lon":107.6186,

4. "lat":-6.9039

5. },

6. "weather":[

7. {

8. "id":804,

9. "main":"Clouds",

10. "description":"overcast clouds",

11. "icon":"04d"

12. }

13. ],

14. "base":"stations",

15. "main":{

16. "temp":299.3,

17. "feels_like":301.74,

18. "temp_min":299.3,

19. "temp_max":299.3,

20. "pressure":1012,

21. "humidity":62,

22. "sea_level":1012,

23. "grnd_level":932

24. },

25. "visibility":10000,
26. "wind":{

27. "speed":0.68,

28. "deg":38,

29. "gust":0.87

30. },

31. "clouds":{

32. "all":99

33. },

34. "dt":1614909285,

35. "sys":{

36. "country":"ID",

37. "sunrise":1614898494,

38. "sunset":1614942453

39. },

40. "timezone":25200,

41. "id":1650357,

42. "name":"Bandung",

43. "cod":200

44. }

Karena Anda hanya akan mengambil data yang dibutuhkan saja (yang dicetak
tebal), buatlah bentuk class-nya seperti berikut:

 Kotlin
 Java

1. data class Response(

2. val id: Int,

3. val name: String,

4. @Json(name = "weather")

5. val weatherList: List<Weather>,


6. val main: Main,

7. )

8.

9. data class Weather(

10. val main: String,

11. val description: String

12. )

13.

14. data class Main(

15. @Json(name = "temp")

16. val temperature: Double

17. )

Kuncinya adalah apabila ia berupa JSON Array (ditandai dengan kurung siku/[ ]),
variabelnya berupa List. Jika ia berupa JSON Object (ditandai dengan kurung
kurawal/{ }), bentuknya adalah Object dari Class.

Pastikan juga bahwa nama variabel yang digunakan sama dengan key yang ada
pada API. Apabila Anda ingin menggunakan nama variabel yang berbeda, Anda
bisa menggunakan anotasi @Json seperti contoh di atas.

Setelah usai, Anda cukup menginisialisasi Moshi dan dapat mengganti kode
untuk parsing manual yang lama dengan Object yang bisa langsung didapatkan
dengan Moshi seperti berikut:

 Kotlin
 Java

1. private fun getCurrentWeather(city: String?): Result {

2. ...

3. [Link](url, object : AsyncHttpResponseHandler() {

4. override fun onSuccess(statusCode: Int, headers: Array<Header?


>?, responseBody: ByteArray) {

5. val result = String(responseBody)

6. Log.d(TAG, result)
7. try {

8. // Hapus kode ini

9. // val responseObject = JSONObject(result)

10. // val currentWeather: String =


[Link]("weather").getJSONObject(0).getStr
ing("main")

11. // val description: String =


[Link]("weather").getJSONObject(0).getStr
ing("description")

12. // val tempInKelvin =


[Link]("main").getDouble("temp")

13.

14. val moshi = [Link]()

15. .addLast(KotlinJsonAdapterFactory())

16. .build()

17.

18. val jsonAdapter =


[Link](Response::[Link])

19. val response = [Link](result)

20.

21. response?.let {

22. val currentWeather = [Link][0].main

23. val description = [Link][0].description

24. val tempInKelvin = [Link]

25.

26. val tempInCelsius = tempInKelvin - 273

27. val temperature : String =


DecimalFormat("##.##").format(tempInCelsius)

28. val title = "Current Weather in $city"

29. val message = "$currentWeather, $description with


$temperature celsius"

30. showNotification(title, message)


31. }

32. Log.d(TAG, "onSuccess: Selesai.....")

33. resultStatus = [Link]()

34. } catch (e: Exception) {

35. ...

36. }

37. }

38. ...

39. })

40. return resultStatus as Result

41. }

Yang perlu diperhatikan adalah pada kode [Link] pastikan Anda


memberikan class yang sesuai dengan hasil JSON yang diberikan. Dalam
kasus ini adalah kelas Response yang merupakan induk dari semua
kelas.
Catatan:
Apabila response JSON dimulai dengan kurung siku / [ ] yang berarti ia
berupa JSON Array. Maka pada [Link] argumen yang dimasukkan
adalah Array<Response>.

3. Jalankanlah kembali aplikasi Anda dan aplikasi tetap berhasil digunakan


secara sempurna. Yang membedakan adalah sekarang Anda tidak perlu
mem-parsing JSON secara manual satu per satu lagi. Namun Anda bisa
langsung mendapatkannya dalam bentuk Object. Bayangkan jika data
yang ingin Anda ambil puluhan tentu cara ini akan sangat membantu kan?
Kelebihan lainnya adalah Anda bisa menghindari eror yang bisa saja
terjadi karena salah menuliskan key atau pendefinisian object saat mem-
parsing secara manual. Keren kan! Selamat mencoba!

Selamat! Anda telah mempelajari sesuatu yang keren. Tidak hanya belajar
bagaimana menerapkan WorkManager, tetapi Anda juga telah belajar
bagaimana mengambil data dari sebuah layanan di Internet. Anda juga
telah belajar mengintegrasikan library ke dalam aplikasi melalui gradle.

Source code dapat diunduh di:

 Source Code Latihan WorkManager


Teori Retrofit

Retrofit adalah satu library buatan Square yang populer digunakan untuk
melakukan Networking ke Web API. Tahukah mengapa? Dengan Retrofit,
mengatur endpoint API dan parsing JSON jadi jauh lebih mudah.

Untuk menggunakannya, Anda perlu menambahkan beberapa library


terlebih dahulu seperti berikut:

1. implementation "[Link].retrofit2:retrofit:$retrofitVersion"

2. implementation "[Link].retrofit2:converter-gson:
$retrofitVersion"

Catatan:
Untuk melihat versi terbaru Anda dapat melihatnya di sini.

Seperti yang Anda lihat, selain library Retrofit, Anda juga menambahkan
library untuk converter, dalam hal ini yaitu Gson. Alih-alih melakukan
parsing secara manual, melalui Gson Anda dapat melakukan parsing
secara otomatis hanya dengan menambahkan annotation pada class
model. Sebenarnya terdapat converter lain yang bisa Anda gunakan
seperti Moshi, Jackson, dsb yang dapat Anda lihat pada tautan ini. Namun,
pada latihan ini Anda hanya akan fokus menggunakan GSON.

Studi Kasus

Supaya lebih jelas mari kita buat contohnya dengan mengambil data list
users dari URL [Link] Respon dari request
pada URL [Link] akan menghaspilkan output seperti berikut:

1. {

2. "page":1,

3. "per_page":6,

4. "total":12,

5. "total_pages":2,

6. "data":[

7. {

8. "id":1,

9. "email":"[Link]@[Link]",
10. "first_name":"George",

11. "last_name":"Bluth",

12.
"avatar":"[Link]
n/[Link]"

13. },

14. {

15. "id":2,

16. "email":"[Link]@[Link]",

17. "first_name":"Janet",

18. "last_name":"Weaver",

19.
"avatar":"[Link]
n/[Link]"

20. },

21. {

22. "id":3,

23. "email":"[Link]@[Link]",

24. "first_name":"Emma",

25. "last_name":"Wong",

26.
"avatar":"[Link]
aev/[Link]"

27. },

28. {

29. "id":4,

30. "email":"[Link]@[Link]",

31. "first_name":"Eve",

32. "last_name":"Holt",
33.
"avatar":"[Link]
ires/[Link]"

34. },

35. {

36. "id":5,

37. "email":"[Link]@[Link]",

38. "first_name":"Charles",

39. "last_name":"Morris",

40.
"avatar":"[Link]
oon/[Link]"

41. },

42. {

43. "id":6,

44. "email":"[Link]@[Link]",

45. "first_name":"Tracey",

46. "last_name":"Ramos",

47.
"avatar":"[Link]
o/[Link]"

48. }

49. ]

50. }

Setelah tahu berbagai kata kunci dalam output tersebut, Anda perlu
membuat kelas model untuk data JSON di atas dan menambahkan
annotation seperti ini:

 Kotlin
 Java

1. data class ResponseUser(


2. @field:SerializedName("page")

3. val page: Int? = null,

4.

5. @field:SerializedName("per_page")

6. val perPage: Int? = null,

7.

8. @field:SerializedName("total")

9. val total: Int? = null,

10.

11. @field:SerializedName("total_pages")

12. val totalPages: Int? = null,

13.

14. @field:SerializedName("data")

15. val data: List<DataItem>? = null

16. )

Catatan:
Pastikan bahwa property yang di dalam SerializedName memiliki nilai
yang sama persis dengan key yang pada respon JSON.

Selanjutnya, buat juga model yang berisi data user dan tambahkan
annotation seperti ini:

 Kotlin
 Java

1. data class DataItem(

2. @field:SerializedName("id")

3. val id: Int? = null,

4.

5. @field:SerializedName("email")

6. val email: String? = null,


7.

8. @field:SerializedName("first_name")

9. val firstName: String? = null,

10.

11. @field:SerializedName("last_name")

12. val lastName: String? = null,

13.

14. @field:SerializedName("avatar")

15. val avatar: String? = null

16. )

SerializedName berfungsi untuk menandai suatu variabel untuk


dimasukkan data dengan key yang sesuai dari JSON. Misalnya variabel
firstName akan dimasukkan data dengan key "first_name".

Tips:
Anda bisa memanfaatkan plugin RoboPOJOGenerator pada Android Studio
untuk membuat kelas model secara otomatis.

Setelah usai, Anda perlu membuat interface berdasarkan URL API


[Link]

 Kotlin
 Java

1. interface ApiService {

2. @GET("api/users")

3. fun getListUsers(@Query("page") page: String): Call<ResponseUser>

4. }

Seperti yang Anda lihat, di sini URL yang digunakan hanya bagian
belakangnya saja. Hal ini dikarenakan URL dibagi menjadi 3 bagian yaitu:

 Base URL : [Link] (URL dasar yang tetap yang akan diletakkan
di Api )
 Endpoint : api/users (bagian akhir URL yang biasanya digunakan untuk
menentukan aksi)
 Parameter : ?page=1 (ditandai dengan awalan “?” dan pemisah antar
parameter “&”)
Kemudian untuk membuat instance Retrofit diperlukan [Link]
seperti berikut:

 Kotlin
 Java

1. val retrofit = [Link]()

2. .baseUrl("[Link]

3. .addConverterFactory([Link]())

4. .build()

5. val apiService = [Link](ApiService::[Link])

Perhatikan bahwa base URL diletakkan di sini dan juga terdapat converter
factory yang berfungsi untuk menambahkan converter GSON.

Kemudian untuk mengeksekusi request secara asynchronous, Anda dapat


menggunakan fungsi enqueue seperti berikut:

 Kotlin
 Java

1. val client = [Link]("1")

2.

3. [Link](object : Callback<ResponseUser> {

4. override fun onResponse(call: Call<ResponseUser>, response:


Response<ResponseUser>) {

5. if ([Link]) {

6. val listUser = [Link]()?.data as List<DataItem>

7. ...

8. }

9. }

10.

11. override fun onFailure(call: Call<ResponseUser>, t: Throwable) {

12. // Terpanggil ketika tidak dapat menghubungi server

13. }
14. })

Wow! Anda langsung bisa mendapatkan List Object tanpa perlu


melakukan parsing manual. Mantap bukan?

Mari kita bandingkan jika tidak menggunakan library GSON Converter.


Untuk mendapatkan listUser, Anda perlu menuliskan kode seperti berikut:

 Kotlin
 Java

1. val listUser = arrayListOf<DataItem>()

2. val jsonObject = JSONObject([Link]().toString())

3. val jsonArray = [Link]("data")

4. for (i in 0 until [Link]()) {

5. val id = [Link](i).getInt("id")

6. val email = [Link](i).getString("email")

7. val firstName = [Link](i).getString("first_name")

8. val lastName = [Link](i).getString("last_name")

9. val avatar = [Link](i).getString("avatar")

10. val user = DataItem(id, email, firstName, lastName, avatar)

11. [Link](user)

12. }

Banyak sekali baris yang diperlukan bukan? Dengan Retrofit dan GSON
Converter, kode tersebut dapat diringkas hanya menjadi satu baris.
Mantap! Contoh ini juga masih menggunakan API yang simpel lho!
Bayangkan kalau API yang digunakan jauh lebih kompleks lagi. Tentu
menggunakan GSON Converter ini akan sangat berguna sekali.

Macam Method dan Annotation pada Retrofit

Selain request @GET seperti contoh di atas, Anda juga dapat melakukan
variasi request lainnya seperti berikut:

 Kotlin
 Java

1. //add information using Header


2. @Headers("Authorization: token <Personal Access Token>" )

3. @GET("api/users")

4. fun getListUsers(@Query("page") page: String): Call<ResponseUser>

5.

6. //get list user by id using path

7. @GET("api/users/{id}")

8. fun getUser(@Path("id") id: String): Call<ResponseUser>

9.

10. //post user using field x-www-form-urlencoded

11. @FormUrlEncoded

12. @POST("api/users")

13. fun createUser(

14. @Field("name") name: String,

15. @Field("job") job: String

16. ): Call<ResponseUser>

17.

18. //upload file using multipart

19. @Multipart

20. @PUT("api/uploadfile")

21. fun updateUser(

22. @Part("file") file: [Link],

23. @PartMap data: Map<String, RequestBody>

24. ): Call<ResponseUser>

Berikut adalah fungsi dari masing-masing kode annotation di atas:

 @Headers : menambahkan informasi tambahan pada header request


seperti Authorization, jenis data dsb.
 @Query : memasukkan parameter pada method @Get.
 @Path : memasukkan variabel yang dapat diubah-ubah pada endpoint
Sebagai contoh id akan mengganti {id} yang ada di endpoint.
 @FormUrlEncoded : menandai fungsi pada @Post sebagai form-url-
encoded.
 @Field : memasukkan parameter pada method @Post.
 @Multipart : menandai sebuah fungsi bahwa ia merupakan multi-part.
 @Part : mengirimkan file dengan cara multi-part.
 @PartMap : mengirimkan data lain selain file pada multi-part.

Mantap bukan? Anda tinggal memanggil fungsi yang di dalam ApiService


ini ketika perlu suatu data tanpa membuat dari awal.

Debug Response dari Retrofit.

Untuk mengetahui hasil respon saat melakukan request dengan


menggunakan Retrofit, Anda bisa menggunakan 2 cara berikut:

Logging Interceptor by OkHttp

Logging Interceptor merupakan library tambahan yang digunakan untuk


menampilkan hasil response pada Logcat tanpa menuliskannya secara
manual. Untuk menggunakannya, Anda perlu menambahkan library pada
[Link] seperti berikut:

1. implementation '[Link].okhttp3:logging-interceptor:
$loggingInterceptorVersion'

Untuk menggunakannya Anda perlu menambahkan client baru seperti


berikut:

 Kotlin
 Java

1. val loggingInterceptor =

2.
HttpLoggingInterceptor().setLevel([Link])

3. val client = [Link]()

4. .addInterceptor(loggingInterceptor)

5. .build()

6. val retrofit = [Link]()

7. .baseUrl("[Link]

8. .addConverterFactory([Link]())

9. .client(client)
10. .build()

Ketika request dijalankan, akan muncul hasil dari interceptor tersebut


yang terlihat seperti ini:

Dapat dilihat bahwa semua response body dapat tampil di logcat. Untuk
mencarinya, cukup dengan kata kunci “okhttp”.

Chucker

Berbeda dengan library sebelumnya, dengan chucker Anda dapat


menampilkan hasil response langsung pada device tanpa harus melihat
Logcat. Untuk menambahkan library tersebut, tambahkan library tersebut
pada [Link] seperti berikut:

1. debugImplementation "[Link]:library:
$chuckerVersion"

Untuk penggunaanya, cukup simpel yaitu dengan menambahkan


chuckerInterceptor seperti berikut:

 Kotlin
 Java

1. val client = [Link]()

2. .addInterceptor(ChuckerInterceptor(context))

3. .build()

4. val retrofit = [Link]()

5. .baseUrl("[Link]

6. .addConverterFactory([Link]())
7. .client(client)

8. .build()

Hasil interceptor dari Chucker di atas akan terlihat seperti

ini:

Yeay, Anda telah mempelajari berbagai macam tentang Retrofit, mulai


dari cara penggunaan sampai proses debugging. Mantap! Untuk
mempelajari lebih lanjut, Anda dapat melihat beberapa tautan berikut:

 Documentation : Retrofit
 Documentation : Chucker
Latihan Networking dengan Retrofit
Tujuan

Pada materi kali ini Anda akan belajar menggunakan Retrofit untuk
menampilkan dan mengirim data ke server. Aplikasi buatan Anda akan
terlihat seperti ini:

Alur Latihan

Berikut alur yang akan kita lakukan dalam latihan kali ini:

1. Membuat project baru dengan nama RestaurantReview.


2. Mengatur tampilan pada berkas layout XML.
3. Menambahkan library ke dalam project.
4. Membuat kelas RestaurantResponse untuk menampung sementara
data pengguna.
5. Membuat kelas ApiService dan ApiConfig untuk mengatur konfigurasi
Retrofit.
6. Mengimplementasikan Retrofit ke dalam Activity.
7. Menjalankan aplikasi.
Codelab Mengambil Data Review Restoran

Pada codelab latihan awal ini Anda akan menampilkan data restoran dan
review dengan menggunakan metode GET pada Retrofit.

1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:

Nama Project RestaurantReview


Target & Minimum Target Phone and Tablet, API
SDK level 21
Tipe Activity Empty Activity
Activity Name MainActivity
Use AndroidX Artifacts True
Language Kotlin/Java

2. Mari buat terlebih dahulu desain tampilan aplikasi seperti berikut


pada activity_main.xml:

1. <?xml version="1.0" encoding="utf-8"?>

2. <[Link]
xmlns:android="[Link]

3. xmlns:app="[Link]

4. xmlns:tools="[Link]

5. android:layout_width="match_parent"

6. android:layout_height="match_parent"

7. tools:context=".MainActivity">

8.

9. <ImageView

10. android:id="@+id/ivPicture"

11. android:layout_width="0dp"

12. android:layout_height="200dp"

13. android:contentDescription="@string/title"

14. android:scaleType="centerCrop"

15. android:visibility="visible"
16. app:layout_constraintEnd_toEndOf="parent"

17. app:layout_constraintStart_toStartOf="parent"

18. app:layout_constraintTop_toTopOf="parent"

19. tools:srcCompat="@tools:sample/backgrounds/scenic" />

20.

21. <TextView

22. android:id="@+id/tvTitle"

23. android:layout_width="match_parent"

24. android:layout_height="wrap_content"

25. android:layout_marginStart="16dp"

26. android:layout_marginTop="16dp"

27. android:layout_marginEnd="16dp"

28. android:text="@string/title"

29. android:textSize="18sp"

30. android:textStyle="bold"

31. android:visibility="visible"

32. app:layout_constraintEnd_toEndOf="parent"

33. app:layout_constraintStart_toStartOf="parent"

34. app:layout_constraintTop_toBottomOf="@+id/ivPicture" />

35.

36. <TextView

37. android:id="@+id/tvDescription"

38. android:layout_width="match_parent"

39. android:layout_height="wrap_content"

40. android:layout_marginStart="16dp"
41. android:layout_marginTop="8dp"

42. android:layout_marginEnd="16dp"

43. android:ellipsize="end"

44. android:maxLines="2"

45. android:text="@string/description"

46. android:visibility="visible"

47. app:layout_constraintEnd_toEndOf="parent"

48. app:layout_constraintStart_toStartOf="@+id/tvTitle"

49. app:layout_constraintTop_toBottomOf="@+id/tvTitle" />

50.

51. <TextView

52. android:id="@+id/tvReview"

53. android:layout_width="match_parent"

54. android:layout_height="wrap_content"

55. android:layout_marginStart="16dp"

56. android:layout_marginTop="20dp"

57. android:layout_marginEnd="16dp"

58. android:text="@string/write_your_review_here"

59. android:textSize="18sp"

60. android:textStyle="bold"

61. app:layout_constraintEnd_toEndOf="parent"

62. app:layout_constraintStart_toStartOf="parent"

63. app:layout_constraintTop_toBottomOf="@+id/tvDescription" />

64.

65. <ProgressBar
66. android:id="@+id/progressBar"

67. style="?android:attr/progressBarStyle"

68. android:layout_width="wrap_content"

69. android:layout_height="wrap_content"

70. android:visibility="gone"

71. app:layout_constraintBottom_toBottomOf="parent"

72. app:layout_constraintEnd_toEndOf="parent"

73. app:layout_constraintStart_toStartOf="parent"

74. app:layout_constraintTop_toTopOf="parent"

75. tools:visibility="visible" />

76.

77. <[Link]

78. android:id="@+id/inputLayout"

79. style="@style/[Link]"

80. android:layout_width="0dp"

81. android:layout_height="wrap_content"

82. android:layout_margin="16dp"

83. app:layout_constraintEnd_toEndOf="parent"

84. app:layout_constraintStart_toStartOf="parent"

85. app:layout_constraintTop_toBottomOf="@+id/tvReview">

86.

87. <[Link]

88. android:id="@+id/edReview"

89. android:layout_width="match_parent"

90. android:layout_height="wrap_content"
91. android:ems="10"

92. android:hint="@string/review" />

93. </[Link]>

94.

95. <Button

96. android:id="@+id/btnSend"

97. android:layout_width="wrap_content"

98. android:layout_height="wrap_content"

99. android:layout_marginTop="16dp"

100. android:layout_marginEnd="16dp"

101. android:layout_marginRight="16dp"

102. android:text="@string/send"

103. app:layout_constraintStart_toStartOf="@id/inputLayout"

104. app:layout_constraintTop_toBottomOf="@id/inputLayout" />

105.

106. <[Link]

107. android:id="@+id/rvReview"

108. android:layout_width="0dp"

109. android:layout_height="0dp"

110. android:layout_marginStart="16dp"

111. android:layout_marginEnd="16dp"

112. android:overScrollMode="never"

113. android:scrollbarSize="0dp"

114. app:layout_constraintBottom_toBottomOf="parent"

115. app:layout_constraintEnd_toEndOf="parent"
116. app:layout_constraintStart_toStartOf="parent"

117. app:layout_constraintTop_toBottomOf="@+id/btnSend" />

118. </[Link]>

Tambahkan juga untuk resource teksnya pada [Link] seperti


berikut:

1. <resources>

2. <string name="app_name">Restaurant Review</string>

3. <string name="title">Judul</string>

4. <string name="description">Description</string>

5. <string name="review">Review</string>

6. <string name="write_your_review_here">Write your review here..</string>

7. <string name="send">Kirim</string>

8. </resources>

Jalankan dulu aplikasi Anda dan pastikan tampilan sesuai seperti desain di
atas.

Di awal telah dijelaskan bahwa Anda akan membuat sebuah aplikasi yang
menampilkan data restoran beserta reviewnya. Di sini Anda akan
menggunakan data API dari [Link] Endpoint
yang dipanggil yaitu:
[Link] dengan
uewq1zg2zlskfw1e867 adalah salah satu id restoran yang dapat dilihat di
[Link]

Selanjutnya Anda akan menggunakan library Retrofit untuk mengakses


API tersebut. Untuk menambahkannya, buka berkas
[Link](Module: app). Kemudian tambahkan baris berikut:

 Kotlin
 Java

1. dependencies {

2. ...

3. implementation '[Link]:glide:4.11.0'

4. implementation '[Link].retrofit2:retrofit:2.9.0'
5. implementation "[Link].retrofit2:converter-gson:2.9.0"

6. implementation "[Link].okhttp3:logging-interceptor:4.9.0"

7. }

Sebelum melakukan sync, aktifkan terlebih dahulu ViewBinding dengan


menambahkan konfigurasi berikut:

1. ...

2. android {

3. ...

4. buildFeatures {

5. viewBinding true

6. }

7. }

8. ...

Setelah selesai, langsung saja klik Sync Now di pojok kanan atas Android Studio
untuk mengunduh library.

Selanjutnya Anda akan membuat kelas model untuk menampung respon dari
API. Supaya lebih mudah, kali ini Anda akan menggunakan plugin bernama
RoboPojoGenerator yang dapat Anda unduh melalui menu File → Setting →
Plugins. Pada tab Marketplace cari RoboPojoGenerator dan klik Install.
Setelah selesai di-install, RoboPojoGenerator siap digunakan.

Kemudian klik kanan pada package utama dan pilih New → Generate POJO
from JSON. Sebenarnya Anda bisa saja copy-paste semua kode JSON pada
[Link]
Namun karena Anda hanya akan menampilkan sebagian informasi yang
dibutuhkan saja, Anda dapat menghapus beberapa baris yang berhubungan
dengan city, address, dan menusehingga data yang dimasukkan cukup seperti
ini:

1. {

2. "error":false,

3. "message":"success",

4. "restaurant":{

5. "id":"uewq1zg2zlskfw1e867",

6. "name":"Kafein",

7. "description":"Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel


augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus.
Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero,
sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus
pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec
vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci
eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec
sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit
cursus nunc,",
8. "pictureId":"15",

9. "rating":4.6,

10. "customerReviews":[

11. {

12. "name":"Gilang",

13. "review":"Saya sangat suka menu malamnya!",

14. "date":"13 November 2019"

15. },

16. {

17. "name":"Ahmad",

18. "review":"Tempatnya bagus namun menurut saya masih sedikit mahal." ,

19. "date":"14 Agustus 2018"

20. }

21. ]

22. }

23. }

Selanjutnya jangan lupa untuk atur language sesuai dengan bahasa Anda, pilih
GSON pada Framework, dan isi pada Root object name dengan
RestaurantResponse. Lalu klik Generate maka akan tercipta class dengan isi
seperti berikut:

 Kotlin
 Java

Pastikan untuk hapus centang pada nullable fields juga.


 Kotlin
 Java

[Link]

1. data class RestaurantResponse(

2.

3. @field:SerializedName("restaurant")

4. val restaurant: Restaurant,

5.

6. @field:SerializedName("error")

7. val error: Boolean,

8.

9. @field:SerializedName("message")

10. val message: String

11. )

12.

13. data class Restaurant(

14.

15. @field:SerializedName("customerReviews")
16. val customerReviews: List<CustomerReviewsItem>,

17.

18. @field:SerializedName("pictureId")

19. val pictureId: String,

20.

21. @field:SerializedName("name")

22. val name: String,

23.

24. @field:SerializedName("rating")

25. val rating: Double,

26.

27. @field:SerializedName("description")

28. val description: String,

29.

30. @field:SerializedName("id")

31. val id: String

32. )

33.

34. data class CustomerReviewsItem(

35.

36. @field:SerializedName("date")

37. val date: String,

38.

39. @field:SerializedName("review")

40. val review: String,

41.
42. @field:SerializedName("name")

43. val name: String

44. )

Voila! Class yang berisi berbagai macam model untuk menampung data dari
JSON sudah tercipta dengan cepat dan mudah. Menarik, bukan?

Selanjutnya untuk persiapan Retrofit, buatlah interface baru dengan cara klik
kanan di package utama pada proyek aplikasi → New → Kotlin Class/Java
Class dan beri nama ApiService.

 Kotlin
 Java

1. import [Link]

2. import [Link].*

3.

4. interface ApiService {

5. @GET("detail/{id}")

6. fun getRestaurant(

7. @Path("id") id: String

8. ): Call<RestaurantResponse>

9. }

Selanjutnya, buat kelas baru dengan nama ApiConfig dengan cara klik kanan di
package utama pada proyek aplikasi → New → Kotlin Class/Java Class, lalu
tambahkan kode berikut untuk membuat konfigurasi Retrofit.

 Kotlin
 Java

1. class ApiConfig {

2. companion object{

3. fun getApiService(): ApiService {

4. val loggingInterceptor =
5. HttpLoggingInterceptor().setLevel([Link])

6. val client = [Link]()

7. .addInterceptor(loggingInterceptor)

8. .build()

9. val retrofit = [Link]()

10. .baseUrl("[Link]

11. .addConverterFactory([Link]())

12. .client(client)

13. .build()

14. return [Link](ApiService::[Link])

15. }

16. }

17. }

Kelas ini akan membuat kode Anda menjadi lebih efektif karena Anda tidak perlu
membuat konfigurasi Retrofit baru setiap kali membutuhkannya, tetapi cukup
memanggil fungsi yang ada di dalam class ini saja.

Sebelum melangkah lebih jauh, buatlah terlebih dahulu layout untuk item
RecyclerView dengan cara klik kanan pada folder layout → New → Layout
resource file dan beri nama item_review. Lalu buat desain seperti berikut:

1. <?xml version="1.0" encoding="utf-8"?>

2. <TextView xmlns:android="[Link]

3. xmlns:tools="[Link]

4. android:id="@+id/tvItem"

5. android:layout_width="match_parent"

6. android:layout_height="wrap_content"

7. android:gravity="center_vertical"

8. android:paddingTop="8dp"
9. android:paddingBottom="8dp"

10. android:textAppearance="?android:attr/textAppearanceListItemSmall"

11. tools:text="Review" />

Kemudian buat class baru untuk adapter RecyclerView dengan nama


ReviewAdapter dan tuliskan kode berikut:

 Kotlin
 Java

1. class ReviewAdapter(private val listReview: List<String>) :


[Link]<[Link]>() {

2. override fun onCreateViewHolder(viewGroup: ViewGroup, viewType:


Int) =

3.
ViewHolder([Link]([Link]).inflate([Link]
m_review, viewGroup, false))

4.

5. override fun onBindViewHolder(viewHolder: ViewHolder, position:


Int) {

6. [Link] = listReview[position]

7. }

8.

9. override fun getItemCount() = [Link]

10.

11. class ViewHolder(view: View) : [Link](view) {

12. val tvItem: TextView = [Link]([Link])

13. }

14. }

Kemudian bagian utamanya adalah proses mengambil data dengan Retrofit dan
menampilkannya menggunakan RecyclerView di MainActivity. Berikut adalah
caranya:

 Kotlin
 Java

1. class MainActivity : AppCompatActivity() {

2.

3. private lateinit var binding: ActivityMainBinding

4.

5. companion object {

6. private const val TAG = "MainActivity"

7. private const val RESTAURANT_ID = "uewq1zg2zlskfw1e867"

8. }

9.

10. override fun onCreate(savedInstanceState: Bundle?) {

11. [Link](savedInstanceState)

12. binding = [Link](layoutInflater)

13. setContentView([Link])

14.

15. supportActionBar?.hide()

16.

17. val layoutManager = LinearLayoutManager(this)

18. [Link] = layoutManager

19. val itemDecoration = DividerItemDecoration(this,


[Link])

20. [Link](itemDecoration)

21.

22. findRestaurant()

23. }

24.
25. private fun findRestaurant() {

26. showLoading(true)

27. val client =


[Link]().getRestaurant(RESTAURANT_ID)

28. [Link](object : Callback<RestaurantResponse> {

29. override fun onResponse(

30. call: Call<RestaurantResponse>,

31. response: Response<RestaurantResponse>

32. ){

33. showLoading(false)

34. if ([Link]) {

35. val responseBody = [Link]()

36. if (responseBody != null) {

37. setRestaurantData([Link])

38.
setReviewData([Link])

39. }

40. } else {

41. Log.e(TAG, "onFailure: ${[Link]()}")

42. }

43. }

44.

45. override fun onFailure(call: Call<RestaurantResponse>, t:


Throwable) {

46. showLoading(false)

47. Log.e(TAG, "onFailure: ${[Link]}")

48. }
49. })

50. }

51.

52. private fun setRestaurantData(restaurant: Restaurant) {

53. [Link] = [Link]

54. [Link] = [Link]

55. [Link](this@MainActivity)

56. .load("[Link]
{[Link]}")

57. .into([Link])

58. }

59.

60. private fun setReviewData(consumerReviews:


List<CustomerReviewsItem>) {

61. val listReview = ArrayList<String>()

62. for (review in consumerReviews) {

63. [Link](

64. """

65. ${[Link]}

66. - ${[Link]}

67. """.trimIndent()

68. )

69. }

70. val adapter = ReviewAdapter(listReview)

71. [Link] = adapter

72. [Link]("")
73. }

74.

75. private fun showLoading(isLoading: Boolean) {

76. if (isLoading) {

77. [Link] = [Link]

78. } else {

79. [Link] = [Link]

80. }

81. }

82. }

Terakhir, tambahkan permission seperti berikut pada


[Link] karena Anda membutuhkan akses internet.

1. <?xml version="1.0" encoding="utf-8"?>

2. <manifest xmlns:android="[Link]

3. package="[Link]">

4.

5. <uses-permission
android:name="[Link]"/>

6.

7. <application

8. ...

9. </application>

10. </manifest>
12. Selesai! Sekarang jalankan aplikasinya. Maka data restoran dan
review akan tampil seperti contoh di bawah ini:
13. Yeay. Anda sudah dapat mengambil data dari API dengan
menggunakan Retrofit. Mantap!

Codelab Mengirim Data Review

Nah, pada latihan kali ini Anda akan mengirimkan data review dengan
menggunakan metode @POST pada Retrofit. Sudah tidak sabar kan? Yuk,
kita lanjut!

1. Untuk mengirim review, Anda dapat melihat dokumentasi pada


[Link] dan mencoba pada postman terlebih
dahulu sebelum mengimplementasikan ke Android. Berikut adalah
contohnya:
Dari sini kita tahu bagaimana model response setelah berhasil mengirim
review.

2. Selanjutnya, tambahkan dulu model untuk menerima respon ketika


mengirim data seperti yang kita lihat pada postman dengan cara seperti
ini:

o Kotlin
o Java

Tambahkan kode pada RestaurantResponse:

1. ...

2.

3. data class PostReviewResponse(

4.

5. @field:SerializedName("customerReviews")

6. val customerReviews: List<CustomerReviewsItem>,

7.

8. @field:SerializedName("error")

9. val error: Boolean,


10.

11. @field:SerializedName("message")

12. val message: String

13. )

Selanjutnya, tambahkan fungsi baru pada ApiService untuk mengirim data


dengan menggunakan kode berikut:

 Kotlin
 Java

1.  interface ApiService {

2. @GET("detail/{id}")

3. fun getRestaurant(

4. @Path("id") id: String

5. ): Call<RestaurantResponse>

6.

7. @FormUrlEncoded

8. @Headers("Authorization: token 12345")

9. @POST("review")

10. fun postReview(

11. @Field("id") id: String,

12. @Field("name") name: String,

13. @Field("review") review: String

14. ): Call<PostReviewResponse>

15. }

Kemudian tambahkan fungsi untuk mengirim review ketika tombol kirim


diklik pada MainActivity seperti ini:

 Kotlin
 Java
1. class MainActivity : AppCompatActivity() {

2. ...

3. override fun onCreate(savedInstanceState: Bundle?) {

4. [Link](savedInstanceState)

5. binding = [Link](layoutInflater)

6. setContentView([Link])

7. ...

8. findRestaurant()

9.

10. [Link] { view ->

11. postReview([Link]())

12. val imm =


getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager

13. [Link]([Link], 0)

14. }

15. }

16.

17. ...

18.

19. private fun postReview(review: String) {

20. showLoading(true)

21. val client =


[Link]().postReview(RESTAURANT_ID,
"Dicoding", review)

22. [Link](object : Callback<PostReviewResponse> {

23. override fun onResponse(


24. call: Call<PostReviewResponse>,

25. response: Response<PostReviewResponse>

26. ){

27. showLoading(false)

28. val responseBody = [Link]()

29. if ([Link] && responseBody != null) {

30. setReviewData([Link])

31. } else {

32. Log.e(TAG, "onFailure: ${[Link]()}")

33. }

34. }

35.

36. override fun onFailure(call: Call<PostReviewResponse>,


t: Throwable) {

37. showLoading(false)

38. Log.e(TAG, "onFailure: ${[Link]}")

39. }

40. })

41. }

42. }
4. Jalankan kembali aplikasi dan cobalah untuk menulis review. Setelahnya,
jangan lupa klik tombol Kirim ya!
5. Yeay, data review berhasil dikirim ke server dan berhasil ditampilkan.

Selamat! Anda telah mempelajari sesuatu yang keren. Tidak hanya belajar
bagaimana mengambil data dengan Retrofit, namun juga mengirimkan
data dengan Authentication ke server. Keren!

Bedah Kode
Retrofit Model

Dengan menggunakan RoboPojoGenerator, Anda telah membuat class


model yang berfungsi untuk menampung data dari JSON. 3 class yang
dibuat merepresentasikan kedalaman object yang diambil. Untuk lebih
jelasnya lihatlah gambar ini:
Class RestaurantResponse untuk mengambil data respon dari server
seperti eror dan message. Kemudian class Restaurant untuk mengambil
JSON Object restaurant; class CustomerReviewItem untuk mengambil
JSON Array customerReviews. Untuk membedakan antara JSONObject dan
JSONArray, cukup gunakan List<CustomerReviewsItem> untuk
JSONArray; dan Restaurant tanpa List untuk JSONObject.

Kemudian untuk menandai sebuah variabel terhubung dengan data JSON,


gunakan annotation @SerializedName seperti berikut:

 Kotlin
 Java

1. @field:SerializedName("rating")

2. val rating: Double,

Pastikan pula tipe data yang digunakan disesuaikan dengan data yang
diambil. Misal data 4.6 maka sebaiknya menggunakan tipe Double. Jika
Anda menggunakan tipe Integer maka akan mencetuskan eror saat
dikonversi.

Catatan:
Saat menggunakan RoboPojoGenerator, tipe data akan ditentukan
berdasarkan contoh response yang dimasukkan. Apabila pada contoh
tersebut rating berisi angka 4, maka RoboPojoGenerator akan
mendeteksinya sebagai Integer. Maka berhati-hatilah dan cek terlebih
dahulu karena bisa jadi pada rating restoran lain berisi 4.6 yang
seharusnya Double.

ApiConfig

Pada latihan ini, Anda membuat sebuah class yang berfungsi untuk
membuat dan mengkonfigurasi Retrofit dengan nama ApiConfig.

 Kotlin
 Java

1. class ApiConfig {

2. companion object{

3. fun getApiService(): ApiService {

4. val loggingInterceptor =

5. HttpLoggingInterceptor().setLevel([Link])

6. val client = [Link]()

7. .addInterceptor(loggingInterceptor)

8. .build()

9. val retrofit = [Link]()

10. .baseUrl("[Link]

11. .addConverterFactory([Link]())

12. .client(client)

13. .build()

14. return [Link](ApiService::[Link])

15. }

16. }

17. }
Dengan membuat class ini, Anda tidak perlu menyiapkan Retrofit pada
setiap class yang akan menggunakan Retrofit. Anda hanya cukup
memanggil [Link](). Cukup simpel daripada Anda harus
menulis berulang-ulang bukan? Mantap. Ingat selalu prinsip “DRY” ya!
Don’t repeat yourself.

Catatan:
Anda juga dapat menggunakan class ini untuk keperluan API lain dengan
mengganti kode pada base URL dan nama service-nya.

API Service

API Service merupakan interface yang berisi kumpulan endpoint yang


digunakan pada sebuah aplikasi.

 Kotlin
 Java

1. interface ApiService {

2. @GET("detail/{id}")

3. fun getRestaurant(

4. @Path("id") id: String

5. ): Call<RestaurantResponse>

6.

7. @FormUrlEncoded

8. @Headers("Authorization: token 12345")

9. @POST("review")

10. fun postReview(

11. @Field("id") id: String,

12. @Field("name") name: String,

13. @Field("review") review: String

14. ): Call<PostReviewResponse>

15. }
Pada aplikasi ini terdapat 2 endpoint, yakni fungsi getRestaurant dengan
annotation @GET untuk mengambil data. Anda dapat mengganti variable
{id} pada endpoint dengan menggunakan @Path sehingga dapat
mengakses detail suatu restoran dengan URL [Link]
[Link]/detail/uewq1zg2zlskfw1e867.

Lalu, yang kedua adalah fungsi postReview dengan annotation @POST


untuk mengirim data. Selain itu, Anda juga dapat menambahkan @Header
untuk menyematkan token jika API tersebut membutuhkan otorisasi.
Kemudian, Anda harus memakai anotasi @FormUrlEncoded untuk
mengirimkan data dengan menggunakan @Field. Pastikan key yang
dimasukkan pada @Field harus sama dengan field yang ada pada API. Jika
tidak sama, data tidak akan terkirim.

Request API

Setelah persiapan Rtrofit sudah selesai, Anda dapat memanggil endpoint


yang ada pada API Service seperti berikut:

 Kotlin
 Java

1. val client = [Link]().getRestaurant(RESTAURANT_ID)

2. [Link](object : Callback<RestaurantResponse> {

3. override fun onResponse(

4. call: Call<RestaurantResponse>,

5. response: Response<RestaurantResponse>

6. ){

7. showLoading(false)

8. if ([Link]) {

9. val responseBody = [Link]()

10. if (responseBody != null) {

11. setRestaurantData([Link])

12. setReviewData([Link])

13. }

14. } else {
15. Log.e(TAG, "onFailure: ${[Link]()}")

16. }

17. }

18.

19. override fun onFailure(call: Call<RestaurantResponse>, t: Throwable) {

20. showLoading(false)

21. Log.e(TAG, "onFailure: ${[Link]}")

22. }

23. })

Di sini kita menggunakan fungsi enqueue untuk menjalankan


request secara asynchronous di background. Sehingga aplikasi tidak
freeze/lag ketika melakukan request. Kemudian, hasilnya terdapat dua
callback, yakni onResponse ketika ada respon, dan onFailure ketika gagal.
Ketika terdapat respon, bisa jadi terjadi kegagalan seperti eror 404 atau
500, maka untuk mendapatkan data ketika berhasil, kita mengeceknya
melalui [Link]() untuk mengetahui apakah server
mengembalikan kode 200 (OK) atau tidak. Untuk datanya sendiri dapat
diambil di [Link]().

Seperti yang Anda lihat, di sini kita tidak perlu melakukan parsing lagi,
karena data yang didapat sudah berupa POJO. Proses parsing dilakukan
secara otomatis oleh Retrofit dengan menggunakan kode
.addConverterFactory([Link]()) di bagian ApiConfig
dan anotasi SerializedName pada masing-masing POJO.

Selamat Anda sudah belajar tentang bagaimana Retrofit bekerja, baik


untuk melakukan request data dan mengirim data ke API. Pahami baik-
baik dan baca ulang jika kurang mengerti ya. Poin bahasan materi kali ini
tak kalah pentingnya untuk Anda. Semangat!

Silakan unduh proyek di atas pada tautan berikut:

 Source Code Latihan Retrofit

Anda mungkin juga menyukai