Migrate to AndroidX

|

androidx로 마이그레이션하기

2018년 Google IO에서 기존의 android.support.* 라이브러리들을 AndroidX로 교체하기로 발표했습니다.

기존에 만들어진 프로그램들은 Android Studio에서 Migrate to AndroidX 기능을 이용해서 마이그레이션할 수 있기는 한데 완벽하지 않기 때문에 수동으로 변경해줘야 하는 부분들이 존재합니다.

만약 Migrate to AndroidX 기능으로 마이그레이션이 정상적으로 되면, 프로젝트 내에 있는 gradle.properties 파일에 다음과 같은 라인이 추가됩니다.

android.useAndroidX=true
android.enableJetifier=true


build.gralde (모듈)

또한, 각 모듈에 있는 build.gradle 은 다음과 같은 변화가 생깁니다.

androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'

들이

androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'

implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

컴포넌트 변경

그 외에도 많은 부분들이 변경됩니다.

변경 전 변경 후
android.support.v4.widget.DrawerLayout androidx.drawerlayout.widget.DrawerLayout
android.support.design.widget.NavigationView com.google.android.material.navigation.NavigationView
android.support.design.widget.CoordinatorLayout androidx.coordinatorlayout.widget.CoordinatorLayout
android.support.design.widget.AppBarLayout com.google.android.material.appbar.AppBarLayout
android.support.v7.widget.Toolbar androidx.appcompat.widget.Toolbar
android.support.v7.widget.CardView androidx.cardview.widget.CardView

등과 같이 support 라이브러리의 대부분 컴포넌트가 androidX로 바뀌며, 위의 예시 외에도 AppCompatActivityFragment, AlertDialog 등의 컴포넌트들도 전부 변경됩니다.

터미널에서 프로그램 실행 명령어 할당하기

|

프로그램 실행 명령어 할당

예를 들어 비주얼 스튜디오 코드를 사이트에서 dmg 파일을 다운받아 설치할 경우 터미널에 vscodecode 같은 프로그램 실행 명령어가 동작하지 않습니다. (리눅스에서 snap 등으로 VSCode를 설치했을 경우, 과거에는 단축 명령어가 vscode, 현재는 code 입니다.)


alias

이 경우 alias 명령어를 통해 특정 프로그램 실행 명령어를 할당할 수 있습니다.

alias code='open -a "Visual Studio Code"'


함수 생성

터미널용 함수를 만들어서 사용할 수도 있습니다.

