일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
31 |
- 막내의막무가내 프로그래밍
- 막내의막무가내 안드로이드 에러 해결
- 막내의막무가내 일상
- 막내의막무가내 알고리즘
- 부스트코스
- Fragment
- 막내의막무가내 코틀린
- 안드로이드
- 막내의막무가내 코볼 COBOL
- 막내의막무가내 목표 및 회고
- 막내의막무가내
- 막내의막무가내 rxjava
- 주택가 잠실새내
- 막내의막무가내 안드로이드 코틀린
- 부스트코스에이스
- 2022년 6월 일상
- 막무가내
- 막내의 막무가내 알고리즘
- 안드로이드 Sunflower 스터디
- 주엽역 생활맥주
- flutter network call
- 막내의 막무가내
- 막내의막무가내 코틀린 안드로이드
- 막내의막무가내 플러터 flutter
- 막내의막무가내 플러터
- 프로그래머스 알고리즘
- 프래그먼트
- 막내의막무가내 SQL
- 막내의막무가내 안드로이드
- 안드로이드 sunflower
- Today
- Total
막내의 막무가내 프로그래밍 & 일상
[안드로이드] Android Hilt 적용해본 코드 기록!! 본문
https://youngest-programming.tistory.com/545
이전에 위 포스팅에서 구글 프로젝트를 통해 Hilt에 대해 정리한 적이 있는데 이번 공모전 개발에서 직접 사용해보았습니다. (시간이 없어 프로젝트는 구성은 개판일지라도 하나라도 새로운 기술을 써보기 위해..ㅎㅎ) 아직 초급단계지만 사용해보며 확실히 Dagger2 를 처음 접했을때보단 훨씬 간편하고 쉬워졌다고 느낄 수 있었습니다...!
적용해본 코드를 기록하여 나중에 참고할 수 있게 간단하게 포스팅 해보려고 합니다.
[프로젝트 레포]
https://github.com/OnzeGgaaziFlow
먼저 아키텍처를 살펴보면 MVVM 구조로 짯고 Model 부분을 API 통신만 연결할거므로 Local, Remote를 나누지않고 Repository만 만들어 사용했습니다.
기록용이고 이전 포스팅에 설명을 달아놨으므로 설명은 생략하도록 하겠습니다. :)
[Application]
@HiltAndroidApp 설정
@HiltAndroidApp
class MyApplication : Application() {
}
[Activity]
@AndroidEntryPoint 설정
@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding>(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initNavigation()
}
[Fragment]
@AndroidEntryPoint 위 MainActivity에 종속되는 Fragment 입니다. 반드시 이것도 어노테이션 지정 필요
@AndroidEntryPoint
class ChartFragment :
BaseFragment<FragmentChartBinding>(R.layout.fragment_chart) {
private val viewModel: ChartViewModel by viewModels()
override fun init() {
binding.vm = viewModel
initView()
initAdapter()
initViewModelCallback()
}
[Adapter]
처음에 어댑터도 밑에 링크 나오는 예제처럼
https://stackoverflow.com/questions/63697582/how-to-inject-adapter-with-hilt
@Inject
lateinit var chartMissionAdapter: ChartMissionAdapter
class ChartMissionAdapter @Inject constructor(private val onItemClick: (IndustryEnergy) -> Unit)
@Module
@InstallIn(SingletonComponent::class)
class AdapterModule {
@Provides
@Singleton
fun provideChatMissionAdapter(): ChartMissionAdapter = ChartMissionAdapter()
private var onItemClickListener: ((IndustryEnergy) -> Unit)? = null
fun setOnItemClickListener(listener: (IndustryEnergy) -> Unit) {
onItemClickListener = listener
}
}
이런식으로 주입시켜 사용하려 했으나 클릭리스너 부분을 상당 부분 바꿔야하고 애매해서 사용하지 않게되었습니다.
[ViewModel]
@HiltViewModel + @Inject constructor로 주입
@HiltViewModel
class ChartViewModel @Inject constructor(private val repository: ChartRepository) :
BaseViewModel() {
[Repository]
싱글톤 모듈을 만들고 생성자 파라미터를 주입받습니다.
class ChartRepositoryImpl @Inject constructor(private val mainApiInterface: MainApiInterface) :
ChartRepository {
@InstallIn(SingletonComponent::class)
@Module
class DatabaseModule {
@Singleton
@Provides
fun provideUserInfoRepository(apiInterface: ApiInterface): UserInfoRepository {
return UserInfoRepositoryImpl(apiInterface)
}
@Singleton
@Provides
fun provideSignInRepository(apiInterface: ApiInterface): SignInRepository {
return SignInRepositoryImpl(apiInterface)
}
@Singleton
@Provides
fun provideChartRepository(mainApiInterface: MainApiInterface): ChartRepository {
return ChartRepositoryImpl(mainApiInterface)
}
}
[ApiModule]
서버와 통신할 Retrofit2 관련 코드들과 모듈들입니다. Header에 인증토큰을 담아야하는 경우와 아닌 경우가 있어 두 가지를 만들었고 @Singleton으로 적용하여 재생성하는 비효율을 줄였습니다.
추가로 @Field 대신 @FieldMap을 사용해도 됩니다.
@InstallIn(SingletonComponent::class)
@Module
class ApiModule {
@Singleton
@Provides
fun provideApiService(): ApiInterface {
return ApiInterface.create()
}
@Singleton
@Provides
fun provideMainApiService(): MainApiInterface {
return MainApiInterface.create()
}
}
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mtjin.envmate.api
import com.mtjin.envmate.data.model.response.LoginRes
import com.mtjin.envmate.data.model.response.SignUpRes
import io.reactivex.Single
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface ApiInterface {
@FormUrlEncoded
@POST("accounts/signup-req")
fun insertUserInfo(
@Field("business_name") businessName: String,
@Field("business_number")
businessNumber: String,
@Field("officer_email")
officerEmail: String,
@Field("officer_name")
officerName: String,
@Field("officer_phone")
officerPhone: String,
@Field("officer_position")
officerPosition: String,
@Field("password")
password: String,
@Field("industry")
industry: String,
@Field("location_name")
locationName: String
): Single<SignUpRes>
@FormUrlEncoded
@POST("accounts/signup-accept")
fun requestSignUpAccept(
@Field("officer_email") officeEmail: String
): Single<SignUpRes>
@FormUrlEncoded
@POST("accounts/login/")
fun requestLogin(
@Field("email") email: String,
@Field("password") password: String
): Single<LoginRes>
companion object {
private const val BASE_URL =
"http://6e3ac85e023a.ngrok.io"
fun create(): ApiInterface {
val logger = HttpLoggingInterceptor().apply {
level =
HttpLoggingInterceptor.Level.BASIC
}
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiInterface::class.java)
}
}
}
package com.mtjin.envmate.api
import android.util.Log
import com.mtjin.envmate.data.model.response.EnvRes
import com.mtjin.envmate.data.model.response.IndustryEnergyRes
import com.mtjin.envmate.utils.UserInfo
import io.reactivex.Single
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
interface MainApiInterface {
@GET("datas/compare/region")
fun requestCompareRegion(): Single<EnvRes>
@GET("datas/compare/same-region")
fun requestCompareSameRegion(
@Query("usage") usage: Int = 80000
): Single<EnvRes>
@GET("datas/compare/industry-all")
fun requestCompareIndustryAllEnv(): Single<EnvRes>
@GET("datas/compare/industry-sameall")
fun requestCompareIndustrySameAll(@Query("usage") usage: Int): Single<EnvRes>
@GET("datas/detail/industry-energy")
fun requestDetailIndustryEnergy(
@Query("gas") gas: Int,
@Query("other") other: Int,
@Query("oil") oil: Int,
@Query("coal") coal: Int,
@Query("thermal") thermal: Int,
@Query("electric") electric: Int
): Single<IndustryEnergyRes>
companion object {
private const val BASE_URL =
"http://6e3ac85e023a.ngrok.io"
fun create(): MainApiInterface {
val logger = HttpLoggingInterceptor().apply {
level =
HttpLoggingInterceptor.Level.BASIC
}
val interceptor = Interceptor { chain ->
with(chain) {
val newRequest = request().newBuilder()
.addHeader("Authorization", "Token " + UserInfo.headerKey)
.build()
proceed(newRequest)
}
}
Log.d("AAAAA", "Token " + UserInfo.headerKey)
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.addInterceptor(interceptor)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(MainApiInterface::class.java)
}
}
}
이전 프로젝트에서는 외부 API까지만 다루고 LocalDataSource부분은 다루지 않았는데요.
다른 프로젝트에서 이부분을 다뤄 Local 부분을 추가로 작성합니다.
https://github.com/mtjin/mtjin-deeplink-tester-android
[LocalDataSourceModule]
Room 데이터베이스 생성을 의존성 주입하는 것과 LocalDataSource 클래스에 의존성주입을 하는 코드입니다.
package com.mtjin.mtjindlt.di
import android.content.Context
import androidx.room.Room
import com.mtjin.mtjindlt.data.database.MtjinRoomDatabase
import com.mtjin.mtjindlt.data.main.source.local.MainDao
import com.mtjin.mtjindlt.data.main.source.local.MainLocalDataSource
import com.mtjin.mtjindlt.data.main.source.local.MainLocalDataSourceImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
class LocalDataSourceModule {
// Room database
@Singleton
@Provides
fun provideAppDatabase(@ApplicationContext context: Context): MtjinRoomDatabase {
return Room.databaseBuilder(
context,
MtjinRoomDatabase::class.java, "MtjinDLT.db"
).build()
}
// Local
@Singleton
@Provides
fun provideMainLocalDataSource(mainDao: MainDao): MainLocalDataSource {
return MainLocalDataSourceImpl(mainDao)
}
// Dao
@Provides
@Singleton
fun provideUserDao(roomDatabase: MtjinRoomDatabase): MainDao =
roomDatabase.mainDao()
}
[RepositoryModule]
Repository 클래스에 LocalDataSource 를 주입하는 모듈입니다.
package com.mtjin.mtjindlt.di
import com.mtjin.mtjindlt.data.main.source.MainRepository
import com.mtjin.mtjindlt.data.main.source.MainRepositoryImpl
import com.mtjin.mtjindlt.data.main.source.local.MainLocalDataSource
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
class RepositoryModule {
@Singleton
@Provides
fun provideMainRepository(mainLocalRepository: MainLocalDataSource): MainRepository {
return MainRepositoryImpl(mainLocalRepository)
}
}
이상 Hilt를 사용해본 코드를 기록한 포스팅이었습니다.
댓글과 공감은 큰 힘이 됩니다. 감사합니다. !!
'안드로이드 > 코틀린 & 아키텍처 & Recent' 카테고리의 다른 글
[안드로이드] 기기 하나로 다양한 해상도 UI 테스트 하기 (Feat. Android Debug Bridge, ADB, MAC, zsh: command not found: adb) (2) | 2021.06.24 |
---|---|
[안드로이드] build.gradle 베이스 기록 (4) | 2021.06.03 |
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (9-마지막) Unit Test (언젠가 작성예정... 할게많) (0) | 2021.05.10 |
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (8) Service(WorkManager) (0) | 2021.05.10 |
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (7) MVVM - Model (5) | 2021.05.09 |