728x90
반응형

안녕하세요 건리버입니다.
오늘부터 안드로이드에 대해 아주 차근차근 들여다보려고 합니다.
안드로이드를 새롭게 배우고 싶으신 분들을 위해 설명도 최대한 자세히 넣겠습니다.
저도 같이 공부하면서 적어 내려가는 내용인 만큼 혹시 틀린 부분이 있더라도 너그러이 봐 주시고 댓글 남겨주시면 참고하여 수정하도록 하겠습니다.

안드로이드 개발환경 설정하는 방법은 구글링해보면 많이 나와있습니다.
특히 아래 링크한 "멈춤보단 천천히라도" 블로그를 참고하시면 좋을 것 같습니다.
다른데보다 친절하고 자세하게 적혀있더라구요.(Windows 기준으로 설명 되어있지만, Mac에서도 크게 다르지 않습니다)
https://webnautes.tistory.com/1126

안드로이드 스튜디오에서 빈 프로젝트를 생성해 봅시다.
모두 디폴트 세팅을 해주되 저는 Java 대신 Kotlin 언어를 선택 했습니다.
이 전 포스트에서도 잠깐 다뤄봤지만 안정성과 간결성을 높인 Kotlin은 요즘 시장에서 거의 필수라고 보는 것 같습니다.

위와 같은 프로젝트가 생성되었습니다.
아래 세 가지 파일들이 모두 생성되었는지 확인해보세요. 프로젝트를 이루는 가장 핵심이 되는 파일들입니다. 만약 'Kotlin' 대신 'Java'를 선택했다면 MainActivity의 확장자가 .kt 대신 .java라고 나올겁니다.

app/manifests/
--> AndroidManifest.xml

app/java/
--> MainActivity.kt

app/res/
--> activity_main.xml

이 중 Kotlin 코드가 담길 MainActivity.kt를 먼저 살펴보겠습니다.

package com.example.androidstudy

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

맨 윗 줄에 "package com.example.androidstudy"는 제 프로젝트 명이자 제가 만들 어플리케이션의 고유 ID가 됩니다. 따라서 실제로 구글스토어에 릴리즈할때는 example 부분을 주로 사이트 도메인 이름으로 사용합니다. 예를 들어 네이버 지도같은 경우 com.naver.maps 라는 패키지 이름을 쓰고 있죠.

다음 import는 외부의 패키지를 사용하겠다고 선언 하는 것인데요.

  • AppCompatActivity
  • Bundle

이 두개를 디폴트로 사용하고 있습니다.

Android developers page에서 AppCompatActivity를 찾아보면 이렇게 설명하고 있습니다.

Activity의 기본이 되는 클래스이며, 구형 Android 장치에서도 새로운 플랫폼의 기능을 하위호환될 수 있도록 만들어진 것 같습니다.

다른 말은 잘 모르겠지만 어쨌든 중요한 것은 Activity라는 것이고, 그렇다면 이번엔 Activity가 무엇인지 알아봐야할 것 같습니다.

역시 Android devlopers page 에서 찾아봅시다.

요약하자면 일반적인 프로그램이 main() 함수에서 앱을 시작하는 것과 달리, Android는 특정한 lifecycle에 따라 Activity 인스턴스 안에 있는 특정한 Callback함수를 호출하여 초기화한다는 것이죠. (Callback 함수에 관련된 내용은 나중에 Kotlin을 정리하면서 포스팅하도록 하겠습니다.)

안드로이드에서 하나의 Activity에 대한 lifecycle이라는 것은 이렇게 생겼습니다.

Activity가 시작되면 onCreate() 함수가 가장 먼저 호출 되는 것을 볼 수가 있죠.
그러고 보니 우리의 MainActivity 클래스 안에도 onCreate() 함수가 오버라이드 되어있네요. 이 함수가 우리 코드의 시작점이라는 것을 이해할 수 있습니다.

override fun onCreate(savedInstanceState: Bundle?)

또 하나 Activity에 대해 중요한 내용이 들어있는데요.

Activity를 사용하기 위해서는 반드시 manifest 파일에서 activity를 선언해주어야 한다고합니다.

그럼 제가 생성한 프로젝트의 manifest 파일을 한번 확인해볼까요?
app/manifests/
--> AndroidManifest.xml

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

프로젝트를 생성할 때 안드로이드 스튜디오가 자동으로
<activity android:name=".MainActivity">
를 manifest에 선언을 해준 것을 볼 수 있군요.

 

override fun onCreate(savedInstanceState: Bundle?) 

그럼 마지막으로 onCreate() 함수에 인자로 받고 있는 Bundle은 무엇일까요?
Bundle이란 Activity 간에 데이터를 주고 받기 위해 사용하는 개념입니다.

저의 빈 프로젝트에서 Bundle은 어떤 값을 갖고 있을지 궁금해서 디버깅 모드로 실행시킨후 변수를 관찰 해 보았습니다.

