목록전체 글 (140)
시배's Android
TdsTimeLineBar는 TiTI 프로젝트에서 사용되는 커스텀한 타임라인 막대 그래프입니다. 이 그래프는 사용자가 하루 동안의 공부 시간을 쉽게 시각화할 수 있도록 도와줍니다. 각 시간대에 할당된 공부 시간을 백분율로 나타내며, 막대 그래프의 배경으로 Gradient 컬러가 들어가고 있습니다. @Composable fun TdsTimeLineChart( modifier: Modifier = Modifier, times: List, startColor: Color, endColor: Color, ) { require(times.size == 24) { "The times list must be 24 in size" } BoxWithConstraints(modifier = modifier) { val i..
TdsPieChart는 TiTI 프로젝트에서 사용되는 커스텀한 Pie Chart입니다. 각각의 task에 대한 누적시간을 퍼센트로 나타내며, 각 task 사이에는 1도의 여백으로 검은색을 화면에 표시합니다. 또한 애니메이션 효과를 지원하여 사용자에게 부드러운 시각적 효과를 제공합니다. @Composable fun TdsPieChart( modifier: Modifier = Modifier, taskData: List, colors: List, containsDonut: Boolean = false, animationSpec: AnimationSpec = TweenSpec(durationMillis = 500), ) { val transitionProgress = remember(taskData) { An..
Open Keyword Kotlin에서 클래스나 메서드를 상속 가능하게 하려면 open 키워드를 사용해야 합니다. 이는 Liskov Substitution Principle(LSP)를 준수하기 위한 것입니다. LSP는 서브타입(subtype)이 슈퍼타입(super type)을 대체할 수 있어야 한다는 원칙으로, 이를 위해서는 서브타입에서는 슈퍼타입의 모든 규칙을 따라야 합니다. 예를 들어, Animal 클래스를 살펴봅시다. open class Animal { open fun bark(){ println("animal") } fun test(){ println("test") } } 여기서 open 키워드는 이 클래스를 상속 가능하게 만듭니다. bark() 메서드 또한 open 키워드를 통해 서브클래스에서 ..
CoroutineScope 팩토리 함수 CoroutineScope는 coroutineContext를 유일한 프로퍼티로 가지고 있는 인터페이스입니다. CoroutineScope를 구현한 클래스에서 cancel이나 ensureActive 같은 다른 CoroutineScope의 메서드를 직접 호출하면 문제가 발생할 수 있습니다. 코루틴 스코프 인스턴스를 프로퍼티로 가지고 있다가 코루틴 빌더를 호출할 때 사용하는 방법이 선호됩니다. 코루틴 스코프 객체를 만드는 가장 쉬운 방법은 CoroutineScope 팩토리 함수를 사용하는 것입니다. 이 함수는 컨텍스트를 넘겨 받아 스코프를 만듭니다. 안드로이드에서 스코프 만들기 BaseViewModel에서 스코프를 만들면, 모든 뷰 모델에서 쓰일 스코프를 단 한번으로 정의..
기본 디스패처 디스패처를 설정하지 않으면 기본적으로 설정되는 디스패처는 CPU 집약적인 연산을 수행하도록 설계된 Dispatchers.Default 입니다. 실행되는 컴퓨터의 CPU 개수와 동일한 수의 스레드 풀을 가지고 있습니다. Dispatchers.Default의 limitedParallelism을 사용하면 디스패처가 같은 스레드 풀을 사용하지만 같은 시간에 특정 수 이상의 스레드를 사용하지 못하도록 제한할 수 있습니다. 메인 디스패처 안드로이드에서 메인 스레드는 UI와 상호작용하는 데 사용하는 유일한 스레드입니다. 메인 스레드가 블로킹되면 전체 애플리케이션이 멈춰 버립니다. 메인 스레드에서 코루틴을 실행하려면 Dispatchers.Main을 사용하면 됩니다. IO 디스패처 Dispatchers.I..
가변성을 제한하라 상태를 적잘하게 관리해야한다. 프로그램을 이해하고 디버그하기 힘들어집니다. 가변성이 있으면, 코드의 실행을 추론하기 어려워집니다. 멀티스레드 프로그램일 때는 적절한 동기화가 필요합니다. 테스트하기 어렵습니다. 상태변경이 일어날 때, 이러한 변경을 다른 부분에 알려야 하는 경우가 있습니다. val lock = Any() var num = 0 for ( i 1..1000) { thread { Thread.sleep(10) synchronized(lock) { num += 1 } } } Thread.sleep(1000) print(num)가변성은 생각보다 단점이 많아서 이를 완전하게 제한하는 프로그래밍 언어도 있습니다. 바로 순수 함수형 언어입니다. 코틀린에서 가변성 제한하기 읽기 전용 프로..
//이렇게 구현하면 안 됩니다!! suspend fun getUserProfile() : UserProfileData { val user = GlobalScope.async { getUserData() } val notifications = GlobalScope.async { getNotifications() } return UserProfileData( user = user.await(), notifications = notifications.await() ) } GlobalScope는 그저 EmptyCoroutineContext를 가진 스코프일 뿐입니다. 이때 aync 코루틴은 취소가 될 수 없습니다. 부모로부터 스코프를 상속받지 않습니다. 메모리 누수가 발생할 수 있으며 쓸데없이 CPU를 낭비합니다..
코루틴 빌더는 부모도 종료시키며, 취소된 부모는 자식들 모두를 취소시킨다는 점입니다. 코루틴이 종료되기 전에 예외를 잡는 건 도움이 되지만, 조금이라도 늦으면 이미 손쓸 수 없는 상황이 되어 버립니다. 코루틴 간의 상호작용은 잡을 통해서 일어나기 때문에, 코루틴 빌더 내부에서 새로운 코루틴 빌더를 try-catch 문을 통해 래핑하는 건 전혀 도움이 되지 못합니다. SupervisorJob SupervisorJob을 사용하면 자식에서 발생한 모든 예외를 무시할 수 있습니다. fun main() = Unit = runBlocking { //이렇게 하지 마세요. 자식 코루틴 하나가 있고 //부모 코루틴이 없는 잡은 일반 잡과 동일하게 작동합니다. launch(SupervisorJob()) { //1 laun..