"ความรักต้องไม่พยายาม"
รู้จัก Android Jetpack แบบจัดเต็ม เครื่องมือเร่งการพัฒนาแอป ฯ แอนดรอยด์ให้เร็วและโค้ดสวยงาม
9 May 2018 16:18   [112087 views]

บทความเกี่ยวกับแอนดรอยด์บทความแรกในรอบปีกว่า ๆ หลังจากห่างหายไปนานมาก วันนี้ขอกลับมาในเรื่องที่เราแอบรู้สึกว่าน่าสนใจมากในงาน Google I/O 2018 อย่าง "Android Jetpack" ซึ่งน่าจะเป็นหนึ่งในเครื่องมือใหญ่ที่เปลี่ยนโลกการพัฒนาแอป ฯ แอนดรอยด์ไปสู่อีกยุคนึงได้เลยทีเดียว

คำว่า Jetpack คืออะไร ? ถ้าใครเคยใช้ Wordpress มาก่อนอาจจะคุ้นเคยกับคำนี้อยู่แล้ว เพราะมันคือปลั๊กอินที่ "มัดรวมเครื่องมือที่ทรงพลังเข้าด้วยกัน" เพื่อทำให้การพัฒนาทุกอย่างเป็นไปได้อย่างรวดเร็ว ประมาณว่าลง Jetpack ไปตัวเดียว Wordpress ตัวนั้นก็แทบจะมีทุกอย่างพร้อมใช้งานแล้ว

ในแอนดรอยด์ล่าสุดก็เลยมีการมัดรวมสิ่งที่สำคัญต่อการพัฒนาแอป ฯ ดี ๆ ตัวนึงเข้าด้วยกันซะเลย แล้วก็นำเสนอออกมาเป็นทีมฮีโร่ที่เอาไว้กู้โลกแอนดรอยด์ โดยใช้นามว่า "Android Jetpack"

แต่ถามว่ามันเป็นการรวมแบบ เพิ่มไลบรารี่ตัวเดียวแล้วจบใช้ได้ทุกอย่างเลยหรือเปล่า ? คำตอบคือ ไม่ใช่นะครับ เพราะ

Android Jetpack จริง ๆ แล้วเป็นเพียงเหมือนการรวมทีม Avengers แห่งโลก Android Development อัญเชิญไลบรารี่ต่าง ๆ มามัดรวมเข้าด้วยกันเพื่อให้สื่อสารง่ายขึ้นซะมากกว่า

การใช้งานโดยรวมยังเหมือนเดิม คืออยากใช้ไลบรารี่ตัวไหนก็ต้องเพิ่มเข้ามาทีละตัว วิธีการเรียกใช้งานในแต่ละไลบรารี่ก็เหมือนเดิม ไม่มีอะไรต่างออกไป เพียงแต่ทีมแอนดรอยด์แนะนำว่าใช้ไลบรารี่พวกนี้เถอะ เราแนะนำนะ มันดี มันเจ๋งนะ เรารวมมาให้แล้ว มีทุกอย่างให้ใช้ครบถ้วนในการทำแอป ฯ ดี ๆ ขึ้นมาตัวนึงแล้ว

นี่แหละครับ Android Jetpack ซึ่งก็จะเห็นว่ามันมีไลบรารี่ที่เกี่ยวข้องอยู่เพียบ บางตัวคนในสายงานแอนดรอยด์ก็น่าจะรู้จักกันอยู่แล้ว เช่น AppCompat (ใครไม่รู้จักคือบาปมาก) บางตัวเป็นของที่มีมากับแอนดรอยด์เลยอย่าง Fragment (เป็นการบอกเป็นนัยว่า Fragment นั้นสำคัญขนาดไหน) และก็มีไลบรารี่ตัวใหม่เพิ่มขึ้นมาเพื่อให้ทุกอย่างครบถ้วนสมบูรณ์ด้วย เช่น Android KTX, Navigation ฯลฯ ครับ

ในบล็อกนี้ก็จะพามารู้จักทีละตัวเลยละกันว่าแต่ละตัวใน Android Jetpack คืออะไร มีหน้าที่อะไร และมีความสำคัญยังไงในการพัฒนาแอป ฯ แอนดรอยด์ครับ

และแน่นอน บล็อกนี้

#GeekAlert

นะครัช

งั้นมา ชาว Dev เริ่มลุยกันเลย !

จุดประสงค์ของ Android Jetpack

มิชชั่นของ Android Jetpack มีอยู่ 3 ประการด้วยกัน ได้แก่

1) Accelerate development

ทำให้การพัฒนาแอป ฯ แอนดรอยด์ทำได้รวดเร็วขึ้น

2) Eliminate boilerplate code

กำจัดโค้ดที่ไม่จำเป็นทิ้ง โค้ดสั้นลง อ่านง่ายขึ้น เกิดความผิดพลาดน้อยลง

3) Build high quality, robust apps

สร้างแอป ฯ ที่มีคุณภาพและเสถียร

เดี๋ยวก็จะได้เห็นว่าไลบรารี่ต่าง ๆ ที่ถูกนำมามัดรวมใน Jetpack นี้ล้วนตอบโจทย์สามข้อนี้ทั้งสิ้นครับ =)

Android Jetpack ถูกสร้างโดยใช้ Kotlin เป็นฐาน

อีกหนึ่งอย่างที่ต้องรู้ไว้เป็นพื้นก่อนจะไปต่อคือ Android Jetpack ถูกสร้างโดยใช้ Kotlin เป็นฐาน ไม่ใช่ Java ดังนั้นไลบรารี่หลาย ๆ ตัวที่ทำให้การพัฒนาทำได้ง่ายขึ้นหรือโค้ดสั้นลงก็จะมาในรูปแบบ Kotlin ทิ้งสิ้น หากใครยังเขียน Kotlin ไม่เป็นให้เริ่มฝึกตั้งแต่วันนี้นะครับ เพราะหลังจากนี้ทุกอย่างที่ทีมพัฒนาแอนดรอยด์ทำออกมาจะ Base บน Kotlin เป็นหลักแล้ว

