์๋ ํ์ธ์ ๐
์ค๋์ ์ ๋ฒ ํฌ์คํ ์ ์ด์ด์ Legacy ์ฝ๋ ์ ์ง๋ณด์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด๋ ค ํฉ๋๋ค ๐
์ค๋ ํฌ์คํ ํ ๋ด์ฉ์ ์๋ฐ ์ฝ๋๋ฅผ ์ฝํ๋ฆฐ์ผ๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ํฌ์คํ ํด๋ณด๋ ค ํฉ๋๋ค.

์ฝํ๋ฆฐ๊ณผ ์๋ฐ๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด์๋ ์ด์ ํฌ์คํ ์ ์ฐธ๊ณ ๋ถํ๋๋ฆฝ๋๋ค!
[Java][Kotlin][Android] ์๋๋ก์ด๋ ์๋ฐ์ ์ฝํ๋ฆฐ ํจ๊ป ์ฌ์ฉํ๊ธฐ - ์๋๋ก์ด๋ Legacy ์ฝ๋ ์ ์ง๋ณด์ ํ
์๋ ํ์ธ์ ๐ ๊ฑฐ์ ํ ๋ฌ ๋ง์ ํฌ์คํ ์ผ๋ก ์ฐพ์๋ต๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ฌ๋ฌ ํ๋ก์ ํธ๋ค์ ๊ฒฝํํ๋ค ๋ณด๋ฉด ์๋ฐ๋ก ๋์ด์๋ ์ฝ๋๋ฅผ ์ฝํ๋ฆฐ์ผ๋ก ๋ฐ๊พธ๊ฑฐ๋ ๋ ๊ฑฐ์ ์ฝ๋๋ค์ ์ ์ง ๋ณด์ํ๋ฉด์ ์ฝํ๋ฆฐ์
devyo-111commit.tistory.com
Java ํ์ผ Kotlin์ผ๋ก ๋ณํํ๊ธฐ
์๋์ ๊ฐ์ด ์ํ๋ ํ์ผ์ 'Convert Java File to Kotlin File'์ ํตํด ๋ณํํด ์ค๋๋ค.
์๋์ผ๋ก ๋ณํ์ด ์๋ฃ๋ ํ ์๋์ ๊ฐ์ ํ์ ์ด ๋ํ๋๋๋ฐ ๊ทธ๋๋ก ์งํ์์ผ ์ค๋๋ค. (ํ๋ง๋๋ก ๋ ์ข์ ์ฝ๋๊ฐ ์๋๋ฐ ๋ณํ ์์ผ ์ค๊น?)
์ด๋ฌ๋ฉด ๊ฐ๋จํ๊ฒ Java์ฝ๋๊ฐ Kotlin์ฝ๋๋ก ๋ณํ๋๋ ๊ฒ์ ๋ณด์ค ์ ์์ต๋๋ค ๐คฉ
์ด๋ ๊ฒ๋ง ๋๋๋ฉด ์ฌ๋ฏธ์์ผ๋ RxJava๋ก ๋์ด์๋ ๋ถ๋ถ์ Coroutine์ผ๋ก ๋ณํํ๋ ์์ ๊น์ง ํจ๊ป ์งํํด๋ณด๋ ค ํฉ๋๋ค.
๊ธฐ์กด
// RestApi.kt
interface RestApi {
@GET(Utils.GET_BEACH_CONGESTION_URL)
fun getBeachCongestion() : Single<BeachListDTO>
}
// BeachRepository.kt
interface BeachRepository {
/**
* ํด์์์ฅ ํผ์ก๋ ๊ฐ์ ธ์ค๊ธฐ
* **/
fun getBeachCongestion() : Single<BeachListDTO>
}
// BeachRepositoryImpl.kt
class BeachRepositoryImpl @Inject constructor(
private val restApi: RestApi
) : BeachRepository {
/**
* ํด์์์ฅ ํผ์ก๋ ๊ฐ์ ธ์ค๊ธฐ
* **/
override fun getBeachCongestion(): Single<BeachListDTO> {
return restApi.getBeachCongestion()
}
}
๊ธฐ์กด ์ฝ๋๋ Rx๋ฅผ ํ์ฉํ๊ธฐ ์ํด Single๊ฐ์ฒด๋ฅผ ๋ฐํํ๋๋ก ๊ตฌ์ฑ๋์ด ์์์ต๋๋ค.
ํด๋น ๋ถ๋ถ์ ํจ๊ป ๋ฐ๊ฟ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋ณ๊ฒฝ ํ
// RestApi.kt
interface RestApi {
@GET(Utils.GET_BEACH_CONGESTION_URL)
suspend fun getBeachCongestion() : Response<BeachListDTO>
}
// BeachRepository.kt
interface BeachRepository {
/**
* ํด์์์ฅ ํผ์ก๋ ๊ฐ์ ธ์ค๊ธฐ
* **/
suspend fun getBeachCongestion() : Flow<NetworkResult<BeachListDTO>>
}
// BeachRepositoryImpl.kt
class BeachRepositoryImpl @Inject constructor(
private val restApi: RestApi
) : BeachRepository {
/**
* ํด์์์ฅ ํผ์ก๋ ๊ฐ์ ธ์ค๊ธฐ
* **/
override suspend fun getBeachCongestion(): Flow<NetworkResult<BeachListDTO>> {
val response = restApi.getBeachCongestion()
return if(response.isSuccessful) {
flow{
emit(NetworkResult.Success(response.body()!!))
}
} else {
flow{
emit(NetworkResult.Failure("๋คํธ์ํฌ ํต์ ์ ์คํจํ์ต๋๋ค!"))
}
}
}
}
์์ฒ๋ผ Single ๊ฐ์ฒด๋ฅผ ๊ฑท์ด๋ด๊ณ Flow๋ฅผ ๋ฐํํ๋๋ก ์์ ํ์์ต๋๋ค.
์ด์ด์ ViewModel๊ณผ Activity์ฝ๋๋ฅผ ๋ฐ๊ฟ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ธฐ์กด
@HiltViewModel
public class MainViewModel extends ViewModel {
private BeachRepository mBeachRepository;
private CompositeDisposable mCompositeDisposable;
private MutableLiveData<List<BeachDTO>> mBeachList = new MutableLiveData<>();
public LiveData<List<BeachDTO>> getBeachList() {
return mBeachList;
}
private MutableLiveData<String> mToastMessage = new MutableLiveData<>();
public void setToastMessage(String message) {
mToastMessage.setValue(message);
}
public MutableLiveData<String> getToastMessage() {
return mToastMessage;
}
@Inject
MainViewModel(BeachRepository repository){
mBeachRepository = repository;
}
/**
* RxJava๋ฅผ ํตํด ํด์์์ฅ ํผ์ก๋๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฉ์๋
* **/
public void callAllBeachList() {
if(mCompositeDisposable == null){
mCompositeDisposable = new CompositeDisposable();
}
mCompositeDisposable.add(
mBeachRepository.getBeachCongestion()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<BeachListDTO>(){
@Override
public void onSuccess(@NonNull BeachListDTO beachListDTO) {
mBeachList.setValue(beachListDTO.getAllBeachList());
}
@Override
public void onError(@NonNull Throwable e) {
mToastMessage.setValue("ํต์ ์ ์คํจํ์ต๋๋ค!");
}
})
);
}
@Override
protected void onCleared() {
super.onCleared();
if(mCompositeDisposable != null){
mCompositeDisposable.clear(); // ViewModel์ด clear ๋ ๋ CompositeDisposable ์ญ์
mCompositeDisposable = null;
}
}
}
๋ณ๊ฒฝ ํ
sealed class NetworkResult<T> {
data class Loading<T>(val isLoading : Boolean) : NetworkResult<T>()
data class Success<T>(val data : T) : NetworkResult<T>()
data class Failure<T>(val errorMessage : String) : NetworkResult<T>()
}
class MainViewModel @Inject constructor(
private val beachRepository : BeachRepository
) : ViewModel() {
private val _beachList = MutableStateFlow<List<BeachDTO>>(emptyList())
val beachList: StateFlow<List<BeachDTO>>
get() = _beachList
/**
* Coroutine๊ณผ Flow๋ฅผ ๋คํธ์ํฌ ํต์ ์ ํ๋ ํจ์
*/
fun callAllBeachList() {
viewModelScope.launch {
beachRepository.getBeachCongestion().collect{ result ->
when(result){
is NetworkResult.Success -> {
_beachList.value = result.data.getAllBeachList()
}
is NetworkResult.Failure -> {
Log.d(TAG, "callAllBeachList: ${result.errorMessage}")
}
is NetworkResult.Loading -> {
Log.d(TAG, "callAllBeachList: Loading...")
}
}
}
}
}
}
Rx๋ฅผ ๊ฑท์ด๋ด๊ณ ํด๋น ๋ถ๋ถ์ Corouine์ ํตํด ๊ฐ์ ์์ ํ๋๋ก ์์ ํ์์ต๋๋ค.
์์ธ๋ฌ, LiveData๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ๋ StateFlow๋ฅผ ์ฌ์ฉํ๋๋ก ์์ ํ์์ต๋๋ค :)
๊ธฐ์กด
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ActivityMainBinding mBinding;
private MainViewModel mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this , R.layout.activity_main);
mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
mViewModel.callAllBeachList();
observeData();
}
private void observeData() {
mViewModel.getToastMessage().observe(this, new Observer<String>() { // Toast Message
@Override
public void onChanged(String message) {
Toast.makeText(MainActivity.this, message , Toast.LENGTH_SHORT).show();
}
});
mViewModel.getBeachList().observe(this, new Observer<List<BeachDTO>>() {
@Override
public void onChanged(List<BeachDTO> beachDTOs) {
for(BeachDTO beach : beachDTOs){
Log.d(TAG, "onChanged: " + beach.toString());
}
}
});
}
}
๋ณ๊ฒฝ ํ
private const val TAG = "MainActivity"
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private var binding: ActivityMainBinding? = null
private val viewModel : MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel.callAllBeachList()
observeData()
}
private fun observeData() {
with(viewModel){
lifecycleScope.launch{
repeatOnLifecycle(Lifecycle.State.STARTED){
launch {
beachList.collect{ list ->
if(list.isNotEmpty()){
Log.d(TAG, "observeData: $list")
}
}
}
}
}
}
}
}
๋ง์ง๋ง์ผ๋ก Activity ์ฝ๋์ ๋๋ค.
๊ฐ๋จํ ์์ ๋ฅผ ์ ์ฉํด ๋ณธ ๊ฒ์ด์ง๋ง ์ฝ๋๊ฐ ๋งค์ฐ ๊ฐ๊ฒฐํด์ง๋ ๊ฒ์ด ๋๊ปด์ก์ต๋๋ค.
์ฌ๋ฌ๋ถ๋ค์ ์๊ฐ์ ์ด๋ ์ ๊ฐ์? ๐ค
์ด์์ผ๋ก ํฌ์คํ ์ ๋ง์น๋ฉฐ
Legacy์ฝ๋๋ฅผ ์ ์ง ๋ณด์ํ๋ ์ฌ๋ฌ๋ถ๋ค์๊ฒ ๋ง์ ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค.
์ค๋๋ ์ฆ์ฝํ์ธ์ :)