GDSC HUFS 3기/Android with Kotlin Team 2

[2팀]쓰레드 타이머, 코루틴 이미지 다운로드

NakyungIm 2021. 11. 29. 06:16

이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 임나경

개발환경은 Windows, Android Studio입니다.

 

 

 

1. 쓰레드 타이머

1) 화면 준비

xml 화면
해당 화면의 component tree

 

 

** "Hardcoded Text" warning 해결 방법

내용 : hardcoded string should use @string resource

해결 : https://withthisclue.tistory.com/entry/Android-Studio-%EA%B2%BD%EA%B3%A0Warning-Hardcoded-text-%ED%95%B4%EA%B2%B0

 

[Android Studio] 경고(Warning): Hardcoded text - 해결

xml 파일을 열어 Design탭 Palette에서 Component Tree로 textView추가하니 생겼다. 노란색 경고 이미지를 클릭해보면 경고 메시지를 확인하고 해결할 수 있다. 1) 경고 이미지 Component Tree 경고 이미지 2) 메..

withthisclue.tistory.com

 

 

 

2) View Binding

<build.gradle> (:app)

buildFeatures {
	viewBinding true
}

 

<MainActivity.kt>

package com.example.timer

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.timer.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
    }
}

 

 

3) MainActivity.kt 수정

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

	// total, started 변수 선언
    var total = 0
    var started = false

	// Handler import (Alt + Enter 이용)
    val handler = object:Handler(Looper.getMainLooper()) {

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
    }
}

Handler import 시 java.util.logging 말고 android.os를 선택한다.

handler 내부에서 ctrl + O 를 누른 뒤 handleMessage를 선택한다.

 

 

 

package com.example.timer

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import com.example.timer.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) {  // 스레드를 통해 total에 쓴 값을, msg를 통해 화면에 표시
            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) { // sub thread
                    while (started) {
                        Thread.sleep(1000)
                        if (started) {
                            total += 1
                            handler?.sendEmptyMessage(0)
                        }
                    }
                }
            }

            btnStop.setOnClickListener {
                if(started){
                    started = false
                    total = 0
                    textTimer.text = "00:00"
                }
            }
        }
    }
}
  • Thread.sleep() : 괄호 안의 수만큼 멈춤.
  • thread(start = true) : Thread.sleep()을 sub thread에서 동작하게 하기 위해 thread(start = true)를 사용하여 함수 내부의 코드가 모두 sub thread에서 작동하게 함

 

runOnUiThread {
  val minute = String.format("%02d", total/60)
  val second = String.format("%02d", total%60)

  binding.textTimer.text = "$minute:$second"
}
  • runOnUiThread : 이 내부 코드는 메인 스레드에서 작동하게됨. 위의 코드에 handler부분을 아래 코드처럼 runOnUiThread를 이용해서 작성해도 됨.

 

4) 결과 화면

초기화면 또는 종료 후 화면
시작 버튼 누른 뒤

 

 

 

 

2. 코루틴 이미지 다운로드

1) 코루틴이란?

https://developer.android.com/kotlin/coroutines?hl=ko 

 

Android의 Kotlin 코루틴  |  Android 개발자  |  Android Developers

Android의 Kotlin 코루틴 코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다. 코루틴은 Kotlin 버전 1.3에 추가되었으며 다른 언어에서 확

developer.android.com

  • 백그라운드 처리 구조
  • 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴
  • 코루틴을 사용하여 이미지 주소를 입력하면 온라인에서 갖다가 화면에 보여주는 실습~

 

 

2) View Binding

위에 작성한 것과 동일한 방식으로 진행한다.

 

코루틴은 안스 or 코틀린 버전에 따라 dependency에 의존성 추가할지 확인해봐야함.

 

<build.gradle (:app)>

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
}

 

 

3) XML 수정

이미지와 프로그래스 바를 겹치게 하려면 xml을 코드를 수정해야한다.

 

<activity_main.xml>

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle" // 이 부분 수정
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent" // 이 부분 수정
        app:layout_constraintEnd_toEndOf="parent" // 이 부분 수정
        app:layout_constraintStart_toStartOf="parent" // 이 부분 수정
        app:layout_constraintTop_toTopOf="parent" /> // 이 부분 수정

 

xml design

 

visibility를 invisible 또는 gone으로 설정해서 처음에는 보이지 않도록 한다.

 

 

4) 함수 VS 메소드

클래스 에 만드는 것은 메소드, 클래스 에 만드는 것은 함수이다.

 

 

5) MainActivity.kt 수정

  • URL import 시 in android.provider... 말고 java.net으로 하기
  • 메인 스레드는 화면을 다루는 코드들을 작성하기
package com.example.timer

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.provider.ContactsContract.CommonDataKinds.Website.URL
import android.view.View
import com.example.timer.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 {
                CoroutineScope(Dispatchers.Main).launch { // 코루틴의 메인 영역
                    progress.visibility = View.VISIBLE
                    val url = editUrl.text.toString()

                    val bitmap = withContext(Dispatchers.IO) {
                        loadImage(url) // bitmap을 꺼내서 저장. 백그라운드 실행
                    }

                    imagePreview.setImageBitmap(bitmap)
                    progress.visibility = View.INVISIBLE
                }
            }
        }
    }
}

suspend fun loadImage(imageUrl:String) : Bitmap {
    val url = URL(imageUrl)
    val stream = url.openStream()

    return BitmapFactory.decodeStream(stream)
}

** hintTextColor not found 에러

내용 : AAPT: error: attribute android:hintTextColor not found.
해결책 : xml 파일에서 hintTextColor를 textColorHint로 수정한다.

 

 

 

6) 인터넷 권한 설정

<manifests/AndroidManifest.xml>

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.timer">

    <uses-permission android:name="android.permission.INTERNET"/> // 이 부분 추가

 

 

 

7) 결과 화면

초기 화면

 

이미지 URL 붙여넣기
이미지 불러온 결과 화면