Android Jetpack แบ่งเป็น 4 หมวด

ขอลากภาพด้านบนมาแปะอีกรอบ

ดูจากภาพด้านบนนี้ก็จะเห็นว่า Android Jetpack ถูกแบ่งออกมาเป็น 4 หมวดหมู่ด้วยกัน ได้แก่

Foundation - เป็นกลุ่มที่เป็น Core หลักในระดับพื้นฐานของการพัฒนาแอป ฯ แอนดรอยด์

Architecture - เป็นกลุ่มไลบรารี่ที่เอาไว้รองรับการทำงานของ Android Architecture เช่น การบริหาร Lifecycle รวมถึงการแบ่งส่วนของโค้ดเพื่อให้โครงสร้างของโค้ดมีระเบียบสวยงาม

Behavior - เป็นกลุ่มไลบรารี่ที่เรียกใช้งานฟังก์ชันเสริมที่จำเป็นกับแอนดรอยด์เช่น การเล่นเพลง ระบบ Notifications ปุ่มแชร์ หรือระบบ Permission เป็นต้น

UI - ก็ตรงตัว เอาไว้จัดการเรื่องหน้าตา (User Interface) ให้ดูสวยงาม ใช้งานง่าย ไม่ว่าจะบนมือถือ ทีวี หรือกระทั่ง Wearable ต่าง ๆ

ก็จะมีไลบรารี่ทั้งตัวเก่าและตัวใหม่ตามภาพด้านบนเลย ตัวที่ใหม่ก็จะเขียนไว้ว่า new! ซึ่งก็มีอยู่แค่ 5 ตัวแหละ แต่จะเก่าจะใหม่ก็สำคัญหมด งั้นเรามาเจาะกันทีละตัวเลยละกันนะ

Foundation

กลุ่ม Core หลักของการพัฒนาแอป ฯ แอนดรอยด์ มีอยู่ 4 ตัวด้วยกัน

AppCompat

อันนี้ก็คือ AppCompat v7 ที่เราใช้กันมาตลอดชีพการพัฒนาแอป ฯ แอนดรอยด์นั่นแหละ ทีมแอนดรอยด์จับมาใส่ Jetpack เพราะอยากจะบอกว่าใช้เถอะ มันเป็น Core หลักของแอป ฯ ใด ๆ เลยนะ

ก็เอาเป็นว่าถ้าใครยังฝืนใช้ Activity ที่ไม่ใช่ AppCompat อยู่ให้เปลี่ยนเถอะ นี่เค้าแนะนำกันขนาดนี้แล้วนะ ! (แต่ไม่น่าจะมีใครทำแบบนั้นอยู่แล้วป่ะ)

Android KTX

ไลบรารี่นี้เป็นการต่อยอดขึ้นมาจาก Kotlin และทำมาเพื่อแอนดรอยด์โดยเฉพาะ โดยมีจุดประสงค์หลักคือ ช่วยกำจัดโค้ดที่ไม่จำเป็นทิ้ง ลดความผิดพลาดในการเรียกใช้งานคำสั่งต่าง ๆ ทำให้สุดท้ายโค้ดจะสั้นลงและอ่านง่ายขึ้นมาก ๆ นี่เป็นตัวอย่างของโค้ดที่ใช้ Android KTX ร่วมกับ Kotlin ครับ

โดย KTX ถูกทำเป็นไลบรารี่แยกออกมาเป็นหลายสิบตัวอิงตามว่าเราจะใช้ KTX กับส่วนไหนของโค้ด เช่น โค้ดทั่วไปของแอนดรอยด์ก็จะอยู่ในส่วนของ core-ktx โค้ดของ Fragment จะอยู่ในส่วนของ fragment-ktx และก็มีส่วนอื่นอีกเพียบ ตามนี้

ความจริง Android KTX เปิดตัวมาหลายเดือนแล้วหละ แต่ก็ยังถือว่าใหม่อยู่ นี่ยังอยู่ในสถานะ Alpha อยู่เลย แต่ส่วนตัวว่า Android KTX นี่แหละที่จะสร้างความเปลี่ยนแปลงทางด้านคุณภาพโค้ดของแอป ฯ แอนดรอยด์ในระดับที่สูงมาก ถ้าใครใช้ Kotlin อยู่แล้วก็ลองเล่น KTX เพิ่มดูนะ ไว้รอหลุด Alpha แล้วแนะนำให้นำไปใช้จริงอย่างยิ่งครับ

สนใจไปดูข้อมูลเพิ่มเติมของ KTX ได้ที่ https://developer.android.com/kotlin/ktx เบยย

Multidex

ไม่มีอะไร มันก็ Multidex เดิมนั่นแหละ เอาไว้เปิดใช้ในกรณีที่แอป ฯ ใหญ่มากและมีคำสั่งเกิน 64K อยู่ในแอป ฯ ซึ่งในรายละเอียดก็จะมีอีกว่า Multidex สำหรับ Android API Level ต่ำกว่า 21 ก็จะทำแบบนึง ถ้าแอนดรอยด์ API Level 21 เป็นต้นไปก็จะทำอีกแบบนึง

Multidex ก็ยังจำเป็นต้องใช้อยู่เพราะแอป ฯ นับวันยิ่งใหญ่ขึ้นเรื่อย ๆ อันนี้เค้าก็เลยเอามาจับใส่ใน Jetpack ด้วยนั่นเอง

Test

การเขียน Test จะทำให้โค้ดมีคุณภาพสูงขึ้นและลดความผิดพลาดที่อาจเกิดขึ้นจากการแก้โค้ดในอนาคตได้ ตอนนี้ Test กลายเป็นหนึ่งใน Fundamental ที่จำเป็นมาก ๆ แล้ว ไม่ว่าจะเป็น Unit Test, Integration Test หรือ UI Test ใครยังเขียนเทสต์ไม่เป็นก็เริ่มเขียนได้แล้วน้าาา โดยเริ่มเขียนไปพร้อมกับการเขียนโค้ดลอจิคเลย เพราะโค้ดที่เขียนมาเละ ๆ มันจะเทสต์ไม่ได้ มาเขียนเทสต์ตอนหลังอาจจะไม่ทันละ ปรับ Mindset ให้เขียนโค้ดไปเขียนเทสต์ไป จะได้เทคนิคอะไรตามมาอีกเพียบ รวมถึงการจัดการโครงสร้างโค้ดให้เป็นสัดส่วน การนำ DI เข้ามาใช้ ฯลฯ ด้วย

