Chào mừng các bạn đến với chủ đề về Android Jetpack.
Hôm nay, để bắt đầu đi sâu về một thư viện cụ thể của Jetpack, chúng ta sẽ nói đến cái tên Android KTX.
Như được giới thiệu ở phần đầu, Android KTX được Google trình làng cùng thời điểm với đợt giới thiệu lần đầu tiên về Android Jetpack. Chính vì lẽ đó mà mình cũng chọn em nó cho bài viết cụ thể về Jetpack đầu tiên này.
Android KTX này thực sự rất thú vị, tuy đôi lúc nó gây ra sự bối rối đối với các lập trình viên nếu không biết về thể loại thư viện này, bởi thay vì dùng các cách được khuyến cáo của từng loại thư viện, Android KTX lại cho ra một cách viết khác. Nguyên nhân bởi vì Android KTX không phải một dạng thư viện phục vụ cụ thể cho một mục đích nào đó như anh em của nó trong họ Jetpack, mà nó giúp tăng cường tính “thẩm mỹ” cho các thư viện họ hàng, giúp các lập trình viên dễ dàng thao tác với các thư viện Jetpack và cả không phải Jetpack khác, bằng cách cung cấp những dòng code đẹp hơn, dễ nhìn hơn. Và dĩ nhiên cũng giúp giảm lỗi hơn với việc code ngắn gọn và xúc tích hơn kiểu này. Ngoài việc mang đến một ngoại hình mới cho các dòng code của các thư viện đã có, Android KTX còn giúp chúng ta có những ý tưởng thú vị hơn trong việc xây dựng nên những Kotlin Extension cho project của chúng ta. Tất cả những ý đẹp đẽ trên đây cụ thể như thế nào mời các bạn cùng vào bài viết với mình.
Android KTX Là Gì?
Trước hết là ở cái tên. Thực sự ngay khi vừa đọc đến, dù biết là khá nhảm, nhưng cái chữ KTX gợi cho mình cái tên Ký Túc Xá!!! Nói cho vui thôi, thực chất Android KTX là một từ viết ngắn hơn và có một tí chơi chữ của từ Android Kotlin Extensions. Đọc cái tên cũng mở ra cho chúng ta một tí ý niệm, đó là đây sẽ là các Tiện ích mở rộng của Kotlin để giúp xây dựng ứng dụng Android.
Tiện ích này là các thành phần được xây dựng sẵn, được tổng hợp lại thành các gói thư viện riêng lẻ để mở rộng, để bổ sung cho các thư viện sẵn có. Giúp chúng ta xây dựng các chức năng thay vì dùng các code cũ thì với Android KTX, code Kotlin của chúng ta sẽ trở nên đẹp đẽ, quyến rũ và dễ đọc hơn.
Dưới đây là video của Google về Android KTX, nếu bạn không thích xem video thì cứ đọc các mục của mình bên dưới cũng được.
Một Ví Dụ Áp Dụng Android KTX
Để dễ hình dung hơn về Android KTX, chúng ta hãy nhìn qua một ví dụ sau. Ví dụ này đơn giản là sử dụng Shared Preferences để lưu trữ một giá trị Boolean vào bộ nhớ của thiết bị, đây là cách viết truyền thống hẳn nhiều bạn không hề xa lạ.
val sharedPref = this.getSharedPreferences("com.myapp.shared_preferences", Context.MODE_PRIVATE) val editor = sharedPref.edit() editor.putBoolean(SHOW_DELETED_WORDS_KEY, enable) editor.apply()
Nếu bạn đã quen dùng các dòng code này rồi thì có nhận ra rằng đôi khi chúng ta quên mất dòng editor.apply()
cuối cùng, hoặc để có thể lưu trữ các key-value này thì dòng code khai báo editor = sharedPref.edit()
thể hiện sự dư thừa trong code hay không. Và đây là cách viết bằng Android KTX, bạn so sánh nhé.
val sharedPref = this.getSharedPreferences("com.myapp.shared_preferences", Context.MODE_PRIVATE) sharedPref.edit { putBoolean(SHOW_DELETED_WORDS_KEY, enable) }
Rõ ràng là chúng ta không cần phải để ý đến editor, và như vậy sẽ không sợ quên dòng editor.apply()
đúng không nào.
Bạn đã hiểu cách mà Android KTX giúp chúng ta viết code như thế nào chưa nào.
Android KTX Xây Dựng Các Tiện Ích Mở Rộng Như Thế Nào
Mục này mình nói thêm cho bạn nào thích tìm hiểu sâu, nếu bạn không muốn quan tâm Android KTX thực chất là gì thì có thể xem qua mục tiếp theo để biết cách dùng Android KTX ở những trường hợp cụ thể nhé.
Như ví dụ trên, bạn có thể lầm tưởng rằng Android KTX can thiệp vào đối tượng SharedPreferences và làm thay đổi nó. Thực chất không phải vậy, phần lớn sự chỉnh sửa của Android KTX là dựa vào một chức năng của ngôn ngữ Kotlin có tên Extension functions. Tính năng này của Kotlin giúp chúng ta mở rộng các function cho các lớp Kotlin có sẵn mà không cần phải sửa chữa hay kế thừa lớp đó.
Nếu nhìn vào các dòng code đã xây dựng nên SharedPreferences.edit(), bạn sẽ hiểu rõ hơn.
inline fun SharedPreferences.edit( commit: Boolean = false, action: SharedPreferences.Editor.() -> Unit ) { val editor = edit() action(editor) if (commit) { editor.commit() } else { editor.apply() } }
Bạn thấy Extension functions này cho phép chúng ta truyền vào 2 tham số. Tham số commit có giá trị mặc định là false nên nếu bạn không truyền vào giá trị cho tham số này thì việc code sẽ như ví dụ trên kia, còn nếu bạn muốn truyền tham số vào thì có thể viết tường minh ra như thế này.
val sharedPref = this.getSharedPreferences("com.myapp.shared_preferences", Context.MODE_PRIVATE) sharedPref.edit(commit = false) { putBoolean(SHOW_DELETED_WORDS_KEY, enable) }
Bạn đã hiểu Android KTX hoạt động như thế nào chưa. Với mục này thì mình “mổ xẻ” một em ra làm ví dụ thôi nhé. Mục tiếp theo đây mình chỉ liệt kê các cách cũ và cách Android KTX để bạn so sánh và áp dụng thôi, bạn tự tìm hiểu kỹ chúng nha.
Áp Dụng Android KTX Cho Một Số Thư Viện
Android KTX được xây dựng cho hơn 20 thư viện khác nhau, cả cho Jetpack lẫn các thư viện ngoài Jetpack. Do đó việc áp dụng Android KTX cho thư viện nào là hoàn toàn tùy ở bạn. Dưới đây là nguyên tắc nếu bạn muốn dùng Android KTX cho một thư viện nào đó.
Có một nguyên tắc nếu bạn đang xây dựng ứng dụng bằng ngôn ngữ Kotlin, đó là hãy thêm -ktx vào sau các thư viện được khai báo. Thì bạn sẽ vừa có thư viện đó, vừa có Android KTX bổ sung cho thư viện đó. Tất nhiên là chỉ áp dụng -ktx đươc với các thư viện được Android KTX hỗ trợ thôi nhé, chi tiết những thư viện nào thì bạn có thể xem thêm ở link này của Google.
Mình giả sử như nếu project bạn đang code bằng Java và có khai báo sử dụng thư viện ViewModel.
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
Thì với project viết bằng Kotlin, bạn hãy sử dụng thư viện ViewModel với -ktx hỗ trợ luôn như sau.
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
Dạo Qua Một Vài Android KTX
Như mục trên có nói rằng Android KTX được xây dựng cho hơn 20 thư viện khác nhau. Thế thì nếu chúng ta nói hết về Android KTX này thì nhiều lắm. Chính vì vậy mình sẽ tuyển chọn ra vài em thường dùng để chúng ta cùng dạo qua. Không chỉ các thư viện mở rộng cho Jetpack mà còn cho các thành phần quen thuộc khác nữa nhé. Mình nhắc lại rằng nếu bạn nào quan tâm có bao nhiêu thư viện Android KTX mà Google hỗ trợ thì có thể thể “ngâm cứu” thêm ở link này của Google nhé.
Core KTX
Với Android KTX của nhóm này thì bạn chỉ cần xem project của mình đã khai báo thư viện sau trong build.gradle của module chưa nhé.
dependencies { implementation "androidx.core:core-ktx:1.3.2" }
SharedPreferences
Như ví dụ trên kia, mình không nói nhiều, mỗi khi bạn muốn sử dụng SharedPreferences trong Kotlin được ngắn gọn và thú vị hơn, hãy dùng Android KTX.
val sharedPref = this.getSharedPreferences("com.myapp.shared_preferences", Context.MODE_PRIVATE) sharedPref.edit(commit = false) { putBoolean(SHOW_DELETED_WORDS_KEY, enable) }
String to Uri
Mỗi khi bạn parse một String thành Uri. Thay vì dùng cách bình thường như nào giờ vẫn dùng.
val uri = Uri.parse(myUriString)
Thì hãy thử với Android KTX xem có dễ nhìn hơn không nhé.
val uri = myUriString.toUri()
TextWatcher
Nếu bạn đã từng lắng nghe sự kiện text thay đổi trên một EditText, như sau.
edtWatcher.addTextChangedListener(object : TextWatcher { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { tvWatcher.text = s.toString() } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } override fun afterTextChanged(s: Editable?) { } })
Thì bạn có thấy rằng có quá nhiều dòng code dư thừa do chúng ta buộc phải implement hết tất cả các function bên trong interface TextWatcher. Sự thực là chúng ta chỉ cần override mỗi onTextChanged mà thôi. Vậy hãy xem Android KTX giúp chúng ta giải quyết bài toán này như sau.
edtWatcher.doOnTextChanged { text, start, before, count -> tvWatcher.text = text }
Tạo Bundle
Nếu bạn muốn truyền dữ liệu nhỏ giữa các Activity hay Fragment thông qua Bundle, thay vì tạo Bundle theo cách cũ.
val bundle = Bundle() bundle.putString("name", "My Name") bundle.putInt("age", 18)
Thì Android KTX giúp code này trông đẹp hơn như sau.
val bundle = bundleOf( "name" to "My Name", "age" to 18 )
Activity KTX
Thư viện cần khai báo.
dependencies { implementation "androidx.activity:activity-ktx:1.2.2" }
Khai báo ViewModel
Mặc dù chức năng này hỗ trợ cho việc khai báo ViewModel, nhưng nó không thuộc thư viện mở rộng cho ViewModel, mà là mở rộng cho Activity nhé.
Nếu trong Activity, bạn hay khai báo một ViewModel như sau.
class MainActivity : AppCompatActivity() { private lateinit var viewModel: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { // ... viewModel = ViewModelProvider(this).get(MainViewModel::class.java) // ... } }
Thì hãy chuyển sang sự hỗ trợ của Android KTX xem như thế nào nhé.
class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels() // ... }
Fragment KTX
Để dùng Android KTX ở nhóm này, bạn hãy khai báo thư viện sau.
dependencies { implementation "androidx.fragment:fragment-ktx:1.3.2" }
Fragment Transaction
Nếu bạn đã từng dùng qua việc add fragment động vào một FrameLayout như sau.
supportFragmentManager .beginTransaction() .replace(R.id.container, SampleFragment.newInstance(), SampleFragment.TAG) .commitAllowingStateLoss()
Vậy thì cách dùng này của Android KTX có làm bạn cảm thấy thích hơn không nào.
supportFragmentManager.commit(allowStateLoss = true) { replace(R.id.container, SampleFragment.newInstance(), SampleFragment.TAG) }
Khai Báo ViewModel
Nếu Android KTX hỗ trợ khai báo ViewModel bên trong Activity như trên kia, thì với Fragment cũng vậy.
Bạn khai báo ViewModel bằng cách cũ trong Fragment như sau.
class HomeFragment : Fragment() { private lateinit var viewModel: MainViewModel // ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java) // ... } }
Việc khai báo trên đây rất dễ dẫn đến một điều là các viewModel ở các Fragment bỗng dưng chẳng liên quan gì với nhau, bạn đã set giá trị cho viewModel ở Fragment này, mà qua Fragment khác giá trị đó vẫn như cũ. Đó là vì bạn rất dễ bị nhầm giữa việc khai báo ViewModelProvider(requireActivity())
thành ViewModelProvider(this)
lắm nhé.
Để tránh lỗi trên và code được gọn hơn nhiều, Android KTX viết như sau.
class HomeFragment : Fragment() { private val viewModel: MainViewModel by activityViewModels() // ... }
LiveData KTX
Khai báo thư viện.
dependencies { implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' }
Observer
Bạn có nhớ với LiveData bạn thường xuyên implement Observer như thế nào không.
viewModel.articles.observe(this, Observer { articles -> articles?.forEach { // Do your job } })
Giờ thì bạn có thể viết ngắn hơn một chút.
viewModel.articles.observe(this) { articles -> articles?.forEach { // Do your job } }
ViewModel KTX
Khai báo thư viện.
dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1" }
CoroutineScope
Mỗi khi bạn dùng đến Coroutines bên trong một ViewModel, hãy thoải mái gọi đến viewModelScope(). Khi này một Coroutines sẽ được tạo với Scope gắn liền với đời sống của ViewModel đó.
class MainViewModel : ViewModel() { private fun makeNetworkRequest() { viewModelScope.launch { // ... } } }
Kết Luận
Mặc dù mình cảm thấy những liệt kê trên của mình về Android KTX là chưa đầy đủ lắm, thậm chí là khá ngắn ngủi so với sự hỗ trợ hùng hậu từ thư viện này. Vậy thì một lần nữa, mình muốn các bạn hãy tự vào trang chính thống này của Google, thử nghiên cứu và suy nghĩ xem mỗi một hỗ trợ này của Android KTX sẽ được dùng như thế nào vào các project của riêng bạn nhé.
Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao ở mỗi bài viết nếu thấy thích.
– Comment bên dưới mỗi bài viết nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
– Ủng hộ blog theo hướng dẫn ở thanh bên phải để blog ngày càng phát triển hơn.
Rõ ràng và súc tích. Thanks Ad!