Skip to content

hanhmh1203/mvvm-structure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GEAR BY KOTLIN AND MVVM

structure:

Image of structure

Image of structure

buildSrc:

 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)
example build gradle:

model:

Include all entities in project

local:

Implement Room to management database.

remote:

Implement Retrofit to get data from api

repository:

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. 

common:

1. Util class.
2. Base class.
3. views need to re-use (dialog..)
4. Extension 

app:

Main module. 
1. AndroidManifes.xml
2. Activities, Fragment, Viewmodel.
3. layout
4. Dependency injection.
5. Use case class.

Technical in used

Dependency injection: Dagger
AppComponent: with 2 modules: AppModule::class, ActivityProvider::class
  1. AppModule: with modules network, database, repository, viewModel
  2. ActivityProvider: provide activities, FragmentProvider.
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
Example: ThirdFragment and layout: fragment_third
File location: res/navigation
1 activity has 1 nav_graph.

navigation_graph

Actions: define action to replace fragment.
Arguments: this is safe-args, it's send data between fragments.

destinations

List fragment in graph

navigation_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      
Example: app_nav_graph

Image of ViewModel LifeCycle

- 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)
to understanding livedata: link
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){
            }
        })

ROOM in module: local

AppDatabase

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.
Example: UserGitDao

Retrofit in module: remote

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

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>
File example Coroutines: CoroutinesUseCase.kt

WorkManager

component
Worker, WorkRequest, WorkManager, WorkStatus
Workflow

WorkerUsercase.kt

UploadWorker.kt

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)  

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages