มาเพิ่ม Skill ของ Android Developer ด้วยการทำ Android Library กันเถอะ
เราเชื่อว่า Android Developer ทุกคนเคยใช้ library แต่ก็ไม่ใช่ทุกคนที่เคยทำ library ใช่ป้ะ?
บทความนี้เป็นเนื้อหาที่พูดในงาน Android Bangkok 2022 นะ
แล้วทำไมเราต้องทำ library ด้วยนะ ?
อาจจะเกิดจาก developer คนนึง อยากทำ library อะไรสักอย่าง เพื่อใช้เอง หรือแบ่งปันให้เพื่อนๆได้ใช้งาน เช่น อยากทำ library สำหรับ scan QR Code, ทำแอพแชทหน้าตาเหมือน Facebook Messenger, ทำ view แบบปัดซ้ายขวาเหมือน Tinder หรือทำ library แบบ IG Story ที่มันมี indicator ข้างบน
หรือถ้าเป็นงานเป็นการหน่อย …
สมมุติทีม A ทำ product app A ขึ้นมา มี 9 features
วันดีคืนดี ทีม B อยากได้ feature 1 ไปใช้บ้าง และ PM คำนวณแล้ว การเอา feature ที่มีอยู่แล้วในองค์กรมาใช้ ก็น่าจะมี cost ที่น้อยกว่าทำใหม่ทั้งหมดเนอะ แล้ว feature นั้น ก็ดั้นไปอยู่ใน module app ที่อ้วนตาม feature ที่เพิ่มขึ้นมาด้วยหน่ะสิ
บางที่เขาก็แยกทีมทำ library ไปเลย โดยสร้าง library module สำหรับ feature ต่างๆ ไปไว้ใน SDK repository
ซึ่งการที่เราจะทำ library หรืออะไรต่างๆได้นั้น ควรจะต้องใช้ modular architecture นะ เพื่อให้สะดวก และง่ายต่อการประกอบของต่างๆที่มี เข้าด้วยกันได้ หรือถอดออกจากกันก็ง่าย
เช่น ตัวอย่างนี้ module app เป็น module ที่ใช้ในการรันแอพขึ้นมา ซึ่งข้างในไม่ได้ implement อะไร นอกจากเรียกใช้งาน module อื่นๆ ไม่ว่าจะเป็น module ที่เป็น feature หรือ core
ทำให้เราสามารถดูแลของข้างในได้ง่ายขึ้น ทำให้บิ้วแอพได้เร็วขึ้น และนำบาง feature ที่ทำไปให้ทีมอื่นเอาไปใช้ได้ด้วยหล่ะ
สำหรับเรื่องของ modular architecture เราเคยเล่าเรื่อง concept อะไรต่างๆไว้แล้ว สามารถไปฟังเพิ่มเติมได้ที่คลิปนี้นะ
และเราจะสร้าง android library อย่างไรหล่ะ ?
แน่นอนว่า build.gradle
ของ module app จะเป็น apply plug-in แบบนี้ เพื่อบอกว่า module นี้เป็น module application นะ
apply plugin: 'com.android.application’
วิธีการสร้าง library module ก็แสนจะง่าย เพียงแค่คลิกขวาสร้าง module ใหม่
เลือก Android Library แล้วกรอก module name และ package name ให้เรียบร้อย และอย่าลืมเลือก minimum SDK ด้วยนะ พยายามเลือกขั้นตํ่าที่สุด อาจจะเลือก API 16 หรือ API 21 ก็ได้ ตามความเหมาะสม
เมื่อทำการสร้าง library ของเราเรียบร้อยแล้ว สังเกตว่า ใน build.gradle
ของ library นี้ จะ apply plugin Android Library มาแล้วนะ
apply plugin: 'com.android.library'
จากนั้นก็ coding ไปเรื่อยๆจนเสร็จสิ้น พร้อมส่งมอบ library ให้กับคนอื่นๆต่อแล้วนะ
แล้วเราจะสร้างปล่อย android library ที่เราทำเสร็จแล้ว ให้คนอื่นได้ใช้งานได้อย่างไร?
มีวิธีที่เราหยิบมาพูดถึง 2 วิธีด้วยกัน โดยในตอนนี้จะพูดถึงการปล่อย library ที่เราทำเสร็จให้ทีมอื่น ๆ ได้นำไปใช้งานกัน คือ AAR และ POM เราจะมาค่อยๆดูไปทีละอันกัน
AAR (Android ARchive)
วิธีการ generate AAR ไฟล์นั้นก็แสนจะง่ายดาย เมื่อเราทำ Library จนพอใจแล้ว ก็ทำการ build ซะ โดยจิ้มไปดังนี้
{Project_Name} -> {Library_Module} -> Tasks -> build -> build
ไฟล์ AAR ที่ได้ จะอยู่ใน
{Project_Name} -> {Library_Module} -> build -> outputs -> aar
วิธีการนำไฟล์ AAR ไปใช้งาน มีหลายๆวิธี เช่น
แบบแรก วิธีแรก ให้สร้าง library module ขึ้นมาใหม่ และลบไฟล์ทุกอย่างทิ้ง จากนั้นใส่ไฟล์ aar ของเราลงไป
จากนั้นแก้ไฟล์ build.gradle
ของ library ที่เพิ่งสร้าง เป็นหน้าตาแบบนี้ จากนั้น sync gradle 1 รอบ
แล้วค่อยเอาไปใช้งานตามปกติแบบนี้
ซึ่ง ref มาจากบล็อกพี่เอก
แบบที่สอง เป็นวิธีที่เราใช้ประจำ คือเอา AAR มาใส่ใน folder libs จากนั้นนำมา implement ใน build.gradle
ของ module ที่เราต้องการ จากนั้น sync gradle 1 รอบก็ใช้งานได้หล่ะ
พอได้แล้วก็นำไปให้คนอื่น ๆ ได้นำไปใช้งานกัน
แต่ก็มีปัญหา คือ แต่ก็เกิดปัญหาขึ้นมา จากการนำเจ้า AAR ไปใช้ต่อ คือ พวก dependency ที่ใช้ใน Library นั้น มันไม่ตามมาด้วยจ้าาาาาา
เรามีดูข้างในเจ้า AAR ไฟล์กันหน่อย ว่าข้างในมีอะไรบ้าง
จากใน document เขาบอกว่า เจ้า aar file เหมือน zip file มีไฟล์ที่บังคับที่มีในนั้น คือ AndroidManifest
ส่วนข้างล่างไฟล์ aar จะมีพวกนี้ติดมาด้วยนะ
อ่ะไหนๆลองพิสูจน์ดูซิ ว่าตรงกันไหม
เราเปลี่ยนสกุลไฟล์ AAR เป็น zip เพราะใน Document บอกว่า AAR เป็น zip ที่รวมไฟล์ต่างๆของ Library เข้าด้วยกัน
ปล. ของ macos อย่าลืมกด Add ไม่เช่นนั้นเวลา extract ไฟล์นี้ออกมันจะดันกลับมาเป็น AAR เหมือนเดิมจ้า
จากนั้นลอง extract file ออกมาดู จะพบกับ AndroidManifest แล้วก็ไฟล์ source code ต่างๆ อยู่ใน classes.jar มีไฟล์ resource ต่างๆ รวมไปถึง proguard ด้วย
แน่นอนว่า ไม่น่าจะมีสิ่งที่เกี่ยวข้องกับ dependency ที่ library เราเรียกใช้อยู่นะ
วิธีแก้ปัญหา ต้องไปบอกทีมหรือคนที่ใช้ AAR นี้ ให้เราได้ทราบ เช่น ไปใส่ใน README.md
ในโปรเจก ว่า ต้องใส่ dependency เพิ่มด้วยนะ ไม่งั้นใส่แล้วแอปจะ crash นะ เพราะไม่ได้เพิ่ม dependency ที่ library ใช้ เข้ามาด้วย
แล้วเราทำไฟล์ AAR ที่ติดพวก dependency ที่ library ใช้มาด้วยได้ไหมนะ?
คำตอบคือได้นะ ที่ทีมเคยทำจะประมาณนี้นะ เดี๋ยวเรามาอธิบายเรื่อง POM กันต่อเลย
POM (Project Object Model)
แจ้งเตือน: แต่ละเจ้าจะที่ทำ private maven repository มีการ set config หรืออะไรบางอย่างที่แตกต่างกันไป แต่โดยรวมขั้นตอนและหลักการเหมือนกัน แนะนำให้ไปอ่าน document แต่ละเจ้าเอานะ
pom คืออะไร?
ย่อมาจาก Project Object Model
เป็นไฟล์ xml ที่ประกอบไปด้วยข้อมูลของเจ้า library ของเรา ประกอบไปด้วย groupId, artifactId, version ของ library ของเรา และ dependencies ต่างๆใช้ใน library ทำให้เราสามารถนำตัวนี้ไปใช้ได้เลย โดยไม่ต้องบอกให้คนที่ใช้งาน library เรานั้น ไปใส่ dependency ต่างๆที่เกี่ยวข้องเอง
ขั้นตอนการเอา pom ขึ้นไป ก่อนอื่นสร้างไฟล์ใหม่ ชื่อว่า upload.gradle
ก็แล้วกันเนอะ อยู่ folder เดียวกับ build.gradle
ของ module แล้วก็พิมพ์คำสั่งแบบนี้ ซึ่งคำสั่งนี้ทำงานกับ gradle 7 แต่ถ้าใช้ version ของ gradle ตํ่ากว่านี้ก็ท่าประมาณนี้แหละ syntax ใกล้เคียงกัน
- การใส่พวก
groupId
artfaceId
version
แรกๆเราจะงงว่าใส่ยังไง จริงๆถ้าเราอัพ pom ขึ้นไปเสร็จแล้ว หน้าตาจะเป็น format แบบนี้เลย มี colon คั่น
{groupId}:{artifactId}:{version}
Ex. androidx.recyclerview:recyclerview:1.2.1
url
ถ้าเราขึ้นแบบ local บนเครื่องเราเอง จะใส่เป็นแบบนี้ก็ได้ แต่ถ้าใช้ของเจ้าอื่นเขาจะมี url format ให้ใส่ตาม document- ถ้าขึ้น local บนเครื่องของเราเอง ไม่ต้องใส่ credentials ก็ได้ แต่ถ้าขึ้นบางทีอาจจะต้องใช้ เพราะว่ามันมีเรื่อง permission ว่าคนไหนเข้าได้ไม่ได้ ได้แค่ไหน
และเราสามารถไปเก็บพวก url หรือ username password ได้ที่ไฟล์ gradle.properies
หรือเก็บไว้ใน local.properties
ถ้าไม่อยากเอาขึ้น git นะ อันนี้แล้วแต่สะดวกเลย
url=”/helloaar/”
username=USERNAME
password=PASSWORD
หน้าตาโค้ดโดยรวมเนอะ
การบิ้ว pom ของเราขึ้นไป ไปที่แถบ gradle แล้วจิ้ม
{Project_Name} -> {Library_Module} -> Tasks -> publishing -> publish
เมื่อเรา upload POM เสร็จแล้ว จะได้ผลลัพธ์อะไรกลับมากันนะ ?
จากรูปนี้ เราจะเห็นของ 2 ส่วนด้วยกัน คือ folder ที่มีเลข version และ metadata เราจะมาดูทีละส่วนกัน
- folder ของเลข version จะมีไฟล์ aar และไฟล์ pom
ข้างในไฟล์ pom จะประกอบไปด้วย groupId
artifaceId
version
เจ้า packaging
เป็น aar แล้วก็ dependency
ต่าง ๆ ที่ library ของเราใช้งานด้วย โดยถ้าเราเรียกใช้ library ตัวนี้ จะดึงของ dependency มาที่ local นะ
- metadata ข้างในประกอบด้วย
groupId
และartifactId
เช่นเดิม ส่วนversioning
จะมีrelease
คือ version ล่าสุด และversions
คือ version ทั้งหมด แล้วก็lastUpdated
บอกว่าอัพเดตล่าสุดเมื่อไหร่ โดยมันจะเป็น milliseconds นะ
การนำ POM มาใช้งาน
ไปที่ build.gradle ของ project ใส่เพิ่มเติมไปดังนี้ ตรง allproject ที่ build.gradle
ที่ชั้น root นะ ถ้าอันนั้นมี credentials ก็ใส่ไปด้วย
//build.gradle of project
allprojects {
apply plugin: 'maven-publish'
repositories {
maven {
url '/helloaar/'
credentials {
username "${username}"
password "${password}"
}
}
}
}
การใช้งาน ไปที่ build.gradle
ของ module แล้วก็เรียก dependency ไปตามปกติเลย แล้วก็ build ไปสักหนึ่งรอบก็ใช้งานได้หล่ะ
//build.gradle of module
implementation "{groupId}:{artifactId}:{version}"
implementation "androidx.recyclerview:recyclerview:1.2.1"
ข้อควรระวัง
- minSdkVersion & targetSdkVersion: ควรทำให้มัน compat ให้แอพอื่นๆสามารถใช้งาน library ของเราได้นะ ไม่ใช้อันใหม่จนเกินไป ไม่งั้นจะใช้ไม่ได้
- Library Conflict: อันนี้ทำ modular architecture ก็เจอได้ สมมุติถ้าใน library หรือไม่ก็ module ชั้นในใช้ version library ที่ใหม่กว่าชั้นนอก เช่น module app ก็ทำให้โปรเจกต์ของเรา build ไม่ผ่านเนอะ
วิธีแก้ก็คือ set version ของ library ไว้ที่ build.gradle
ชั้นนอกสุดเลย และตัวแปรนี้ไปใช้งาน ก็จะหมดปัญหาไปหนึ่ง
แต่ถ้าในโปรเจกต์เราใช้ version เดียวกันหมดแล้ว แต่ใน pom ใช้ version ที่ไม่เหมือนกัน ให้ใช้ force scope เมื่อมี dependency version ที่ไม่เท่ากัน เพื่อให้เอามาจาก global หรือใน pom แทนในโปรเจกต์
- Resource Conflict: เราควรหลีกเลี่ยงการตั้งชื่อ resource ที่เหมือนกัน ไม่เช่นนั้นจะถูกแอปหลัก override ได้
เช่น ใน library ของเรา เป็นปุ่มสี่เหลี่ยมขอบมนหน่อยๆ มี icon หน้าคำว่า ADD แต่ในแอพหลักเราก็ดันมีปุ่มเพิ่มเหมือนกัน แต่เป็นปุ่มแบบรีๆ และมีแค่คำว่า ADD สุดท้ายเมื่อเรารันแอพ เราจะเห็นว่าปุ่มที่เป็นส่วนของ library จะถูกเปลี่ยนไปงี้
วิธีแก้ ให้ใส่ชื่อ library เป็น prefix ไว้ข้างหน้า เป็นการหลีกเลี่ยงการถูก override ของ resource นะ
ถ้าเราต้องใส่ prefix เยอะมาก ๆ แล้วกลัวหลุด อาจจะใช้อันนี้ในการช่วย check ในการที่เราใส่ prefix ต่างๆของแต่ละ xml หรือ resource ว่าเราใส่ไปแล้วนะ ถ้าเราไม่ได้ใส่มันจะเตือนแดงๆให้
//module build.gradle of library
android {
sdkResourcesPrefix 'YOUR_PREFIX_'
}
- Proguard + minify: ถ้าเรา enable ProGuard และทำ minify ตอนที่จะเอา library ขึ้นไป ชื่อไฟล์อาจจะถูกเปลี่ยนเป็นประมาณ
a.b
ซึ่ง library อื่นอาจจะโดนเปลี่ยนชื่อแล้วโดนชื่อเหมือนกันได้ และเมื่อแอพเราใช้ lib 2 ตัวนี้ มันจะโดน duplicate file เป็น compile error
วิธีแก้ ใช้ option -repackageclasses a.example.package
บน ProGuard เวลา obfuscate มันจะย้ายไป folder ที่เรากำหนด a.example.package
- Don’t forget Unit Testing: สุดท้าย ก่อนส่ง library ให้คนอื่นใช้ อย่าลืมทำ Unit Test และถ้าใช้ในองค์กร ให้ tester สุดน่ารักของเราช่วยดูอีกรอบก็ได้นะ
Beyond to open source
สำหรับใครที่อยากเอา library ของเราออกสู่ชาวโลก เป็น open source เนอะ
การทำ README.md
โปรเจกต์ open source ที่ดี ต้องมี readme ที่อ่านแล้วเข้าใจง่าย ง่ายๆคือคนที่จะมาใช้ เขาต้องรู้ว่า library นี้ทำอะไรได้บ้าง เอาไปติดตั้งยังไง เอาไปใช้งานยังไงต่อ ตอนนี้ version เท่าไหร่ และตบท้ายด้วย license ของ project
อันนี้เป็นตัวอย่าง เอามาประยุกต์ใช้ได้
หรือเข้าเว็บนี้ก็ได้นะ มี template ให้เลย
ตัวอย่าง Android Library ที่เป็น open source ว่าเขาใส่ readme ยังไงเนอะ ซึ่งทางเราหยิบ library ตัวนี้ของพี่เอกมาศึกษาดูจ้า
- ด้านบนเราสังเกตว่า จะมี badge ต่างๆ บอกว่า library นี้อยู่บนเว็บ Android Arsenal นะ ตอนนี้เป็น version เท่าไหร่ มี minSdkVersion เท่าไหร่ เทสเป็นไง
- library นี้คืออะไร
- วิธีติดตั้งให้โปรเจกต์
- การใช้งาน library ตัวนี้
- demo เพื่อบอกว่าเอ้ออ ใช้แล้วผลเป็นยังไง มีหน้าตายังไง เอาให้เราตัดสินใจได้ว่า library นี้ตรงกับที่ต้องการหรือไม่
- สุดท้าย license ส่วนใหญ่จะใช้ Apache License, Version 2.0 กันนะ
ซึ่ง document ของ Android ก็พูดเรื่องนี้ไว้เช่นกัน
การใส่ license ของ library จะมีผลต่อแอพที่ใส่เจ้า Open Source Notices ด้วยนะ อันนี้เป็นตัวอย่าง เราลองส่อง library ในตำนานของพี่เอก ที่ตอนนี้ไมได้ทำต่อแล้ว เพราะ Android 13 support เรื่องภาษาในแอพแล้ว เมื่อกดไปดูเราก็จะเห็น license ของ library นั้น ๆ
เมื่อไป check บน github พบว่าตรงกันเนอะ คือดึงจากตรงแถวๆนี้
สามารถอ่านเรื่อง Open Source Notices ต่อได้ที่นี่เลย
ปล. อยากอ่านเรื่อง license เพิ่ม ไปดูได้ที่นี่เลย
Create Release in Github & Publish Library with Jitpack
สามารถอ่านต่อได้ที่บล็อกนี้เลยจ้า
Bonus
- Jitpack สามารถทำ private repo ได้ด้วยนะ ดูตามราคาในนี้ได้เลย
- สามารถอ่านวิธีการ set pom ขึ้น jitpack จากบล็อกคุณคนนี้ได้ ซึ่งเขาใช้ gradle 7 ทำไม่ยาก แล้วก็ตัวของที่ต้องใช้จะคล้ายๆกัน แล้วแต่แต่ละเจ้าว่าใส่อะไรเพิ่มเติมไปบ้าง
อันนี้สไลด์ที่ขึ้นพูดนะ
.
สุดท้ายขอบคุณทีม ที่ช่วยเหลือในเรื่องของข้อมูล ทำให้ session นี้สมบูรณ์ยิ่งขึ้น
ขอบคุณทุกคนที่มาฟัง ที่เสียสละวันศุกร์ที่เป็นวันทำงาน มางานนี้ และมาฟัง session ของเรา
และขอบคุณงาน Android Bangkok 2022 ด้วยนะคะ สำหรับโอกาสที่ได้ขึ้นพูดอีกครั้งนึง
ปิดท้ายบล็อกนี้ด้วยรูปจากพี่ตี๋นะคะ ขอบคุณค่า
สามารถ support ค่ากาแฟเจ้าของบล็อกได้ที่ปุ่มแดงส้มสุดน่ารักที่มุมซ้ายล่าง หรือกดปุ่มตรงนี้ก็ได้จ้า
ช่องทาง Twitter ติดตามข่าวสารแบบไว ๆ
ติดตามข่าวสารและบทความใหม่ ๆ ได้ที่
ช่องทางใหม่ ติดตามทุก ๆ สตรีมของเราได้ที่
Subscribe ช่อง YouTube ของเราได้ที่
download แอพอ่านบล็อกใหม่ของเราได้ที่นี่