Android ViewModel Dan LiveData


VIEW MODEL
Secara konsep ViewModel merupakan class yang dibuat untuk menghandle logical bussines dan mempertahankan dari secara continues dalam suatu lifecycle (ex : activity / fragment)

Benefit ViewModel
- Lifecycle Aware untuk menghindari NPE ketika terjadi perubahan UI komponent pada Activity / Fragment, dimana Activity / Fragment bisa saja sudah tidak exist atau ter-destroy
- Bisa mempertahankan data ketika terjadi perubahan cofigurasi pada Activity / Fragment, misalnya rotasi layar yang mengakibatkan Activity / Fragment akan ter-create ulang.

Activity Lifecycle when screen rotated 
onPause
onStop
onSaveInstanceState
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume

Configuration changes
rotate screen
dark mode
change language

ViewModelProvider merupakan class yang berfungsi dalam menginisiasi ViewModel dan menjembatani agar ViewModel memilik kemampuan lifecycle aware

activity-ktx
implementation "androidx.activity:activity-ktx:$activity_version"
menyediakan delegasi inisiasi ViewModel dengan keyword by viewmodels()
Contoh : private val awesomeViewModel : AwesomeViewModel by viewmodels()

Shared ViewModel jika kita ingin menggunakan viewmodel pada beberapa fragment dalam 1 activity, kita bisa init 1 viewmodel yang sama yang akan digunakan pada activity dan beberapa fragment, dengan lifecycle yang terikat pada activity.
Contoh inisialisasi

Avoid android / Context
Jangan menyimpan apapun terkait class Context dalam ViewModel contohnya TextView dsb.
  1. Karena ketika terjadi perubahan configurasi misalnya rotasi screen TextView dsb pada activity akan di inisiasi ulang, sedangkan TextView dsb yang ada pada ViewModel tidak akan di inisiasi ulang yang akan mengakibatkan perbedaan instance pada TextView dsb
  2. Proses unit testing akan lebih mudah tanpa adanya class yang mewarisi class Context, sehingga unti test dapat di run hanya dengan JUnit tanpa perlu tambahan library seperti Roboelectric
LIVE DATA
Merupakan class holder data yang dapat diamati / observerable dan lifecycle friendly

Observable
An observable is an object which notifies observers about the changes in its state.
observable = dapat diamati
observers = pengamat

Istilah lainnya
publisher / emitter = pengirim / pemancar
subcriber = pelanggan

Active Lifecycle = onStart & onResume
LiveData menganggap observer berada dalam status aktif jika lifecyle-nya dalam status STARTED atau RESUMED.

setValue vs postValue

setValue():
Sets the value. If there are active observers, the value will be dispatched to them. This method must be called from the main thread.

postValue():
Posts a task to a main thread to set the given value. If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.

Tips

1. Jangan gunakan fragment sebagai lifecycle owner, gunakanlah viewLifecycleOwner.
Karena dibeberapa kasus hanya view-nya yang ter-destroy tapi fragmentnya tidak, sehingga memungkinkan ter-init observer yang sama lebih dari 1 kali.

2. Don't exposing MutableLiveData, perlu dibuat private agar perubahan livedata hanya terjadi di viewmodel.
Perlu membuat 2 type live data 1 mutable 1 lagi imutable, lalu view observe ke imutable object agar tidak bisa melakukan perubahan state pada view (setValue / postValue).

3. Don't put observer at event handling, karena bisa ter-init lebih dari sekali

My Personal Doc About GIT

  1. clone = clone remote repository / server to local machine / laptop

  2. fetch = fetch updated history / git tree dari suatu branch

  3. pull = untuk mengupdate perubahan code dalam suatu branch

  4. commit = untuk submit perubahan code dalam suatu branch dilocal machine / laptop

  5. push = untuk submit perubahan code dalam suatu branch ke remote repository / server

  6. ammend = mengedit commit sebelumnya dalam suatu branch, tujuannya untuk efesiensi commit

  7. squash commit = menjadikan multiple commit menjadi single commit, biasanya ketika merging ke main branch

  8. merge = menggabungkan perubahan yang ada pada suatu branch ke branch yang kita inginkan

  9. Sample commit message
    feat : login
    - revamp ui login
    - close JIRA-XXXX

  10. git flow
    - main branch is a release branch / production code (prod and sandbox), use main branch as sandbox branch, sandbox sebisa mungkin dibuat mirip dengan production
    - development branch is for staging (dev / integ)
    - untuk flow sederhana, ketika akan mengerjakan fitur baru atau bug fixing, selalu buat branch baru dari main branch contoh branch feat-abc
    - setelah melakukan commit perubahan pada branch feat-abc, maka kita perlu melakukan cherry pick ke development branch, dan build aplikasi staging (dev / integ) dari development branch.
    Tujuannya untuk apa? agar semua perubahan code dari semua developer misal ada 10 developer ada dalam 1 branch sehingga tidak ada code yang tertinggal ketika QA melakukan testing.
    - setelah proses testing pada staging (dev / integ) selesai maka kita melakukan pull request untuk merge branch feat-abc ke branch main, setelah itu build aplikasi untuk stage sandbox dari branch main untuk di test QA.
    - jika proses testing di stage sandbox sudah selesai maka selanjutnya build aplikasi production dari branch main untuk dilakukan test terakhir oleh QA.
    - Jika test terakhir sudah selesai maka aplikasi di release ke production (BE server production / Mobile ke play store / app store)