code () {
    if [[ $# = 0 ]]
    then
        open -a "Visual Studio Code"
    else
        echo "Opening: "$@
        "/Applications/Visual Studio Code.app/Contents/MacOS/Electron" $@
    fi
}


아래와 같은 방법으로 심볼릭 링크(Synmolic link)를 이용해서 실행 명령어를 생성할 수 있습니다.

ln -s /Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code ~/bin/code


VSCode 내에서 할당

VSCode 같은 경우는 Command + Shift + P를 누르고 Shell을 타이핑하고 Shell Command: Install 'code' Command in PATH 항목을 선택하면 됩니다.

이 경우는 /usr/local/bin/code에 실행파일이 생성되며, which code로 확인할 수 있습니다.


그 외 자주 쓰는 것들

alias clion='open -a "/Applications/CLion.app"'

Total Finder 세팅

|

Total Finder

맥OS에 기본적으로 탑재되어 있는 Finder는 지원하는 기능이 너무 부족합니다. 멀티 윈도우 기능부터 잘라내기, 붙여넣기 등의 기능들이 불편하거나 지원하지 않아서 많이 불편을 느껴서 유료 앱은 Total FinderForkLift 3를 구매했습니다.

두 프로그램은 장단점이 있는데, 일단 여기서는 Total Finder에 대해서 포스팅합니다. Total Finder는 완전히 새로운 프로그램이 아니라 기존의 Finder의 애드온(Add-On) 형태로 설치하는 프로그램입니다.

다운로드 및 구매는 여기에서 할 수 있습니다.


설정

Total Finder 설치 후 Preferences에서 다음과 같이 설정을 해줍니다.

먼저 Finder 기본 설정입니다.

  • GeneralNew Finder windows show: Home으로 설정해줍니다.
  • Sidebar에서도 필요한 항목들을 체크해줍니다. Home 같은 경우는 체크를 해 주는 것이 편합니다.
  • Advanced 탭에서는 Show all filename extensions 옵션을 체크합니다.

그리고 TotalFinder 설정도 아래와 같이 해줍니다.

  • Dock에서는 Show progress bar in the Dock 체크
  • Menues에서는 Allow path copying from Context MenusShow CutPaste buttons in Context Menus 체크
  • File Browser에서는 Automatically adjust widths in Column ViewColored Labels 체크
  • Tabs에서는 Open new tabs with previous location 체크

정도 해 주면 되고, Visor 는 취향에 따라 활성화하면 될 것 같습니다.

코루틴(Coroutine) vs 쓰레드(Thread)

|

Coroutine is ‘Light-weight Thread’

흔히 코루틴은 Light-weight Thread라고 합니다. 이 말이 정확히 어떤 것을 의미하는지 좀 더 자세히 알아보도록 하겠습니다.


동시성(Concurrency)과 병렬성(Parallelism)

  • 동시성(Concurrency): 다수의 Task를 수행하기 위해서 각 Task를 조금씩 나누어서 실행하는 시분할 방식
  • 병렬성(Parallelism): 다수의 Task를 동시에 실행하는 것

동시성은 각 Task를 조금씩 나누어서 실행하는 것이기 때문에 총 실행 시간은 각 Task의 실행 시간을 합친 것과 같습니다. 예를 들어 10분짜리 Task 5개를 실행한다면, 총 수행 시간은 5 x 10 = 50분이 됩니다. 여기에 각 Task간 작업 전환을 위한 Context Swithcing이 추가로 발생합니다.

병렬성은 Task간 전환이 없기 때문에 Context Switching이 발생하지 않습니다. 대신 자원이 Task 수 만큼 필요합니다. 총 수행 시간은 가장 시간이 긴 Task 만큼 소요됩니다.


Coroutine & Thread for Concurrency

Coroutine과 Thread는 둘 다 동시성을 보장하기 위한 기술입니다. Thread는 OS 레벨에서 각 작업의 동시성을 위해 Preemtive Scheduling을 해서 각 작업을 조금씩 나누어서 실행합니다. Coroutine도 동시성을 목표로 하고 있지만, 각 작업에 Thread를 할당하는 것이 아니라 작은 Object 만을 할당한 다음 이 Object를 스위칭하면서 Context Switching 비용을 최대한 줄였습니다. 그래서 Light-weight Thread라고 부릅니다.


Thread

  • Thread는 각 Task 마다 Thread를 할당합니다.
  • 각 Thread는 자체적인 Stack 메모리를 가지며 JVM Stack 영역을 가집니다.
  • OS 커널에서 Context Switching을 해서 동시성을 보장합니다.
  • 만약 복수의 Thread를 사용해서 Thread 1Thread 2의 결과를 기다려야 한다면, Thread 1은 그 때까지 Blocking 되어 해당 자원을 사용할 수 없습니다.


Coroutine

  • Task 마다 각각 Object를 할당합니다.
  • 각 Coroutine Object는 JVM Heap에 적재됩니다.
  • 커널 레벨의 Context Switching이 아니라 프로그래머가 컨트롤하는 Switching을 통해 동시성을 보장합니다.
  • Task 1 작업을 수행하다가 suspend 되더라도, 해당 Thread 는 유효하기 때문에 Task 2를 같은 Thread에서 실행할 수 있습니다.
  • 하나의 Thread에서 다수의 Coroutine Object를 실행할 수 있으며, 이 경우 Coroutine Object 교체만 발생하기 때문에 커널 레벨의 Context Switching이 발생하지 않습니다.

만약 여러 Thread에서 다수의 Coroutine을 실행할 경우에는 Thread 전환이 일어날 경우 Context Switching이 발생합니다. Coroutine의 No Context Switching 장점을 살리기 위해서는 하나의 Thread에서 복수의 Coroutine Object를 실행하는 것이 유리합니다.

Coroutine은 기존의 Thread를 좀 더 작은 단위로 쪼개어 사용할 수 있는 개념입니다.

하나의 Thread에서 복수의 Coroutine이 실행될 경우 각 Thread가 가지는 Stack 메모리 영역도 하나가 되어 메모리 절약이 되며, 공유 메모리 접근으로 발생할 수 있는 Deadlock 문제도 해결될 수 있습니다.


Stackful & Stackless

Coroutine은 크게 Stackful 방식과 Stackless 방식으로 나눌 수 있습니다. Kotlin의 경우는 Stackless 방식이기 때문에 약간의 기능 제한이 있습니다.

Thread의 경우 자체 Stack 메모리 영역을 가지기 때문에 Stack을 이용해서 함수를 실행하고 관리할 수가 있습니다.

  • Stackful Coroutine : 코루틴 내부에서 다른 함수를 호출할 수 있고 값을 리턴하거나 suspend 할 수 있습니다.
  • Stackless Coroutine : caller에게 항상 무엇인가를 리턴해야 하며, 값을 리턴하거나 no result yet, I'm supended를 리턴합니다.

다시 요약하면, Stackless Coroutine는 항상 값이나 결과를 리턴해야 하기 때문에 코루틴을 호출한 caller가 그 값을 이용해서 판단 및 제어를 해야 하고, Stackful Coroutine는 일반적인 Thread처럼 스스로 suspend도 할 수 있으며 값을 리턴할 수도 있습니다.

각 언어별 지원하는 코루틴 정보는 다음과 같습니다.

  • Stackful Coroutine: Javaflow, Quasar
  • Stackless Coroutine: Kotlin, Scala, C#


suspend

하나의 Thread는 여러 개의 Coroutine을 실행할 수 있습니다. 이 때 Context Switching이 발생하지 않기 때문에 Light-weight Thread라고 부릅니다. 하나의 Thread에서 여러 개의 Coroutine가 실행될 때, 실행 중이던 하나의 코루틴이 suspend(멈춤) 상태가 되면, 해당 Thread에서는 해당 Thread 내의 resume할 다른 코루틴을 찾습니다.

따라서 코루틴 내에서 호출하는 멈출 수 있는 함수는 suspend 키워드를 이용해서 선언할 수 있습니다.

suspend fun getDataFromServer() : Data {
    // TODO
}

위와 같은 함수는 suspend function이 호출되는 순간 해당 코루틴을 잠시 중단시켜놓을 수 있으며 결과 값이 오면 해당 함수를 다시 resume 시킵니다.

코루틴(Coroutine) Dispatchers.Main 사용하기

|

Dispatchers.Main

Dispatchers.Main는 해당 CoroutineScope을 Main UI Thread에서 동작시키도록 합니다.

안드로이드에서 Dispatchers.Main을 그냥 사용하려고 하면 다음과 같은 에러가 발생하는데,

java.lang.ILLegalStateException: Module with Main dispatcher is missing. Add dependency with required Main dispatcher, e.g. 'kotlinx-coroutines-android'

gradle에 다음 라인을 추가해주면 됩니다.

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1'

Dispatchers.Main을 이용해서 코루틴을 구동시키면 Main UI Thread에서 동작하기 때문에, Thread.currentThread().id 값을 확인해보면 같은 id 값을 가지는 것을 확인할 수 있습니다.

또한 시간이 오래 걸리는 작업 등을 동작시킬 경우 ANR(Application Not Responding)과 같은 오류가 발생할 수도 있으니 주의해야 합니다.