[Android] Non-Transitive R Classes in Android Gradle Plugin.

Pongpiya Rukwirojsuk
Lotus’s IT
Published in
2 min readJan 17, 2024

วันนี้จะมาแชร์บทความดีๆ ที่เกี่ยวกับเรื่อง 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
impor
t 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% 😱

https://slack.engineering/its-a-non-transitive-r-class-world/

ก็น่าสนใจดีนะ ถึงแม้ว่าการเปิดใช้งาน 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 ให้น้อยลง แต่เราก็อาจจะต้องปรับตัว รวมไปถึงเทคนิคการเขียนโค้ดต่างไปนิดหน่อย ส่วนตัวคิดว่า น่าสนใจมากเลยที่เดียวในการนำเข้ามาลองใช้ครับ

--

--