Intent Pada Android


Intent adalah sebuah messaging Object yang dapat digunakan untuk melakukan / meminta action dari componen aplikasi lainnya (Activity Lain, Service dll) 

Intent Explicit (Jelas)
Dimana kita secara explicit menentukan target / component tujuan dari event intent, membuka spesifik halaman memulai atau mengakhiri service dll.

Intent Implicit (Samar / Tidak Jelas)
Dimana kita tidak menentukan target / componnet tujuan dari intent, tapi kita hanya menggunakan Action yang memungkinkan action di handle oleh component / aplikasi lain.
Contohnya Intent.ACTION_VIEW untuk melihat dokument pdf bisa di handle oleh aplikasi Gdrive atau aplikasi pembace pdf lainnya

Intent Filter (Penyaringan Intent)
Ketika melakukan intent implicit maka system akan memunculkan pilihan aplikasi yang memiliki intent filter yang sesuai dengan action dari implicit intent.

Intetn filter ini di deklarasikan pada Manifest aplikasi, tujuannya untuk memfilter action apa saja yang bisa di trigger oleh aplikasi lain, setidaknya setiap aplikasi memiliki 1 intent filter yaitu Intent action MAIN category LAUNCHER, untuk mengahandle event intent implicit dari icon aplikasi yang ada pada halaman home device / OS.



SOLID Principle on Android App Development

  1. Single Responsibility
    • Each software module should have one and only one reason to change.
    • Suatu class atau method diusahakan hanya memiliki satu fungsi / tanggungjawab
    • Tujuannya untuk mempermudah difahami dan mempermudah proses pengembangan, mengurangi resiko senggolan karena codenya lebih terisolasi.
    • Contohnya penggunaan pattern MVP (Model View Presenter), dimana model class bertanggung jawab untuk menyimpan data, Presenter untuk mengolah data yang ada pada model, misalnya dengan melakukan validasi dan kalkulasi, View bertugas untuk menampilkan data yang telah diolah oleh presenter
  2. Open Close
    • Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
    • Open for extension : class atau method mudah untuk di-extend, dibuat subclass baru yang mewarisi property & method class parent-nya.
    • Close for modification : class atau method parentnya tidak harus mengalami perubahan.
    • Contohnya TextView dan ImageView
      • Dua View yang berbeda, dimana ImageView memiliki method untuk mengolah gambar seperti setScaleType sedangkan TextView tidak ada, meskipun keduanya extend dari class yang sama yaitu class View (Open for extension)
      • Keduanya mewarisi property dan method yang sama yang ada pada class View seperti setWidth, setHeight tanpa perlu merubahnya / tidak perlu perubahan di class parent-nya (Close for modification)
  3. Liskov Subtitution
    • Subtypes must be substitutable for their base type.
    • Subtipe harus dapat diganti dengan tipe dasarnya.
    • Prinsip agar sub-class dapat digantikan perannya oleh parent-class tanpa merusaknya
    • Teknik yang biasa digunakan adalah dengan menambah 1 class baru untuk menggrouping class yang sesuai dengan behaviournya
    • Tanpa Menggunakan LSP
    • Dengan Menggunakan LSP
    • Contoh di android
    • TextView extend View
    • LinearLayout extend ViewGroup dimana ViewGroup extend View
    • Sehingga ketika melakukan pengelompokan berdasarkan class View, maka hanya bisa mengakses property dan method yang ada pada class View, dan ketika misalnya melakukan perubahan warna background tidak akan merusak program.
    • Ketika melakukan pengelompokan berdasarkan ViewGroup, maka class TextView tidak dapat dimasukan kedalam kelompok ViewGroup, dan akan menampilkan error pada saat compile time, sehingga program akan lebih aman pada saat run time
    • Contoh kasus misalnya countChildView yang ada dalam ViewGroup, hanya class yang extend dari ViewGroup saja yang bisa, sedangkan yang lainnya seperti TextView tidak bisa
  1. Interface Segregation
    • Segregation artinya pemisahan
    • Interface segregation adalah konsep untuk memisahkan client class dari interface yang tidak diperlukan sehingga interface tersebut perlu dipisahkan kedalam interface baru yang lebih kecil
    • misalnya class Phone memiliki interface brandName, typeName, color, dan flipDirection
    • Karena tidak semua handphone dapat dilipat, maka kita perlu memisahkan interface untuk flipDirection, yang hanya akan di implement pada handphone yang dapat dilipat saja FlipPhone
    • Contoh lainnya misalnya kita membuat dialog confirmation dengan 2 buah button positive & negative action, dimana negative button bersifat optional show / hide. Maka kita memerlukan interface untuk masing-masing button, jangan dibuat dalam 1 interface.
  2. Dependency Inversion
    • Dependency artinya ketergantungan
    • Inversion / invers artinya pembalikan
    • High-level modules should not depend on low-level modules. Both should depend on abstraction
    • High level module = consumer
    • Low level module = core
    • Do not create new Object on high level / consumer module, but use an interface for abstraction
    • Tujuannya agar tidak terlalu terikat / less couple, jika suatu waktu nanti perlu pergantian core atau low level module tidak perlu banyak perubahan. 
    • Misalnya mengganti networking library dari Volley ke OkHttp.
      Kita bisa membuat interface baru untuk capture event onSuccess & onError, yang akan menghubungkan networking library yang kita gunakan dengan high level module di aplikasi kita misalnya class Presenter
    • https://javatechonline.com/solid-principles-the-dependency-inversion-principle/

