Gradle Dependency Management
1. Create a root level directory called buildSrc
2. Inside buildSrc create the file build.gradle.kts and enable the kotlin-dsl plugin with the snippet in build.gradle.kts.
3. Create file Dependencies.kt for define libraries
4. Can code build gradle by kotlin language (not implement)
1. Handle get data from api and save in database.
2. Expose data result
1. Wrapper get data from local database and get data from api.
2. Data result always in object Resource
3. We can force always load from network or local.
1. Binding data from viewmodel to layout with two ways.
2. handle action to view (click, input text)
add this code into root of view fragment.
<data>
<variable
name="viewModel"
type="com.example.gear_kotlin.viewmodel.ThirdFragmentViewModel" />
</data>
- One class _fragmentName_Binding will automatic create.
Example is: ThirdFragmentBinding
And in Fragment: thirdFragmentBinding.viewModel = viewModel
File location: res/navigation
1 activity has 1 nav_graph.
Actions: define action to replace fragment.
Arguments: this is safe-args, it's send data between fragments.
List fragment in graph
1. After build, it will auto generate file Directions with name: fragmentName + Directions,
example: AppFirstFragmentDirections
2. Call action with parameter like this:
val action = AppFirstFragmentDirections.setParamInt(param1).setParamStr(param2)
view.findViewById<Button>(R.id.button_first).setOnClickListener {findNavController().navigate(action)}
3. Get data from args like this:
val safeArgs: AppSecondFragmentArgs by navArgs()
val paramInt = safeArgs.paramInt
val paramStr = safeArgs.paramStr
- ViewModel never keep context of view (fragment, activity)
- ViewModel init by owner scope (fragment, activity) and ViewModelFactory.
- Using LiveData to expose to Views (Fragment, Activity)
init 1 ViewModel in fragment:
1. Add DI to ViewModelModule
2.
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
3. in onViewCreated()
viewModel = getViewModel(viewModelFactory)
LiveData
What is LiveData?
If we look at the definition form the Android website then it says:
LiveData is an observable data holder class. LiveData is lifecycle-aware,
meaning it respects the lifecycle of other app components, such as activities, fragments, or services.
This awareness ensures LiveData only updates app component observers that are in an active lifecycle state
1. MutableLiveData should be used only for notifying your UI when observing any data.
2. MediatorLiveData merge multi LiveData (by addSource and removeSource)
We have two ways to for set value for LiveData
1. livedata.value = xxx -> only set on mainthread.
2. livedata.postValue(xxx) -> can set in background thread.
example implement LiveData like above:
in ViewModel:
var toHome = MutableLiveData<Boolean>()
fun toHome(){
toHome.value = true
}
and in Fragment
viewModel.toHome.observe(viewLifecycleOwner, Observer {
if(it){
}
})
When we want to create 1 table we need to:
1. Create 1 file Entity
2. add class entity to AppDatabase
3. Create abstract class DAO
DAO object
return 2 type:
1. suspend function: always use ( I will explain suspend function in Coroutines)
2. normal function return type Flow: this is new in coroutines, I have not implement yet.
Define api in class api.
Define function call api in DataSource
example call an api in viewmodel
private fun getUsers(forceRefresh: Boolean) = viewModelScope.launch(dispatchers.main) {
withContext(dispatchers.io) {
usersSource = getTopUsersUseCase()
}
_users.addSource(usersSource) {
_users.value = it
}
}
EventBus
1. Already add by Dagger
2. @Inject EventManager for post and receive
3. Define requestCode in MessaeEvent
with receiver: register in onCreate, unregister in onStop
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onMessageEvent(event: MessageEvent) { /* Do something */
}
Can Subscribe with Main thread or Background.
Example With post:
val message = Message()
message.data = bundleOf(
Pair("arg1", "Arg1 From App Second Fragment"),
Pair("arg2", "Arg 2 From App Second Fragment")
)
eventManager.post(MessageEvent(MessageEvent.REQUEST_MOOD_RATING_DONE, message))
Coroutines
Coroutines offer main-safety
1. In ViewModel: using viewModelScope (viewModelScope.launch..)
2. In Activity, Fragment: using lifecycleScope (lifecycleScope.launch..)
3. Dispatchers already provide with DI
Dispatchers.IO: This Dispatcher is used to execute long-running and blocking I/O operations
Dispatchers.Main: Confines itself to the Main thread, and hence, only using one thread
Dispatchers.Default: Using CPU, core of device
Dispatchers.Unconfined: shouldn't normally be used in code.
launch (scope.launch): return Unit()
async (scope.async): return a Deferred<T>
1. Create subclass of Worker -> Result Sucess, Failure, Retry
2. Defines Contraints:
.setRequiresBatteryNotLow(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
3. Create WorkRequest
- OneTimeWorkRequest to run work only one time.
- PeriodicWorkRequest to run periodically, set the interval between to subsequent work. (min in 15 minutes)
4. Schedule the request
WorkManager.getInstance().enqueue(request)