Architecture

ไลบรารี่ที่ทำให้โค้ดแบ่งเป็นสัดส่วนสวยงามและปรับ Architecture โค้ดให้อยู่ในรูปแบบที่เทสต์ง่ายขึ้นได้

Data Binding

อันนี้ก็เป็นไลบรารี่ที่เก่าพอสมควรแล้ว มีคนเอาไปใช้มากมายอยู่แล้ว แต่สำหรับคนที่ยังไม่รู้จัก มันคือไลบรารี่ที่ทำให้เราสามารถ Bind ตัวแปรใน Layout XML เข้ากับตัวแปร (Data Source) ในส่วนของโค้ด Java/Kotlin ได้ ซึ่งเราจะเรียกตัวแปรเหล่านี้ว่า ViewModel นั่นเอง เช่น

<TextView
    android:text="@{viewmodel.userName}" />

ผลของการ Bind คือจะทำให้โค้ดที่ต้องเขียนลดลงอย่างมีนัยสำคัญ เพราะเราไม่ต้องเขียนโค้ดในส่วนของ View เพื่อควบคุมการเปลี่ยนแปลงข้อมูลอีกแล้ว เจ้า Data Binding จะจัดการให้หมด

และผลที่ตามมาอีกคือเราสามารถปรับโครงสร้างโค้ดให้กลายเป็น MVVM ได้อย่างง่ายดาย และพอจัดการแยก View, ViewModel ออกมา ก็จะทำให้การเขียนเทสต์ทำได้ง่ายขึ้นมาก ๆ อีกด้วย

ใครยังไม่ใช้เชียร์ให้ใช้ครับ เป็นไลบรารี่ที่ดีมาก ข้อมูลเพิ่มเติมอยู่นี่ก๊าบบบ Data Binding

Lifecycles

Lifecycle เป็นสิ่งที่สำคัญมากกกกกกในโลกของการพัฒนาแอป ฯ แอนดรอยด์ สำคัญในระดับที่ใครไม่แม่นเรื่อง Lifecycle คือเจ๊งเลย ไม่มีทางทำแอป ฯ แอนดรอยด์ที่มีคุณภาพได้แน่นอน

และความสนุกอย่างนึงของโลกแอนดรอยด์คือ พอคุณแม่นเรื่อง Lifecycle แล้วก็จะพบว่ามีโค้ดจำนวนนึงที่ผูกกับ Lifecycle อยู่ เช่น ให้เริ่มทำงานตอน Fragment นั้นอยู่ในสถานะ Started และให้หยุดทำงานตอน Fragment อยู่ในสถานะ Stopped

ยกตัวอย่างเช่นโค้ดตัวนี้ เราสร้างคลาสขึ้นมาและให้มันทำงานตาม Lifecycle ของ Activity

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}


class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

ก็ดูเหมือนจะใช้งานได้ แต่สังเกตดูว่าโค้ดใน Activity มันก็ดูยุ่งยากวุ่นวาย ต้องเปิด onStart, onStop เพื่อคุม myLocationListener อีกทีนึง จะดีกว่ามั้ยถ้าเราสามารถควบคุมโค้ดให้เปลี่ยนการทำงานตาม Lifecycle ได้ในคลาส MyLocationListener เลย จะได้ไม่ต้องไปรบกวนโค้ดส่วน Activity ?

ถามว่าทำไมต้องไปรบกวน Activity น้อย ๆ ? คำตอบคือ โค้ดที่ดีเราควรจะมีโค้ดในส่วนของ Activity และ Fragment สั้นที่สุดครับ แล้วให้คลาสอื่น ๆ ไปทำงานตามหน้าที่ของตัวเองไป ไลบรารี่ต่าง ๆ เกิดมาเพื่อจัดการเรื่องราวตรงนี้เกือบทั้งสิ้น

และนี่คือสาเหตุที่ไลบรารี่ Lifecycles เกิดมาครับ เพื่อสร้างสิ่งที่เรียกว่า "Lifecycle-Aware Components" หรือออปเจ็กต์ที่ปรับการทำงานตาม Lifecycle ของ Activity หรือ Fragment ที่กำหนดได้ โดยเราสามารถ implements LifecycleObserver ให้กับคลาสใด ๆ ได้เลย แล้วก็ให้ผู้มี Lifecycle เป็นของตัวเองหรือที่เรียกว่า LifecycleOwner เป็นคนโยน Lifecycle ให้คลาสเหล่านั้นไปทำงาน แล้วคลาสนั้น ๆ ก็จะทำงานโดยอิงกับ Lifecycle ของ LifecycleOwner ได้ทันที !

ทั้งนี้ LifecycleOwner คือใคร เดาออกมั้ย ? ... ปิ๊งป่องงง ก็คือ Activity หรือ Fragment นั่นเอง เพราะทั้งโลกของแอนดรอยด์ก็มีแค่สองตัวนี้แหละที่มี Lifecycle เป็นของตัวเอง และคำสั่งที่เราเอาไว้ใช้ดึง Lifecycle ขึ้นมาใช้งานก็คือ getLifecycle() ซึ่งมีให้เรียกจาก Activity และ Fragment เท่านั้น

นี่คือตัวอย่างโค้ดที่มีการปรับนำ LifecycleObserver มาใช้แล้วครับ ให้โค้ดอธิบายในตัวมันเองเลยละกันเนอะ

class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // disconnect if connected
    }
}


class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}

ก็จะเห็นว่าโค้ดในส่วนของ Activity เหลือสั้นนิดเดียว และโค้ดที่เกี่ยวข้องกับ Lifecycle ก็ไปอยู่ใน MyLocationListener แทนด้วยความช่วยเหลือของ Annotation ต่าง ๆ ตามน้านนน

