[Android] Non-Transitive R Classes in Android Gradle Plugin.
วันนี้จะมาแชร์บทความดีๆ ที่เกี่ยวกับเรื่อง Non-Transitive R Classes in Android Gradle Plugin ที่จะเหมาะกับโปรเจคแอปขนาดใหญ่และมี Module แยกย่อย ออกมาจำนวนมาก ซึ่งนอกจากจะช่วยเพิ่มประสิทธิภาพในการ build time แล้ว ยังช่วยในเรื่องของ APK size ให้มีขนาดเล็กลงด้วยครับ
สวัสดีครับ พอดีได้ไปเจอบทความ Non-Transitive R Classes in Android Gradle Plugin จาก slack.engineering ที่น่าสนใจมาก
ต้องขอเล่าเกี่ยวกับ R Class ก่อนนะครับว่า โดยปกติแล้วในการทำงาน เวลาเราจะต้องเข้าไปเรียกใช้ Android Resource เราจะต้องมีการเรียกใช้ผ่าน R Class กันอยู่แล้วเนอะ เพราะว่า R Class จะเป็นตัวกลางที่จะเชื่อมระหว่างโค้ดของเรากับข้อมูลที่อยู่ใน Android Resource เข้าด้วยกัน
ซึ่ง R Class จะถูกสร้างขึ้นโดย AAPT (Android Asset Packaging Tool) ในตอน Compile Time เพื่อสร้าง ID ให้กับ Android Resource ทุกตัวที่มีอยู่ในโปรเจค
Transitive R Classes
Transitive R Classes
// feature home module
import com.example.home.R
R.string.home.number
// feature hotel module
import com.example.hotel.R
R.string.hotel.number
in Module hotel
.
.
getString(R.string.hotel.number)
getString(R.string.home.number) <---
เราสามารถเรียกใช้ Android Resource ใน Module home แบบสั้นๆได้เลย
Transitive R Classes จะสะดวกตรงที่แต่ละ Module สามารถเรียก Android Resource จาก R Class ได้เลย เพราะ AAPT รวมไว้ให้เรียบร้อยแล้ว
แต่!!! โดยปัญหาของ Transitive R Classes ก็คือเมื่อโปรเจคมีขนาดใหญ่มากขึ้นจน Android Resource ในโปรเจคมีเยอะ และมีการแบ่ง Module เป็นจำนวนมาก จะทำให้การทำ Transitive R Classes เพิ่ม Build Time และ APK Size มากขึ้นเท่านั้น 😱
ด้วยเหตุนี้จึงมี Non-transitive R Classes เข้ามาเป็นอีกทางเลือก
Non-Transitive R Classes
รูปแบบของ Non-Transitive R Classes ก็คือแต่ละ Module จะเห็นแค่เฉพาะ Android Resource ของตัวเอง จึงทำให้ AAPT ไม่ต้องเสียเวลารวม Android Resource ของหลาย Module เข้าด้วยกัน 🙏🏻🙏🏻🙏🏻
Non-Transitive R Classes
// feature home module
import com.example.home.R
R.string.home.number
// feature hotel module
import com.example.hotel.R
R.string.hotel.number
in Module hotel
.
.
getString(R.string.hotel.number)
getString(com.example.home.R.string.home.number) <---
โอ้วว รู้สึกขัดใจขึ้นมาทันที ยาวเกิ้น ฮ่าๆ แต่มาดูถึงประโยชน์มันก่อนเด้อ 😆😆
จากแหล่งอ้างอิงได้มี Performance result มาแล้วว่า แอป Slack ที่เปลี่ยนมาใช้ Non-Transitive R Classes แทน พบว่า APK Size ลดลง ~8.5% และ Build Time ลดลงจากเดิมมากถึง 14% 😱
ก็น่าสนใจดีนะ ถึงแม้ว่าการเปิดใช้งาน Non-Transitive R Classes จะต้องแลกด้วยการเปลี่ยนวิธีการเขียนโค้ดเพื่อเรียกใช้งาน Android Resource ด้วยเช่นกัน
จากประเด็นที่ขัดใจเมื่อสักครู่ เราสามารถทำวิธีนี้ก็ได้นะ เพื่อให้การอ่าน/เขียนโค้ด ทำงานได้เข้าใจมากขึ้น
import com.example.home.R as HomeRes
getString(R.string.hotel.number)
getString(HomeRes.string.home.number) <---
How to set up Non-Transitive R Classes
เราสามารถเข้าไปใช้งานได้ผ่าน gradle.properties ได้เลย
android.nonTransitiveRClass=true <---
หากต้องการใช้ให้ใส่เป็น true แต่ถ้าไม่ใส่ false
ซึ่งวิธี Migrate code ในตัว Android Studio ก็มีให้เราไว้แล้วครับ เราสามารถเข้าไปที่ Refactor -> Migrate to non-transitive R classes
สรุปก็คือ Non-Transitive R Classes เป็นตัวเลือกที่ดีตัวหนึ่งที่จะช่วย ลดการทำงานของ AAPT ที่คอยรวม Android Resource ให้กับแต่ละ Module อยู่ตลอด รวมถึงช่วยลด Build Time และ APK Size ให้น้อยลง แต่เราก็อาจจะต้องปรับตัว รวมไปถึงเทคนิคการเขียนโค้ดต่างไปนิดหน่อย ส่วนตัวคิดว่า น่าสนใจมากเลยที่เดียวในการนำเข้ามาลองใช้ครับ