일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- BOJ
- CoordinatorLayout
- Algorithm
- DataBinding
- LiveData
- room
- Navigation
- AppBarLayout
- View
- kotlin
- 알림
- Coroutine
- activity
- 알고리즘
- onMeasure
- hilt
- HTTP
- lifecycle
- 안드로이드
- recyclerview
- ViewModel
- CollapsingToolbarLayout
- Android
- notification
- sqlite
- Behavior
- 백준
- onLayout
- 코틀린
- CustomView
- Today
- Total
개발일지
Android in A..Z - Hilt (개념) 본문
Hilt
기존의 Dagger2를 Android의 구조적으로 맞게 기능을 추가한 라이브러리이다. DI를 도와주며 Annotaion을 통해 보일러 플레이트 코드를 제거하고 쉽게 사용할 수 있다.
DI (Dependency Injection)
Android뿐만 아니라 프로그래밍에서 널리 사용되는 기법이고 다양한 이점이 있다.
- 코드의 재사용성
- 리팩토링 용이성
- 테스트 용이성
클래스에서 다른 클래스를 참조하는 방법은 크게 3가지가 있습니다. (Car와 Engine을 예시로)
1. 클래스에서 필요한 종속 클래스를 인스턴스화하는 방법
class Car {
private val engine = Engine()
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.start()
}
위와 같은 방법은 Car와 Engine이 밀접하게 연결되어 있기 때문에 문제가 발생합니다.
1. 만약 Engine를 상속받은 GasEngine, ElectricEngine Class가 있을 때 쉽게 대체할 수 없습니다.
=> Engine이라는 한가지 유형을 사용하기 때문에 수정하면 연관된 코드를 수정해야 할 가능성이 있다.
2. 테스트를 더욱 어렵게 한다.
=> Engine을 FakeEngine으로 바꿔서 테스트 하려면 Engine과 연관된 코드를 수정해야 한다.
2. getter / setter를 이용한 방법 (필드 삽입, setter 삽입)
class Car {
lateinit var engine: Engine
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.engine = Engine()
car.start()
}
3. 생성자를 통해 넘겨주는 방법 (생성자 삽입)
class Car(private val engine: Engine) {
fun start() {
engine.start()
}
}
fun main(args: Array) {
val engine = Engine()
val car = Car(engine)
car.start()
}
2, 3번이 DI를 이용한 프로그래밍 방법이고 1번에서 발생한 문제점을 쉽게 해결할 수 있습니다.
=> Engine의 서브 클래스들을 쉽게 대입할 수 있기 때문에 Car을 재사용할 수 있고, 쉽게 테스트할 수 있다.
Hilt는 2, 3번을 쉽게 도와주며 중복 코드와 보일러 코드를 최소화 할 수 있습니다.
Dependency
build.gradle(Module)
plugins {
id 'dagger.hilt.android.plugin'
}
dependencies {
// Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}
build.gradle(Project)
ext {
hilt_version = "2.31.2-alpha"
}
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
}
@HiltAndroidApp
Hilt를 사용하는 모든 Application은 @HiltAndroidApp으로 지정된 Application Class를 포함해야 합니다.
@HiltAndroidApp으로 지정된 Application Class는 Hilt의 코드 생성을 트리거합니다.
MainApplication.kt
Application을 상속받는 Class를 만들고, @HiltAndroidApp Annotation을 지정한다.
@HiltAndroidApp
class MainApplication : Application()
AndroidManifest.xml
application에 Name을 추가한다. (@HiltAndroidApp Annotation이 지정된 Application Class로 추가한다.)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taetae98.hilt">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:usesCleartextTraffic="true"
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Hilt">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@Inject
Hilt를 사용하여 의존성을 주입할 때 사용하는 Annotation이다.
1. 주입할 클래스에 @Inject 생성자를 만든다.
@FragmentScoped
class SummonerInformationAdapter @Inject constructor() : BaseAdapter<SummonerInformation>(SummonerInformationItemCallback()) {
init {
setHasStableIds(true)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder<out ViewDataBinding, SummonerInformation> {
return SummonerInformationHolder(HolderSummonerInformationBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun getItemId(position: Int): Long {
return getItem(position).entity.name.hashCode().toLong()
}
inner class SummonerInformationHolder(binding: HolderSummonerInformationBinding) : BaseHolder<HolderSummonerInformationBinding, SummonerInformation>(binding) {
override fun bind(element: SummonerInformation) {
super.bind(element)
binding.information = element
}
}
class SummonerInformationItemCallback() : DiffUtil.ItemCallback<SummonerInformation>() {
override fun areItemsTheSame(oldItem: SummonerInformation, newItem: SummonerInformation): Boolean {
return oldItem.entity.name == newItem.entity.name
}
override fun areContentsTheSame(oldItem: SummonerInformation, newItem: SummonerInformation): Boolean {
return oldItem == newItem
}
}
}
2. 주입받는 객체에 @AndroidEntryPoint를 지정하고 주입받을 필드에 @Inject를 지정한다. (private, val 키워드를 사용할 수 없다. Hilt가 Class를 인스턴스화하고 주입하기 때문이다.)
@AndroidEntryPoint
class MainFragment : BaseFragment<FragmentMainBinding>(R.layout.fragment_main) {
@Inject
lateinit var summonerEntityRepository: SummonerEntityRepository
@Inject
lateinit var summonerInformationAdapter: SummonerInformationAdapter
private val summonerEntityViewModel by viewModels<SummonerEntityViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
summonerEntityViewModel.summonerInformationLiveData.observe(viewLifecycleOwner) {
summonerInformationAdapter.submitList(it)
}
}
override fun init() {
initSupportActionBar()
initSummonerWithEntityRecyclerView()
initOnAdd()
}
private fun initSupportActionBar() {
setSupportActionBar(binding.toolbar)
}
private fun initSummonerWithEntityRecyclerView() {
with(binding.summonerWithEntityRecyclerView) {
adapter = summonerInformationAdapter
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
if (viewHolder is SummonerInformationAdapter.SummonerInformationHolder) {
summonerEntityRepository.deleteSummonerEntity(viewHolder.element.entity)
}
}
}).attachToRecyclerView(this)
addItemDecoration(GridSpacingItemDecoration(10))
}
}
private fun initOnAdd() {
binding.setOnAdd {
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSummonerEntityEditDialog(
SummonerEntity()
))
}
}
}
Git (예제코드)
github.com/KangTaeJong98/Example/tree/main/Android/Hilt
'Android (안드로이드) > Hilt' 카테고리의 다른 글
Android in A..Z - Hilt (Entry Point) (0) | 2021.03.30 |
---|---|
Android in A..Z - Hilt (Component) (0) | 2021.03.18 |
Android in A..Z - Hilt (Module) (0) | 2021.03.18 |