LiveData

LiveData คือคลาสสำหรับเก็บข้อมูลประเภทที่สามารถตั้งให้เรียกทำงานเมื่อข้อมูลมีการเปลี่ยนแปลงได้หรือที่หลาย ๆ คนคงคุ้นเคยกับคำว่า Observable นั่นเอง แต่สำหรับ LiveData มีความพิเศษอีกอย่างนึงคือ มันเป็น Observable ที่ทำงานร่วมกับ Lifecycle ด้วย กล่าวคือ

- ถ้า Activity หรือ Fragment อยู่ในสถานะ Stopped เจ้า LiveData ก็จะไม่ยิง Event ที่ถูก Observe ออกมา มันจึงเหมาะอย่างยิ่งกับการใช้อัปเดต UI

- การที่มันทำงานกับ Lifecycle ได้ ทำให้มันไม่เกิด Memory Leak เพราะ LiveData จะทำลาย Observer ทั้งหลายทิ้งโดยอัตโนมัติถ้า Lifecycle นั้นถูกทำลายทิ้งไปแล้ว

โดยตามปกติ LiveData จะถูกห่อในคลาส ViewModel อีกทีนึง เพื่อให้การเรียกใช้งานดูมี Pattern มากขึ้น มาดูตัวอย่างโค้ดกันนิดหน่อยเนอะ นี่เป็นตัวอย่างคลาส ViewModel ที่เก็บ LiveData ไว้ตัวนึงครับ

class StartViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String>
        get() = _data

    init {
        _data.value = "Hello, world!"
    }
}

และนี่เป็นโค้ดใน Fragment ที่สร้างตัวแปร StartViewModel ขึ้นมาจาก Pattern ของ ViewModel

viewModel = ViewModelProviders.of(this).get(StartViewModel::class.java)

จากนั้นก็จะสามารถ Observe การเปลี่ยนแปลงของข้อมูลได้ทันที

viewModel.data.observe(this, Observer { data ->

})

ง่าย ๆ แค่นี้แหละ แต่มันมีประโยชน์มากนะ ตัวอย่างการใช้งานก็เช่นสร้างคลาสประเภท Repository ขึ้นมาเพื่อดาวน์โหลดข้อมูลจาก Server แล้วนำมาแสดงผลบนหน้าจอ เราก็จะใช้ LiveData ในการเก็บข้อมูลที่ดาวน์โหลดมาและนำไปแสดงผลบนหน้าจอด้วย Observer

ฟัง ๆ ดูคุณสมบัติหลายอย่างก็ค่อนข้างคล้ายกับ Loader ซึ่งจริง ๆ LiveData ก็เกิดมาเพื่อใช้แทน Loader ได้ครับ โดยมันเป็นเหมือนการแยก Loader ออกเป็นสองส่วนคือส่วนของการโหลดข้อมูลและการเก็บข้อมูล พอแยกมันออกจากกันก็ทำให้สามารถเทสต์ได้ง่ายขึ้นด้วยวิธีต่าง ๆ เช่น Mock ครับ

LiveData ก็มีมานานแล้ว โดยรวมค่อนข้างคล้าย Observable ของ RxJava ต่างกันแค่มีความสามารถในการผูกกับ Lifecycle และมี State Active/Inactive อยู่ในตัวของมัน มันเลยเหมาะกับการเขียนแอป ฯ แอนดรอยด์มากกว่า ยังไงใครมาสายนี้ (Rx) อยู่แล้วก็คงจะสนุกกับมันพอควร

Navigation

อันนี้ชอบมาก นั่งจิ้มเล่นสนุกเลย

ใครเคยทำงานฝั่ง iOS น่าจะคุ้นเคยกับ Storyboard ที่สามารถครอบคลุมทั้งแอป ฯ ด้วย Navigation แล้วเปลี่ยนหน้าไปมาตาม Event ที่กำหนดได้ และสิ่งนั้นก็มีให้ใช้บนแอนดรอยด์แล้วจ้าาา

หลัก ๆ มันคือการเชื่อม Fragment ต่าง ๆ เข้าด้วยกัน สร้างเป็น Resource ประเภท navigation เขียนเป็น XML เช่นเดียวกับ Resource ตัวอื่น ๆ นี่เป็นตัวอย่างโค้ดของ Navigation ที่ประกอบด้วย MainFragment และ SecondFragment พร้อมประกาศ Action ชื่อ end_action ในการ Navigate จาก MainFragment ไป SecondFragment โค้ดอ่านง่าย ลองดู ค่อนข้างตรงไปตรงมามาก (รักแอนดรอยด์ก็ตรงนี้)

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@+id/launcher_home">

    <fragment
        android:id="@+id/launcher_home"
        android:name="com.nuuneoi.hellojetpack.ui.main.MainFragment"
        android:label="Home"
        tools:layout="@layout/main_fragment">

        <action
            android:id="@+id/end_action"
            app:destination="@id/end_dest" />

    </fragment>

    <fragment
        android:id="@+id/end_dest"
        android:name="com.nuuneoi.hellojetpack.ui.main.SecondFragment"
        android:label="End"
        tools:layout="@layout/second_fragment">

    </fragment>
</navigation>

ก็จะได้ผลลัพธ์ของ Navigation ออกมาเป็นหน้าตาแบบนี้

และเนื่องจาก Navigation เป็นการประกาศรูปแบบการเชื่อม Fragment ต่าง ๆ เข้าด้วยกัน ตัว Activity ก็เลยต้องนำ Navigation นี้มาใช้ประกาศเป็น Fragment หลักแทนผ่านคลาสชื่อ NavHostFragment นี่คือโค้ดของ main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true"
        />

</FrameLayout>

ซึ่งพอรันขึ้นมาตอนแรกก็จะได้เป็นหน้าตาของ MainFragment โดยอัตโนมัติ

และใน MainFragment ถ้าต้องการจะเปลี่ยนไปสู่ SecondFragment ก็สามารถสั่งด้วยโค้ดด้านล่างนี้ได้ทันที