savedInstanceState: null 이라고 써있는게 보이시나요?
처음 시작한 이 빈 프로젝트는 Bundle에 null을 전달해서 onCreate() 함수를 호출 하였습니다. 전달 받은 Bundle이 빈 것으로 보아 받을 데이터가 아직 없나보군요. 아무 것도 없어서 조금 아쉽긴 하지만 어찌보면 새로운 프로젝트에 데이터가 비어있는 것은 어찌보면 당연한 것이겠죠?

여기까지 간단하게 MainActivity가 어떻게 생겼는지 관찰해보았습니다.
관찰만하니 조금 아쉽네요.
다음 포스팅부터는 실제 Code를 수정하여 원하는 동작이 잘 되는지 확인해보도록 하겠습니다.

728x90
반응형
728x90
반응형

본문은 안드로이드 공식사이트를 참조하여 작성하였습니다.
안드로이드_공식사이트

Android를 처음 배웠을 때는 JAVA만 알면 됬었던 것 같다.
다시 Android를 보려고 하니 Kotlin이라는 아이가 있더라.
JAVA랑 100% 호환되면서 간결성과 안정성을 높인 언어라나...
암튼 뭐 이런녀석을 만들었고 Android에서도 공식적으로 개발 언어로 채택했다는데 나도 배워봐야지 않겠나 싶어 기본 문법부터 보기로 했다.

변수선언

변수는 선언하는 키워드는 두가지가 있다.
val, var

val은 값이 변경되지 않는 변수(사실은 상수, c언어로 치면 const가 앞에 붙은 변수),
var은 값이 변하는 일반적인 변수이다.

Java

String myName = "Gunliver";

Kotlin

var myName : String = "Gunliver"

어? 간결해지기는 커녕 오히려 복잡해진 것 같은데...? (일단 좀더 지켜봅시다)
뒤에 세미콜론이 없어진건 좀 간결해졌다고 볼수 있으려나

Kotlin도 유형추론이 가능하다

var myName = "Gunliver"  
var myAge = 20

// Fails to compile  
myAge = "twenty"

위와 같이 타입을 생략 가능하지만 생략만 할뿐 실제로는 Compile시 유형을 정하는 것이기 때문에 유형을 변경할 수는 없다.

Null 안전

// Fails to compile
var myName : String = null

var myName : String? = null

Kotlin에서는 기본적으로 변수에 null값을 넣을 수 없다. null을 꼭 넣어야하는 변수라면 두번째와 같이 물음표? 를 넣어 nullable 변수로 선언해줘야만 한다. NullPointerException을 막기위한 방법으로 프로그래머가 귀찮게 ?를 넣어야만 null값을 허용해주는 방식으로 가볍게(?) 문제를 해결. 이렇게까지 하는데는 다 이유가 있겠지 생각하고 nullable 변수를 선언할때는 매우 신중하자.

조건부

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

이런 일반적인 조건은문 아래와 같이 작성할 수 있다.
조건마다 println을 하지 않고 맨 위에 선언한 변수에 할당할 값만 작성.

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

조건이 여러개일 때 아래와 같이 when을 사용할 수도 있다.

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

오~! 이제서야 Kotlin의 간결성이란게 뭔지 조금씩 보이는 것 같다.
많이 줄였지만 가독성면에서도 떨어지지 않는 것 같아 마음에 든다.

함수

함수를 선언할 때는 fun 키워드를 사용한다.
fun 함수이름 ( 인수이름 : 인수자료형 ) : 출력자료형

Kotlin

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

반환키워드를 할당 연산자로 바꿀 수도 있다.

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }

이거 새롭긴한데 뭔가 익숙치 않은 느낌이다.

익명 함수

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

여기서 'stringLengthFunc'가 함수의 이름 같지만 함수의 이름이 아니라 익명함수를 참조하는 변수의 이름이다. 함수포인터 또는 델리게이트 라고 보면 될 것 같다.

(String) -> Int 입력 String을 받아서 출력 Int 가 나오는 모양이고,
그 함수는 입력의 길이를 구하는 내용이다.
input -> input.length

이렇게 함수을 참조하는 변수는 고차함수(콜백함수)에 인자로 들어갈 수 있다.

고차 함수

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

'stringMapper'는 입력을 'String' 변수 하나와 '(String) -> Int' 형태의 함수를 인자로 받는다. 이런 함수는 아래와 같이 익명함수를 확용하여 호출 할 수도 있다.

stringMapper("Android", { input ->
    input.length
})

클래스

class키워드로 클래스를 만들 수 있다.
속성은 변수선언과 마찬가지로 변경 가능한 것은 'var', 변경 불가능한 것은 'val'로 선언한다.

class Car {
    val wheels = listOf<Wheel>()
}

getter 와 setter를 만들고 싶을 때는 속성아래에 'get()', 'set()'을 넣어주자.

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value)
    }
728x90
반응형

+ Recent posts