Basic OOP


Pada konsep Object Oriented Programming / OOP variable dan fungsi program akan dibungkus / dikelompokan dalam suatu Object atau Class

Contoh class / object
public class Human {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void attack() {
        System.out.println(getName() + " Kick");    
    }
}
Contoh Penggunaan class / object
Human human = new Human();
human.setName("Jhon");
human.attack();

  • Inheritance 
    • Kita bisa membuat sub class yang mewarisi variable dan fungsi dari parent class
    • Di Java menggunakan keyword extend
    • Contoh Inheritance
      public class SuperHero extend Human {
          private String power;

          public void setPower(String power) {
              this.power = power;
          }

          public String getPower() {
              return this.power;
          }
         
          @Override
          public void attack() {
              System.out.println(getName() + " " + getPower());    
          }
      }
    • Contoh Penggunaan
      SuperHero hero = new SuperHero();
      hero.setName("Maulana");
      hero.setPower("Fire Kick");
      hero.attack();
  • Encapsulation
    • Pembungkusan variable pada suatu class agar tidak bisa sembarangan diakses dari luar classnya
    • Caranya dengan menambahkan akses modifier private pada variable, lalu membuat public method getter dan setter untuk variable tersebut agar bisa diakses dari class yang lain.
  • Polymorphism
    • Poly artinya banyak, Morphism artinya bentuk, jadi Polymorphism artinya banyak bentuk
    • Polimorfisme dinamis menggunakan method overriding, misalnya dengan membuat method didalam sebuah subclass dimana mengoverride / menindih method yang ada di parent classnya. Contohnya method attach diatas.
    • Polimorfisme statis menggunakan cara overloading, dimana nama method / fungsinya sama tapi memiliki type parameter yang berbeda
    • Contoh Overloading
    • class Lingkaran {
      
          // method menghitung luas dengan jari-jari
          float luas(float r){
              return (float) (Math.PI * r * r);
          }
      
          // method menghitung luas dengan diameter
          double luas(double d){
              return (double) (1/4 * Math.PI * d);
          }
      
      }
  • Abstrack Class
    • Class yang masih abstrack sehingga tidak bisa langsung digunakan, seperti class Human atau SuperHero diatas.
    • Abcstract class harus memiliki minimal 1 method abtract
    • // ini abstrak method
      void sayHello();
      
      // ini bukan abstrak method karena
      // punya implementasi di body method
      void greeting(){
        System.out.println("Hello Java");
      }
    • Agar sebuah abstract class dapat digunakan maka perlu membuat subclass yang mengimplementasikan abstract method dari parentnya
    • public abstract class Shape {
          
          String color;
          
          void setColor(String color){
              this.color = color;
          }
          
          String getColor(){
              return this.color;
          }
          
          abstract float getArea();
      }

    • public class Triangle extends Shape {
      
          private float base;
          private float height;
      
          public Triangle(int base, int height) {
              this.base = base;
              this.height = height;
          }
      
          
          @Override
          float getArea() {
              return 0.5f * base * height;
          }
          
      }