view?.let { Navigation.findNavController(it).navigate(R.id.end_action) }

นี่เราลองให้ถ้าเกิดกดปุ่ม Show Me Your Name แล้วช่วย Navigate ไปตาม Action ที่กำหนดทีนะ ผลออกมาก็เลยเป็นแบบนี้

โดยรวมชอบมาก ทำให้เห็นภาพรวมและเขียนโค้ดได้ง่ายขึ้นแบบสุด ๆ อย่างไรก็ตาม อันนี้เป็นแค่เครื่องมือช่วยนะ สุดท้ายทุกคนยังต้องรู้พื้นฐานต่าง ๆ ของแอนดรอยด์อยู่ดี อย่างเจ้า Navigation นี่ถ้าคนเอาไปใช้ไม่รู้เรื่อง Lifecycle นี่พินาศทันทีแน่นอน พื้นฐานยังคงสำคัญครับ

สนใจเพิ่มเชิญจิ้ม ! Android Navigation

Paging

เป็นไลบรารี่ที่ช่วยจัดการระบบการทยอยโหลดข้อมูลและนำมาแสดงผลในปริมาณที่เหมาะสม (Paging) ให้กับ RecyclerView ยกตัวอย่างเช่น เราจะทำแอป ฯ สำหรับอ่านข่าว แต่เว็บนั้นมันมีข่าวอยู่ 20,000 ข่าว ถ้าจะโหลดมาหมดก็คงจะไม่เหมาะ สิ่งที่ควรคือโหลดสัก 10 ข่าวมาก่อน ถ้าผู้ใช้ Scroll ไปจนสุดก็โหลดเพิ่มมาอีก 10 แล้วก็ทำแบบนี้ไปเรื่อย ๆ

ก่อนหน้านี้เราก็ทำกันเป็นปกติอยู่แล้วแหละ เพียงแต่เราต้องเขียนลอจิคมากมายและควบคุมทุกอย่างเอง ซึ่งตัว Paging Library นี้ถูกสร้างมาเพื่อจัดการโค้ดตรงนี้ให้เรา พอเรา Scroll ถึงสุด RecyclerView แล้ว ตัว Callback ที่ถูกกำหนดก็จะถูกเรียกอัตโนมัติทันที เราก็แค่ดาวน์โหลดข้อมูลเพิ่มเติมและนำไปแสดงผลก็เท่านั้น

Component หลักของ Paging Library คือ PagedList ที่เอาไว้ควบคุมและเก็บข้อมูลที่โหลดมาได้ และอีกอันคือ PagedListAdapter ที่เอาไว้เชื่อมข้อมูลใน PagedList เข้ากับ RecyclerView นั่นเอง

ประโยชน์ของ Paging Library ก็คือทำให้โค้ดแบ่งเป็นโครงสร้างที่สวยงามและเทสต์ง่ายอีกเช่นกันครับ

โค้ดของไลบรารี่นี้อธิบายแอบยากเพราะมันเกี่ยวกับโครงสร้างอย่างอื่นด้วยเช่น ViewModel, Repository ยังไงไปลองอ่านเพิ่มเติมดูได้ที่ Paging Library ครับ ส่วนนี่ Codelab

Room

Room เป็น Abstract Layer ของ SQLite ที่ทำให้การเก็บข้อมูลลงใน SQLite ทำได้ง่ายขึ้นกว่าเดิมมากกกกกกด้วยการแบ่งโค้ดออกเป็น Entity และ Dao ก่อนจะเชื่อมกันเป็น Database และนำมาใช้งาน

@Entity
public class User {
    @PrimaryKey
    private int uid;

    @ColumnInfo(name = "first_name")
    private String firstName;

    @ColumnInfo(name = "last_name")
    private String lastName;

    // Getters and setters are ignored for brevity,
    // but they're required for Room to work.
}

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND "
           + "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

วิธีการเรียกใช้ก็แค่

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

จะเห็นว่าโค้ดง่ายกว่าการติดต่อ SQLite ตรง ๆ ด้วยตัวเองมาก ๆ อย่างมีนัยสำคัญ ใครใช้ SQLite อยู่ แนะนำให้สลับมาใช้ Room อย่างยิ่ง ชีวิตเปลี่ยนเลยหละ นอกจากโค้ดจะสวยขึ้นแล้ว การเทสต์ต่าง ๆ ยังทำได้ง่ายขึ้นอีกด้วยจากการแยก Component ย่อยออกมา

อันนี้เป็น Codelab สำหรับ Room ลองไปจิ้มเล่นได้ครัช

ViewModel

เป็นตัวที่เห็นแว้บ ๆ ด้านบนแล้ว มันคือคลาสที่เราเอาพวก LiveData ไปเก็บไว้นั่นเอง ถามว่าทำไมเราถึงเก็บไว้ใน ViewModel กัน ?

หลัก ๆ นิยามของ ViewModel คือ "คลาสที่เอาไว้เก็บข้อมูลที่ยุ่งเกี่ยวกับ UI" เนื่องจากมันมีความสามารถอย่างนึงคือ มันสามารถดำรงข้อมูลอยู่ต่อให้เกิด Configuration Change ก็ตาม (ซึ่งปกติถ้าเราไม่ได้ใช้ ViewModel ข้อมูลพวกนี้จะหายหมด เราต้องทำการเก็บเองด้วย onSaveInstanceState) คือถึกทนมาก

พูดง่าย ๆ มันเป็นคลาสประเภท Lifecycle-Aware นั่นเอง ดังนั้นการจะสร้างมันได้จึงต้องโยน Activity หรือ Fragment เข้าไปด้วย เช่น

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
    }
}

เวลาใช้งานก็ใช้ให้ถูก มันเกิดมาเพื่อเก็บข้อมูลสำหรับนำไปแสดงผลบน UI เป็นหลัก และชีวิตของข้อมูลจะอิงกับ Lifecycle ของ LifecycleOwner ที่มันผูกอยู่ครับ

WorkManager

