이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.
작성자 : 정서영
개발환경은 Windows, Android Studio입니다.
1. 쓰레드 타이머
쓰레드를 이용해서 타이머를 만들어보자~
우선, build. gradle에서 바인딩을 해주고, 메인 레이아웃을 아래처럼 만든다.
흐른 시간을 보여줄 textTimer, 시작과 종료 버튼( id: btnStart, btnStop )을 각각 만들어준다.
이제 MainActivity에서 코드로 와서~
1. 우선, 바인딩 만들어서 setContentView에 binging.root로 넣어준다.
2. 타이머에 사용할 전역변수들을 만들어준다.
- total = 총 지난 시간
- started = 시작 여부
3. android.os.Handler를 상속 받는 handler객체를 만들고, handleMessage (override 함수) 에서 total 변수를 이용해 포맷팅한 시간값을 textTimer에 넣어준다.
4. start 버튼 리스너에서 우선 started를 true로 set하고, 서브쓰레드를 만들어서 1초씩 sleep하면서 total을 +1하고 핸들러에 empty 메세지를 보내서 handleMessage가 실행되도록 한다.
5. stop 버튼 리스너에서는 started를 false로 바꿔주고 값도 0으로 초기화해준다.
(코드)
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import com.example.myapplication.databinding.ActivityMainBinding
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
val binding by lazy { ActivityMainBinding.inflate(layoutInflater)}
//전역변수
var total = 0
var started = false
//화면에 시간값을 출력하는 핸들러
val handler = object:Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
val minute = String.format("%02d",total/60)
val second = String.format("%02d",total%60)
binding.textTimer.text = "$minute:$second"
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
with(binding) {
btnStart.setOnClickListener {
started = true
thread(start = true) {
//---------------서브쓰레드로 초가 동작--------------------
while (started) {
//1초씩 쉬면서
Thread.sleep(1000)
if (started) {
//sleep하는 동안 false가 될 수 있어서 다시 한번 확인
total = total + 1
//핸들러로 메인쓰레드에서 값 출력
handler?.sendEmptyMessage(0)
}
}
//------------------------------------------------------
}
}
btnStop.setOnClickListener {
if(started){
started = false
total = 0
textTimer.text = "00:00"
}
}
}
}
}
2. 코루틴 이미지 다운로드
안드로이드에 새롭게 도입된 '백그라운드 처리구조' 인 코루틴을 사용해서
이미지 주소를 입력하면 이미지를 온라인에서 가져와서 보여주는 코드를 작성해보자~!
1. build.gradle에서 바인딩 설정, dependency 추가
android {
//...
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
//...
}
2. permission 설정에서 인터넷 권한 설정
( AndroidManifest. xml )
<user-permission android:name="android.permission.INTERNET"/>
3. 레이아웃 설정
plainText (id: editUrl), button (id: btnDownload), imageView (id: imagePreview), progressBar (id: progress)를 삽입해서 레이아웃을 만들어준다.
이때, progressBar는 code로 들어가서 아래처럼 수정하고, visibility를 invisible로 설정한다.
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
4. MainActivity에서 코루틴 코드 작성
코틀린 코루틴 (coroutine) : 쓰레드에 비해 비용이 적은 멀티태스킹 방식
- suspend 함수 : 코루틴에서 사용할 함수, 클래스 밖의 top-level로 작성한다.
- Dispatchers : 코루틴 실행에 사용하는 쓰레드를 결정, 모든 코루틴 빌더 ( launch, async )는 디스패처를 지정할 수 있다. 대표적인 디스패처 ) Default( 오래 걸리는 작업 ), IO( 파일io, api콜 ), Main( 메인스레드 작업 )
- withContext : 쓰레드 간 점프에 사용, 사용하는 쓰레드를 변경
다운로드 버튼을 누르면 코루틴이라는 영역이 새로 생성되고 ( by CoroutineScope( ). launch ),
코루틴 영역의 Main스코프에서는 화면을 다룰 수 있으므로 progress bar를 활성화 하고, url을 가져온다.
하지만, Main스코프에서는 통신이나 파일입출력을 할 수 없으므로 withContext로 IO스코프로 변경하여 loadImage함수로 이미지를 로드해온다. 반환된 비트맵 이미지는 바로 사용 가능하다.
이미지를 인터넷에서 가져오는 함수는 suspend키워드를 붙여서 loadImage라는 함수로 구현
( url주소를 stream으로 열고 비트맵으로 디코드해서 반환 )
package com.example.myapplication
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.View
import com.example.myapplication.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URL
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
//바인딩 생성
val binding by lazy { ActivityMainBinding.inflate(layoutInflater)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
with(binding) {
btnDownload.setOnClickListener {
//어떤 쓰레드에서 동작하는지 명시해서 코루틴 스코프 생성
//밖에 있는 메인쓰레드와는 다른 코루틴만의 새로운 영역
//-------------------코루틴 영역----------------------
//-----------------main 스코프------------------------
CoroutineScope(Dispatchers.Main).launch {
//progress bar 활성화
progress.visibility = View.VISIBLE
//url 받아오기
val url = editUrl.text.toString()
//--------------------IO 스코프-----------------
val bitmap = withContext(Dispatchers.IO) {
//suspend함수 사용
//이미지 비트맵으로 반환받아서 저장
loadImage(url)
}
//--------------------IO 끝----------------------
//받아온 이미지를 레이아웃에 set
imagePreview.setImageBitmap(bitmap)
//progress bar 다시 비활성화
progress.visibility = View.INVISIBLE
}
//-----------------------main 끝-------------------------
//------------------------코루틴 영역 끝------------------
}
}
}
//클래스 안에서는 메서드 (또는 함수)
}
//클래스 밖에서 만들면 무조건 함수!
suspend fun loadImage(imageUrl:String) : Bitmap {
val url = URL(imageUrl) //url로 변환
val stream = url.openStream() //주소를 stream으로 open
//stream으로 받은 이미지데이터를 비트맵으로 변환해서 return
return BitmapFactory.decodeStream(stream)
}
'GDSC HUFS 3기 > Android with Kotlin Team 6' 카테고리의 다른 글
[6팀] 코틀린 안드로이드 기초강의 51 Android 구글 맵 사용하기 (0) | 2021.12.26 |
---|---|
[6팀] 코틀린 안드로이드 기초 강의 49 Android 뮤직 플레이어 만들기 (0) | 2021.12.07 |
[6팀] 코틀린 안드로이드 기초강의 41 Android와 SQLite 데이터 베이스 (0) | 2021.11.30 |
[6팀] 코틀린 안드로이드 기초강의 35-36 탭메뉴 뷰페이저와프래그먼트, 리사이클러뷰 (2) | 2021.11.26 |
[6팀] 코틀린 안드로이드 기초강의 41 Room (0) | 2021.11.26 |