ตลอดเวลาหลายปีที่ผ่านมา แอนดรอยด์พา API กลุ่ม Job Scheduler หรือการตั้งให้ส่วนของโค้ดทำงานตามเงื่อนไขที่กำหนด เยอะเหลือเกิน ไม่ว่าจะเป็น JobScheduler, Firebase JobDispatcher หรือ AlarmManager เห็นแล้วไมเกรนแทบขึ้น เราจะใช้ตัวไหน เรียกยังไงดีเนี่ย โค้ดวุ่นวายไปหมด

ไม่ใช่ปัญหาอีกต่อไป เพราะแอนดรอยด์จูงมือพาเจ้าสิ่งนี้มาให้ผู้คนรู้จักเพิ่มอีกแล้ว WorkManager 555

แต่รอบนี้ถือเป็นข่าวดีเพราะเจ้า WorkManager เป็น Wrapper ของ API ต่าง ๆ ด้านบนอีกที เราแค่สั่ง ๆ มันไป มันจะไปพิจารณาเองว่าจะใช้คำสั่งไหนในการเรียกให้เราเพื่อให้เหมาะสมกับเงื่อนไขที่กำหนด จากนี้ก็ไม่ต้องคิดละว่าจะใช้อันไหน เรียกใช้ WorkManager อันเดียวพอ !

นี่เป็นตัวอย่างโค้ด เอามาให้ดูว่ามันง่ายแค่ไหน จะได้สบายใจ

public class CompressWorker extends Worker {
    @Override
    public Worker.WorkerResult int doWork() {

        // Do something

        // Indicate success or failure with your return value:
        return WorkerResult.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

// Create a Constraints that defines when the task should run
Constraints myConstraints = new Constraints.Builder()
    .setRequiresDeviceIdle(true)
    .setRequiresCharging(true)
    // Many other constraints are available, see the
    // Constraints.Builder reference
     .build();

// ...then create a OneTimeWorkRequest that uses those constraints
OneTimeWorkRequest compressionWork =
                new OneTimeWorkRequest.Builder(CompressWorker.class)
     .setConstraints(myConstraints)
     .build();

// Enqueue Task
WorkManager.getInstance().enqueue(compressionWork);

อ่านโค้ดรู้เรื่องเนอะ ตามน้านนน

Behavior

มาส่วนของฟังก์ชันเสริมที่ทำให้ประสบการณ์การใช้งานแอป ฯ สมบูรณ์แบบกัน (แต่ส่วนใหญ่มันก็เป็นเรื่องพื้นฐานนะ ดังนั้นอันไหนไม่มีอะไรมากจะผ่านไปไว ๆ)

Download manager

เป็น System Service ที่เกิดมาตั้งแต่ API Level 9 ละ เอาไว้ให้ระบบของเครื่องดาวน์โหลดไฟล์ใหญ่ ๆ ให้โดยอัตโนมัติ เช่น ถ้าเราต้องการให้เครื่องโหลดไฟล์วีดีโอขนาด 1GB ก็ไม่ต้องเขียน HTTP Request เองให้วุ่นวาย ให้ Download manager จัดการให้ อย่างไรก็ตาม การสั่งให้ดาวน์โหลดไฟล์ด้วยวิธีนี้ รีเควสต์ของการดาวน์โหลดก็จะไปโผล่ในแอป ฯ Downloads ภายในเครื่องด้วยเช่นกัน

หลาย ๆ คนอาจจะไม่เคยเรียกคำสั่งนี้ แต่ในแง่การใช้งาน เราเชื่อว่าหลายคนคงเคยใช้งานไปแล้วหละ

Media & playback

คลาสกลุ่ม MediaPlayer ที่มีมากับเครื่องตั้งแต่สมัยครั้งโบราณกาล เอาไว้เล่น Video หรือ Audio ซึ่ง ... ตรงไปตรงมามาก ไม่มีอะไรจะอธิบายเพิ่ม 555

Notifications

Notifications ที่ทุกคนรู้จักกันดีอยู่แล้วก็ถูกบรรจุไว้ใน Jetpack ด้วย เป็นการบอกว่าถ้าจะทำแอป ฯ แอนดรอยด์ให้สมบูรณ์น่าใช้งานก็อย่าลืม Implement ระบบ Notifications ให้สมบูรณ์ด้วยนะ ซึ่งแอนดรอยด์แต่ละเวอร์ชันแต่ละ Target Device ก็มี Best Practice ที่แตกต่างกันไปอีกด้วย

สำหรับ Notifications นั้นถือว่าเป็นโค้ดที่มี Fragmentation สูงมากอันดับต้น ๆ ของแอนดรอยด์ โดยเฉพาะเวอร์ชันหลัง ๆ มีอะไรโผล่มาเต็มไปหมดเลย ยังไงก็แนะนำให้ใช้ตัว Notification ใน Support Library จะดีที่สุดครับ จัดการโค้ดง่ายกว่ามากแน่นอน

Permissions

ระบบ Permission ของแอนดรอยด์ในเวอร์ชั่นเก่า ๆ ถือว่าง่ายมาก คือลงแอป ฯ ปุ๊บได้รับสิทธิ์ทุกอย่างไปเลย ซึ่ง ... Security หละหลวมมาก ตั้งแต่ Android 6.0 เป็นต้นมา ระบบ Runtime Permission หรือการขอ Permission ระหว่างใช้งานแอป ฯ อยู่ตอนที่แอป ฯ กำลังจะต้องใช้สิทธิ์นั้น ๆ เท่านั้น

งานนี้ยังมีหลายคนที่ไม่แม่นและทำตัว Runtime Permission ไม่เป็น นาทีนี้ Android P จะออกแล้ว ต้องทำเป็นแล้วนาจา ไม่งั้นแอป ฯ พังและ User Experience จะไม่ดีครับ

ทางเว็บ Android Jetpack ไม่มีคำแนะนำเรื่องไลบรารี่ เค้ายังเขียนให้ใช้วิธีเดิม ๆ อยู่ ยังไงถ้าใครอยากเขียนสั้นลงก็ลองไปดูไลบรารี่ที่ชื่อ Dexter ได้ครับ

Sharing

แอป ฯ สมัยนี้ไม่ได้ใช้งานตัวเดียวแล้วจบ กว่ารูปเซลฟี่จะหน้าวิ้งเหมือนดาราเกาหลีได้ เราต้องส่งรูประหว่างแอป ฯ ไปตั้งกี่แอป ฯ ไม่รู้ (หวายยยย) ดังนั้นระบบ Share ก็สำคัญนะเออ User Experience จะดีขึ้นมากมายถ้าใส่ระบบ Share เข้าไปอย่างเหมาะสม

เชื่อว่าใช้กันเป็นหมดอยู่แล้วหละ แต่ไม่เป็นไร เอาลิงก์มาแปะหน่อย ShareActionProvider

Slices

Slices นี่เป็นของใหม่และถือเป็นไฮไลท์ของส่วน Behavior เลย

ถ้าถามว่า Slices คืออะไร ก็ต้องปูพื้นเรื่องตลาด Mobile App ทุกวันนี้ก่อนที่ต้องยอมรับกันได้แล้วว่าจำนวนคนกดที่เข้าแอป ฯ นั้นมีน้อยลงทุกวี่วัน ทางกูเกิลเลยพยายามสร้างสถานที่รวมศูนย์ให้ทุกคนเอาข้อมูลในแอป ฯ ตัวเองไปรวมอยู่ด้วยกันกับแอป ฯ ชาวบ้านได้ จะได้เป็นการเพิ่มจำนวนผู้ใช้งาน และแอป ฯ ที่ว่านั้นก็คือ Google Assistant นั่นเองงงงง

ทีนี้ทางกูเกิ้ลเลยเปิด API ชื่อว่า Slice เพื่อให้เราสามารถเสก UI ของแอป ฯ เราไปอยู่บนแอป ฯ Google Assistant ได้ หน้าตาประมาณนี้ คงเคยเห็นกันมาบ้างถ้าเคยใช้ Google Assistant

ซึ่ง Slice ที่ทำก็จะอยู่ในรูปแบบของ Provider ชื่อ SliceProvider และประกาศมันไว้ใน AndroidManifest.xml ตามรูปแบบที่คุ้นเคย

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.app">
    ...
    <application>
        ...
        <provider android:name="MySliceProvider"
            android:authorities="com.example.android.app"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.app.slice.category.SLICE" />
            </intent-filter>
        </provider>
        ...
    </application>

</manifest>

คราวนี้ใน MySliceProvider เราก็ประกาศโค้ดตามหลักของ Slice API เพื่อให้มันเรนเดอร์หน้าตาออกมาตามที่เราต้องการได้ เช่น

override fun onBindSlice(sliceUri: Uri): Slice? {
    return if (sliceUri.path == "/hello") {
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .addRow { it.setTitle("URI found.") }
                .build()
    } else {
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .addRow { it.setTitle("URI not found.") }
                .build()
    }
}

ก็จะได้หน้าตาออกมาแบบนี้

นอกจากนั้นก็ยังสามารถ Handle Event ผ่านพวก BroadcastReceiver ได้ด้วยเช่นกัน ไม่เอาวิธีมาลงละกัน แต่โดยรวมค่อนข้างคล้ายกับการทำ Widget บน Home Screen เลย (ทุกวันนี้ยังมีใครทำอยู่มั้ย) ยังไงใครอยากรู้ข้อมูลโดยละเอียดและทดลองด้วยตัวเองก็สามารถไปอ่านเพิ่มเติมได้ที่ Slices ครับ

UI

สุดท้ายละ หมวดนี้ก็ไม่ค่อยมีอะไร เน้นเรื่อง Fundamental ของแอนดรอยด์มามัดรวมกันมากกว่า ก็มาดูกันเนอะว่าเค้าเอาอะไรมาเน้นใน Jetpack กันบ้าง

Animation & transitions

อันนี้ไม่มีอะไรพิเศษเลย มันคือ Animation และ Transition ของ View ต่าง ๆ บนหน้าจอนั่นเอง ไม่มี API อะไรพิเศษ มันคือตัวที่ใช้กันมานานแล้วนั่นแหละ

ซึ่งก็สำคัญนะ เพราะแอป ฯ ที่แข็งทื่อ ปราศจาก Animation และ Transition ก็เป็นแอป ฯ ที่ไม่น่าใช้เท่าไหร่ งานนี้ก็ไม่ใช่แค่เรื่องของโปรแกรมเมอร์อย่างเดียวด้วย แต่รวมถึงงานด้าน Design และ UX ด้วย รับผิดชอบร่วมกันนะะะ

Auto

แปลว่าอัตโนมัติ ... ไม่ใช่ ต้องแปลว่ายานพาหนะสิ ! ความจริงก็ไม่มีอะไรอีกนั่นแหละ แต่เป็นการเรียกร้องเป็นนัย ๆ จากทางกูเกิ้ลว่า เรามาออกแบบแอป ฯ ให้ใช้บน Android Auto อย่างสมบูรณ์แบบได้กันเถอะะะ

ไม่รู้จะเขียนอะไร เหมือนเป็นของแถมซะมากกว่า ถ้าใครจะทำแอป ฯ ให้ใช้ได้บน Android Auto ก็ลองไปอ่านพวก Guideline เพิ่มเติมได้ที่ Android Auto Overview นาจา

Emoji

เป็นหนึ่งในความบ้าบอ (ที่ยัดมาใน Jetpack ด้วย) แต่ก็น่ารักดี

โอ้ จอร์จ เคยมั้ย เคยทำแอป ฯ ที่มีการแสดงข้อความ แต่ Emoji ดันไม่แสดงเพราะแอนดรอยด์รุ่นเก่า ๆ ไม่มีฟอนต์ Emoji มั้ย ?

ซาร่า ไม่ต้องกังวลอีกต่อไปแล้ว เพราะแอนดรอยด์จัดนี่มาให้แล้ว EmojiCompat ที่พร้อมจะแสดงผลทุก Emoji บนแอป ฯ เราได้ถึงแม้เครื่องนั้น ๆ จะไม่มี Emoji ตัวนั้น ๆ ติดตั้งอยู่ก็ตามมม

คราวนี้ไม่ว่าจะรันบนรุ่นเก่าแค่ไหนก็มี Emoji แสดงผลแล้วแน่นอนจ้าาาา

จบ ใครคิดว่าได้ใช้ก็ไปดูวิธี Implement กันได้ใน Emoji Compatibility ก๊ะเจ้าา

Fragment

อันนี้ข้ามเลยได้มะ ฮ่า ๆ มันก็คือ Fragment ตัวที่ใช้กันอยู่แล้วทุกวี่ทุกวันนี่แหละ เลยไม่รู้จะพูดอะไรเพิ่ม เค้าจับมายัดลง Jetpack ก็เพราะมันเป็นสิ่งที่สำคัญมากในการพัฒนาแอป ฯ แอนดรอยด์ดี ๆ สักตัว

ถ้าจะให้พูดตรงนี้คือ ... ใครยังใช้ Fragment ไม่เป็นนี่ตีมือเพี๊ยะจริง ๆ นะ มันสำคัญมากกก แต่นาทีนี้เราไม่คาดหวังว่าคนที่เขียนแอนดรอยด์จะยังใช้ Fragment ไม่เป็นอยู่แล้วแหละนะ ดังนั้นผ่านไป ผ่านไป

Layout

นี่ก็คือการเขียน Layout XML ให้เป็น ตั้งแต่ระดับเบสิค วาง View โน่นนี่ลงไป ไปจนถึงการจัดวางหน้าตาบนหน้าจอขนาดต่าง ๆ ให้ได้ตามที่ Requirement เขียนมาครับ (ไม่มีอะไรมากกว่านั้น นี่มันพื้นฐานของพื้นฐานอีกทีนึงนะเนี่ยเอาจริง ๆ)

Palette

Pallete API ที่เอาไว้สร้างชุดสีโดยใช้ภาพก็ถูกใส่เข้ามาใน Jetpack เช่นกัน ความจริงมีมานานมากกกกแล้ว แต่ก็ไม่ค่อยเห็นใครใช้กันเท่าไหร่

ประโยชน์ของมันจริง ๆ ก็มีนะ พอแอป ฯ กับ Content ที่อยู่ภายในแอป ฯ มีความ Blend กัน แอป ฯ ก็จะดูน่าใช้สบายตาขึ้น แต่ปัญหาคือแอป ฯ ส่วนใหญ่จะมี CI ของบริษัทหรือของผลิตภัณฑ์อยู่แล้ว ก็เลยไม่ค่อยนิยมเปลี่ยนสีแบบ Real Time กันเท่าไหร่

ยังไงถ้าอยากให้แอป ฯ ของท่านมีความเป็นอีกัวน่า เปลี่ยนสีตาม Content ภายในแอป ฯ ก็ลองใช้งาน Palette API ดูได้ครับ

TV

นี่ก็ของแถม กูเกิ้ลยังพยายามดัน Android TV ต่อไป ก็เอาเป็นว่าใครสนใจจะทำแอป ฯ บน Android TV ก็ไปอ่านไปจิ้มเล่นกันได้ที่ Android TV Overview คร้าบ

Wear OS by Google

ชิ้นสุดท้ายก็ยังเป็นของแถม 555 เช่นเดียวกับ Auto และ TV แต่อันนี้เป็น Wear OS สำหรับอุปกรณ์ Wearable (ที่ยังมีคนใช้อยู่อีกหรอววว)

ไม่ว่าจะยังไง เราว่าในบรรดา Android for xxx ทั้งหลายเนี่ย Wear OS เป็นตัวที่ทุกแอป ฯ ควรจะทำให้สนับสนุนมากที่สุด เพราะมีโอกาสสูงมากที่ผู้ใช้จำนวนหนึ่งจะใช้ทั้งมือถือ Android และ Wear OS Device (นาฬิกา) ซึ่งผู้ใช้กลุ่มนี้คงจะประทับใจมากถ้าเราทำแอป ฯ ให้สนับสนุนบน Wear ด้วย ก็จะได้ไม่เป็นการเสียโอกาสเนอะ

แต่ถ้าไม่มีเวลา ไม่มีงบ ไม่ต้องก็ได้ ...

ยังไงก็ไปศึกษาเพิ่มเติมกันได้ที่ Wear OS Overview คับป๋ม

สรุป

พิมพ์เหนื่อยมาก ...

ความจริงแล้ว Jetpack ก็ไม่ได้มีอะไรใหม่มากมายเท่าไหร่ เป็นการมัดรวม "ของมันต้องมี" ของการพัฒนาแอป ฯ แอนดรอยด์เข้าด้วยกันแค่นั้นเอง แต่ของใหม่นี่แค่หยิบมือเท่าไหร่ ซึ่งถามว่ามีไฮไลท์อะไรที่อยากให้ไปดูเป็นพิเศษก็คงจะเป็นตามนี้

- Android KTX

- Navigation

- Slice

- วิธีใช้ Architecture ทั้งก้อนนั่น (LiveData, Room, Data Binding, ViewModel) ในการเรียงโครงสร้างแอป ฯ ให้อยู่ในรูปแบบที่สวยงามและเทสต์ง่าย ถ้าบรรลุตรงนี้จะรู้เลยว่าทำไมแต่ละอันในหมวด Architecture ถึงมีประโยชน์และถูกนำมาใส่ใน Jetpack

ก็หวังว่าจะมีประโยชน์ เปิดตัวแว้บเดียว เขียนซะเป็นวัน ทำบล็อกเกอร์ลำบากนะรู้มั้ยกูเกิ้ลลลล

บทความที่เกี่ยวข้อง

Jul 23, 2017, 15:36
237597 views
บันทึกสองเดือนของการสังเกตการณ์ แอบดูการทำงานของคนใน Silicon Valley
Aug 5, 2018, 04:23
67824 views
รีวิวหนัง "AppWar" หนังรักในธีม Startup ที่ประสานกันอย่างลงตัวสุด ๆ (8/10)
0 Comment(s)
Loading