๋ค์์ Futurice์ Android ๊ฐ๋ฐ์๋ค๋ก๋ถํฐ ํ์ตํ ๋ด์ฉ๋ค์ด๋ค. ์ด ๊ฐ์ด๋๋ฅผ ๋ฐ๋ผ๊ฐ๋ฉด์ ์ด๋ฏธ ํ๋ ์์ ์ ๋ํ์ด(Reinventing the wheel)ํ์ง ์๋๋ก ํ์. iOS๋ Windows Phone ๊ฐ๋ฐ์๋ ๊ด์ฌ์ด ์๋ค๋ฉด, iOS Good Practices ํน์ Windows App Development Best Practices ๋ฌธ์๋ค๋ ํ์ธํด๋ณด์.
HTTP ํด๋ผ์ด์ธํธ๋ฅผ ์ง์ ์์ฑํ์ง ๋ง๊ณ , Volley๋ OkHttp ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ฌ์ฉํ์.
65,000 ๋ฉ์๋ ์ ์ ํ์ ๋ฐฉ์งํ๊ธฐ ์ํด Guava๋ ํผํ๊ณ ๋ช ๊ฐ์ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค๋ง์ ์ฌ์ฉํ์.
ํ๋์ style์ด ๋ฐฉ๋ํด์ง๋ ๊ฒ์ ํผํ๊ธฐ ์ํด ์ฌ๋ฌ๊ฐ์ง style ํ์ผ๋ค์ ์ฌ์ฉํ์.
Android SDK๋ฅผ ํ ๋๋ ํ ๋ฆฌ๋ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ๋ฆฝ์ ์ธ ์์น์ ๋์. ๋ช๋ช IDE๋ค์ ์ค์น์์ SDK๋ฅผ ํด๋น IDE์ ๊ฐ์ ๊ฒฝ๋ก์ ํฌํจ์ํค๋๋ฐ, ์ด๋ IDE๋ฅผ ์ ๊ทธ๋ ์ด๋(ํน์ ์ฌ์ค์น)ํ๊ฑฐ๋ IDE๊ฐ ๋ณ๊ฒฝ๋ ๋ ๋ถํธํ๋ค. ๋ํ IDE๊ฐ root ์๋์ ์์ง ์๊ณ user ์๋์์ ๋์ํ ๊ฒฝ์ฐ, SDK๋ฅผ sudo ๊ถํ์ ์๊ตฌํ๋ ์์คํ ๋ ๋ฒจ์ ๋๋ ํ ๋ฆฌ์ ๋์ง ์๋๋ก ํ์.
๊ธฐ๋ณธ ์ต์ ์ Gradle์ด๋ค. Ant๋ ์๋นํ ์ ํ์ ์ด๊ณ ๋ด์ฉ์ด ์ฅํฉํ๋ค. Gradle์ ์ฌ์ฉํ๋ฉด, ๋ค์ ํญ๋ชฉ๋ค์ด ๊ฐ๋จํด์ง๋ค.
- ์ฑ์ ๊ฐ๊ธฐ ๋ค๋ฅธ Flavor๋ค๊ณผ Varient๋ค์ ๋น๋ํ ์ ์๋ค.
- Task๋ค์ ๊ฐ๋จํ ์คํฌ๋ฆฝํธ์ฒ๋ผ ๋ง๋ค ์ ์๋ค.
- ์ฌ๋ฌ Dependency๋ค์ ๊ด๋ฆฌํ๊ณ ๋ค์ด๋ก๋ํ ์ ์๋ค.
- Keystore๋ค์ ์ปค์คํฐ๋ง์ด์ฆํ ์ ์๋ค.
- ๊ธฐํ ๋ฑ๋ฑ
Android์ Gradle ํ๋ฌ๊ทธ์ธ์ ์๋ก์ด ํ์ค ๋น๋ ์์คํ ์ผ๋ก์ ๊ตฌ๊ธ์ ์ํด ํ๋ฐํ๊ฒ ๊ฐ๋ฐ๋๊ณ ์๋ค.
๋ ๊ฐ์ง ๋ง์ด ์ฐ์ด๋ ์ต์ ๋ค์ด ์๋ค: ๋ก์ Ant & Eclipse ADT ํ๋ก์ ํธ ๊ตฌ์กฐ, ์๋ก์ด Gradle & Android Studio ํ๋ก์ ํธ ๊ตฌ์กฐ๊ฐ ์๋๋ฐ, ์๋ก์ด ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ์ ํํ์. ๋ง์ฝ ๋ก์ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, ๋ ๊ฑฐ์๋ก ํ๋จํ๊ณ ์๋ก์ด ๊ตฌ์กฐ๋ก ํฌํ ํ๋ ์์ ์ ์์ํ์.
Old structure:
old-structure
โโ assets
โโ libs
โโ res
โโ src
โ โโ com/futurice/project
โโ AndroidManifest.xml
โโ build.gradle
โโ project.properties
โโ proguard-rules.pro
New structure:
new-structure
โโ library-foobar
โโ app
โ โโ libs
โ โโ src
โ โ โโ androidTest
โ โ โ โโ java
โ โ โ โโ com/futurice/project
โ โ โโ main
โ โ โโ java
โ โ โ โโ com/futurice/project
โ โ โโ res
โ โ โโ AndroidManifest.xml
โ โโ build.gradle
โ โโ proguard-rules.pro
โโ build.gradle
โโ settings.gradle
์ฃผ๋ ์ฐจ์ด์ ์ Gradle์์ ์จ ๊ฐ๋
์ธ๋ฐ, ์๋ก์ด ๊ตฌ์กฐ๊ฐ 'source sets' (main
, androidTest
)๋ฅผ ๋ช
์์ ์ผ๋ก ๋ถ๋ฆฌ์์ผ๋๋ค๋ ๊ฒ์ด๋ค. ์๋ฅผ ๋ค์ด src
์ paid์ free๋ผ๋ ๊ฐ๊ธฐ ๋ค๋ฅธ Flavor์ ํด๋นํ๋ ์์ค์ฝ๋๋ฅผ ๊ฐ๋ 'paid'๋ผ๋ ์์ค ์
๊ณผ 'free'๋ผ๋ ์์ค ์
์ ์ถ๊ฐํ ์ ์๋ค.
์ต์์ ๋ ๋ฒจ app
์ ๊ฐ๋ ๊ฒ์ ์ฑ๊ณผ ์ฑ์์ ์ฐธ์กฐ๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋ก์ ํธ๋ค(e.g., library-foobar
)์ ๊ตฌ๋ณํ๋ ๋ฐ์ ์ ์ฉํ๋ค. settings.gradle
์ app/build.gradle
์์ ์ฐธ์กฐํ ์ ์๋ ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋ก์ ํธ๋ค์ ๋ณด๊ดํ๋ค.
์ผ๋ฐ์ ์ธ ๊ตฌ์กฐ. Google's guide on Gradle for Android๋ฅผ ํ์ธํ์.
์์ Task๋ค. ์คํฌ๋ฆฝํธ๋ค(shell, Python, Perl, etc) ๋์ , Gradle์ Task๋ค์ ๋ง๋ค ์ ์๋ค. Gradle's documentation์์ ๋ ์์ธํ ๋ด์ฉ์ ํ์ธํ์.
๋น๋ฐ๋ฒํธ ์ฑ์ build.gradle
์ ๋ฆด๋ฆฌ์ฆ ๋น๋๋ฅผ ์ํ signingConfigs
์ ์๊ฐ ํ์ํ ๊ฒ์ด๋ค. ๋ค์์ ํผํ์.
์๋ ๋ฐฉ๋ฒ์ฒ๋ผ ์์ ํ์ง ์๋๋ก ํ๋ค. ์ด๋ ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์์ ๋ํ๋ ๊ฒ์ด๋ค.
signingConfigs {
release {
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
๋์ , gradle.properties
ํ์ผ์ ๋ง๋ค์. ์ด๋ ๋ฒ์ ๊ด๋ฆฌ ์์คํ
์ ์ถ๊ฐ๋์ด์ ์๋๋ค:
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
์ ํ์ผ์ gradle์ ์๋์ผ๋ก ์ํฌํธ๋์ด, build.gradle
์ ์ด๋ ๊ฒ ์ฌ์ฉํ ์ ์๋ค:
signingConfigs {
release {
try {
storeFile file("myapp.keystore")
storePassword KEYSTORE_PASSWORD
keyAlias "thekey"
keyPassword KEY_PASSWORD
}
catch (ex) {
throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
}
}
}
jar ํ์ผ ์ํฌํธ ๋์ Maven์ ์ ํธํ์. ํ๋ก์ ํธ์ jar ํ์ผ์ ๋ช
์์ ์ผ๋ก ํฌํจ์ํฌ ๊ฒฝ์ฐ, ์ด๋ค์ 2.1.1
์ฒ๋ผ ํน์ ํ๊ฒ ๊ณ ์ ๋ ๋ฒ์ ์ด ๋๋ค. jar๋ฅผ ๋ค์ด๋ก๋ํ๊ณ , ์
๋ฐ์ดํธํ๋ ๊ฒ์ ๊ท์ฐฎ์ ์ผ์ด๋ค. ๊ทธ๋ฌ๋ Maven์ ์ด ๋ฌธ์ ๋ฅผ ์ ์ ํ๊ฒ ํด๊ฒฐํด์ค ๊ฒ์ด๊ณ , ๋ํ ์ด๋ Android Gradle ๋น๋์์ ์ฅ๋ ค๋๋ ๋ฐฉ์์ด๋ค. ์๋ฅผ ๋ค์๋ฉด ์ด๋ ๋ค:
dependencies {
compile 'com.squareup.okhttp:okhttp:2.2.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}
Maven์ ๋์ ์์กด์ฑ ํด๊ฒฐ์ ํผํ๋ผ
2.1.+
๊ณผ ๊ฐ์ด ๋์ ์ผ๋ก ๋ฒ์ ์ ์ ํ๋ ๋ฐฉ์์ ๋ถ์์ ํ๊ณ , ๋น๋ ์ฌ์ด์ ๋ฏธ๋ฌํ๊ณ ์ดํดํ๊ธฐ ์ด๋ ค์ด ์ฐจ์ด๋ฅผ ์ด๋ํ ์ ์์ด ํผํ๋๋ก ํ์. 2.1.1
์ฒ๋ผ ์ ์ ์ผ๋ก ๊ณ ์ ๋ ๋ฒ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ณด๋ค ์์ ์ ์ด๊ณ , ์์ธก ๊ฐ๋ฅํ๊ณ , ๋ฐ๋ณต์ ์ธ ๊ฐ๋ฐ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ๋ฐ์ ๋์์ด ๋ ๊ฒ์ด๋ค.
ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃจ๋ ๋ฐ์ ์ฉ์ดํ ์๋ํฐ๋ผ๋ฉด ๋ฌด์์ด๋ ์ฌ์ฉํด๋ ์ข๋ค. ์๋ํฐ๋ ๊ฐ์ธ์ ์ธ ์ ํ์ด๊ณ , ๊ทธ ์๋ํฐ๊ฐ ํ๋ก์ ํธ ๊ตฌ์กฐ์ ํ๋ก์ ํธ ๋น๋ ์์คํ ์ ๋ฐ๋ผ ๊ธฐ๋ฅํ๋๋ก ํ๋ ๊ฒ์ ๊ฐ๋ฐ์์ ๋ชซ์ด๋ค.
ํ์ฌ ๊ฐ์ฅ ์ถ์ฒํ๋ IDE๋ Android Studio์ด๋ค. Google์ด ๊ฐ๋ฐํ๊ณ , Gradle์ ๊ฐ์ฅ ๋ฐ์ ํ๋ฉฐ, ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ก์ด ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋๋ฐ๋ค๊ฐ ์์ ํ ๋จ๊ณ์ ๋ค์ด๊ฐ Android ๊ฐ๋ฐ์ ์ ๋ง์ถ์ด์ ธ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ํ๋ค๋ฉด Eclipse ADT๋ฅผ ์ฌ์ฉํด๋ ์ข์ง๋ง, ๋น๋ํ๋ ๋ฐ์ ๋ก์ ํ๋ก์ ํธ ๊ตฌ์กฐ์ Ant๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ด์ ๋ํ ์ค์ ์ด ํ์ํ๋ค. Vim, Sublime Text, Emacs๊ฐ์ ํ๋ ์ธ ํ
์คํธ ์๋ํฐ๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. ์ด ๊ฒฝ์ฐ์๋ Gradle๊ณผ adb
๋ฅผ ์ปค๋งจ๋๋ผ์ธ์์ ์ฌ์ฉํด์ผ ํ๋ค. Eclipse์ Gradle ์ฌ์ฉ์ด ์ ๋๋ก ์๋ํ์ง ์๋๋ค๋ฉด, ์ปค๋งจ๋๋ผ์ธ์ผ๋ก ๋น๋ํ๊ฑฐ๋ Android Studio๋ก ์ฎ๊ธฐ์. ADT ํ๋ฌ๊ทธ์ธ์ด deprecate๋์๊ธฐ ๋๋ฌธ์, ์ด ๊ฒ์ด ๊ฐ์ฅ ์ข์ ์ต์
์ผ ๊ฒ์ด๋ค.
๋ฌด์์ ์ฌ์ฉํ๋ , ์ ํ๋ฆฌ์ผ์ด์
๋น๋์ ๊ณต์์ ์ธ ๋ฐฉ๋ฒ์ธ Gradle๊ณผ ์๋ก์ด ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฅด๊ณ , ํน์ ์๋ํฐ๋ฅผ ๋ฐ๋ฅด๋ ์ค์ ํ์ผ์ ๋ฒ์ ๊ด๋ฆฌ ์์คํ
์ ์ถ๊ฐํ๋ ๊ฒ์ ํผํ๋ ๊ฒ๋ง ๋ช
์ฌํ์. ์๋ฅผ ๋ค๋ฉด, Ant์ build.xml
ํ์ผ๋ค์ ์ถ๊ฐํ์ง ์๋๋ก ํ๋ค. ํนํ Ant์ ๋น๋ ์ค์ ์ ๋ณ๊ฒฝํ๊ณ ์๋ค๋ฉด build.gradle
์ ์ต์ ์ ์ํ๋ก ๊ธฐ๋ฅํ๋๋ก ํ๋ ๊ฒ์ ์์ง๋ง์. ๋ํ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค์๊ฒ ์น์ ํด์ง์. ๊ทธ๋ค์ ์ค์ ์ ๋ฐ๊พธ๋๋ก ๊ฐ์ํ์ง ์์์ผํ๋ค.
**Jackson**์ Object๋ฅผ JSON์ผ๋ก, ํน์ ๊ทธ ๋ฐ๋๋ก ๋ณํํด์ฃผ๋ Java ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. Gson์ด ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ์ ๋ง์ด ์ฐ์ด๊ธด ํ์ง๋ง, ์คํธ๋ฆฌ๋ฐ, ์ธ๋ฉ๋ชจ๋ฆฌ ํธ๋ฆฌ ๋ชจ๋ธ, ์ ํต์ ์ธ JSON-POJO ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ๊ณผ ๊ฐ์ ์ฌ๋ฌ ๋์๋ค์ ์ง์ํ๋ Jackson์ด ๋ ๊ณ ์ฑ๋ฅ์ผ ๊ฒ์ด๋ค. ํ์ง๋ง ๋ช ์ฌํ์. Jackson์ด GSON๋ณด๋ค ๋ ํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์, 65,000 ๋ฉ์๋ ์ ์ ํ์ ๋ถ๋ชํ ๊ฒฝ์ฐ๋ผ๋ฉด GSON์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋์ ์๋ ์๋ค. ๋ค๋ฅธ ๋์์ผ๋ก๋ Json-smart๊ณผ Boon JSON์ด ์๋ค.
๋คํธ์ํน, ์บ์ฑ, ์ด๋ฏธ์ง. ๋ฐฑ์๋ ์๋ฒ๋ก์ ์์ฒญ ์ฒ๋ฆฌ์ ๋ํด ํด๋ผ์ด์ธํธ๋ฅผ ๊ตฌํํ๊ณ ์ฒ๋ฆฌํ๋ ๋ ๊ฐ์ง ๊ฒ์ฆ๋ ํด๊ฒฐ์ฑ ์ด ์๋ค. Volley ํน์ Retrofit์ ์ฌ์ฉํ์. Volley๋ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๊ณ ์บ์ฑํ๋ ๋์ฐ๋ฏธ๋ฅผ ์ ๊ณตํ๋ค. Retrofit์ ์ ํํ๋ค๋ฉด, ์ด๋ฏธ์ง ๋ก๋ฉ๊ณผ ์บ์ฑ์๋ Picasso๋ฅผ, ํจ์จ์ ์ธ HTTP ์์ฒญ์๋ OkHttp๋ฅผ ๊ณ ๋ คํด๋ณด์. ์ด ๋ชจ๋ ์ธ๊ฐ์ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๊ฐ์ ํ์ฌ์์ ๊ฐ๋ฐ๋์ด ์๋ก ์ํธ๋ณด์์ด ๋งค์ฐ ์ฉ์ดํ๋ค. OkHttp can also be used in connection with Volley.
RxJava ๋น๋๊ธฐ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ Reactive Programming์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ์ด๋ ๋งค์ฐ ๊ฐ๋ ฅํ๊ณ ์ ๋งํ ํจ๋ฌ๋ค์์ผ๋ก, ๋๋ฌด ๋ค๋ฅธ ์ ์ด ๋ง์ ํผ๋์ค๋ฌ์ธ ์ ์๋ค. ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ํคํ ํธ๋ค์๊ฒ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๊ธฐ ์ ์ฃผ์ํ ๊ฒ์ ์ถ์ฒํ๋ค. RxJava๋ฅผ ์ด์ฉํ ๋ช ๊ฐ์ง ํ๋ก์ ํธ๊ฐ ์๋๋ฐ, ํ์ํ๋ค๋ฉด ์ด ์ฌ๋๋ค์๊ฒ์ ๋์์ ๊ตฌํ์: Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen. ์์ฑ๋ ๋ธ๋ก๊ทธ ํฌ์คํธ๋ ์๋ค: [1], [2], [3], [4].
Rx์ ๋ํ ๊ฒฝํ์ด ์๋ค๋ฉด, API ์๋ต ์ฒ๋ฆฌ์๋ง ์ ์ฉํด๋ณด์. ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก๋ ํด๋ฆญ ์ด๋ฒคํธ๋ ๊ฒ์ ํ์ดํ ์ด๋ฒคํธ์ ๊ฐ์ ๊ฐ๋จํ UI ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ์ ์ฉํด๋ณผ ์๋ ์๋ค. Rx ๊ธฐ์ ์ ์์ ๊ฐ์ด ์๊ฒจ ๋ชจ๋ ์ค๊ณ์ ์ ์ฉํ๊ณ ์ถ๋ค๋ฉด, ๋ชจ๋ ๊น๋ค๋ก์ด ๋ถ๋ถ๋ค์ Javadocs๋ฅผ ์์ฑํ์. RxJava์ ์ต์ํ์ง ์์ ๋ค๋ฅธ ํ๋ก๊ทธ๋๋จธ๊ฐ ํ๋ก์ ํธ๋ฅผ ์ ์ง, ๋ณด์ํ๋ ๋ฐ์ ์ด๋ ค์์ด ์์ ์ ์๋ค๋ ๊ฒ์ ๋ช ์ฌํด์ผ ํ๋ค. ๊ทธ๋ค์ Rx์ ์ฝ๋ ์ดํด์ ๋ํด ์ต์ ์ ๋คํด ๋์์ ์ฃผ์.
**Retrolambda**๋ Android ํน์ ๋ค๋ฅธ pre-JDK8 ํ๋ซํผ์์ Lambda ํํ ๋ฌธ๋ฒ์ ์ฌ์ฉํ ์ ์๋๋ก ํ๋ Java ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํนํ RxJava์ ๊ฐ์ด ๊ธฐ๋ฅ ์์ฃผ ์คํ์ผ์ ์ฝ๋๋ฅผ ๋์ฑ ํ์ดํธํ๊ณ ์ฝ๊ธฐ ์ข๊ฒ ๋ง๋ค์ด์ค๋ค. ์ฌ์ฉํ๋ ค๋ฉด, JDK8์ ์ค์นํ๊ณ ์ด๋ฅผ Android Studio ํ๋ก์ ํธ ๋ํ์์์์ SDK ๊ฒฝ๋ก๋ก ์ค์ ํ ํ, JAVA8_HOME
๊ณผ JAVA7_HOME
ํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํ ๋ค ํ๋ก์ ํธ root์ build.gradle์ ์ด๋ ๊ฒ ์ค์ ํ๋ค:
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.1'
}
๊ทธ๋ฆฌ๊ณ ๊ฐ๊ฐ ๋ชจ๋๋ค์ build.gradle์ ์๋ ๋ด์ฉ์ ์ถ๊ฐํ์.
apply plugin: 'retrolambda'
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
retrolambda {
jdk System.getenv("JAVA8_HOME")
oldJdk System.getenv("JAVA7_HOME")
javaVersion JavaVersion.VERSION_1_7
}
Android Studio๋ Java8 lambda์ ์ฝ๋ ์ง์์ ์ ๊ณตํ๋ค. ๋ง์ฝ lambda๊ฐ ์ฒ์์ด๋ผ๋ฉด, ๋ค์ ํญ๋ชฉ๋ค์ ๋ฐ๋ผ ์์ํด๋ณด์:
- ํ๋์ ๋ฉ์๋๋ฅผ ๊ฐ๋ ๋ชจ๋ ์ธํฐํ์ด์ค๋ค์ "lambda์ ๋ฐ์ "ํ๊ณ , ๋์ฑ ํ์ดํธํ ๋ฌธ๋ฒ์ผ๋ก ๋ฌถ์ผ ์ ์๋ค.
- ๋ง์ฝ ํ๋ผ๋ฉํฐ๋ค์ด ์์ฌ์ค๋ฝ๋ค๋ฉด, ์ผ๋ฐ ์ต๋ช ๋ด๋ถ ํด๋์ค๋ฅผ ์์ฑํ๊ณ Android Studio๊ฐ lambda๋ก ๋ฌถ์ด์ฃผ๋๋ก ํด๋ณด์.
Dex ๋ฉ์๋ ์ ํ์ ์ ์ํ๊ณ , ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์ ํผํ์. Android ์ฑ๋ค์ด dex ํ์ผ๋ก ํจํค์ง๋ ๋, 65,536๊ฐ์ ์ฐธ์กฐ ๋ฉ์๋ ์ ์ ํ์ ๊ฐ๋๋ค[1] [2] [3]. ์ ํ๋ ๋ฉ์๋ ์๋ฅผ ๋์ด์๋ฉด ์ปดํ์ผ์ Fatal error๋ฅผ ๋ณด๊ฒ๋ ๊ฒ์ด๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ์ต์ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ฌ์ฉํ๊ณ , dex-method-counts ํด์ ์ฌ์ฉํ์ฌ ์ ํ๋ ์๋ณด๋ค ์ ๊ฒ ์ ์งํ๊ธฐ ์ํด ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ฌ์ฉํ ์ง ๊ฒฐ์ ํ์. ํนํ Guava ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํผํ์. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ 13,000๊ฐ๊ฐ ๋๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
Fragment์ Activity๋ฅผ ์ด์ฉํ์ฌ Android์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ผ๋ก ์ค๊ณํ๋ ๋ฐฉ๋ฒ์ ์ปค๋ฎค๋ํฐ๋ Futurice ๊ฐ๋ฐ์๋ค ์ฌ์ด์์๋ ํฉ์๋ ๋ฐ๊ฐ ์๋ค. Square๋ Fragment์ ์ฐํ ๋์์ผ๋ก a library for building architectures mostly with Views๋ฅผ ์ ๊ณตํ๋๋ฐ, ์ด ๋ํ ์ปค๋ฎค๋ํฐ ์ฌ์ด์์ ๋๋ฆฌ ์ถ์ฒํ ๋งํ ๋ฐฉ์์ ์๋๋ผ๊ณ ์๊ฐ๋๋ค.
Android API์ ํ์คํ ๋ฆฌ๋ก ์ธํด, ๋ง์ฐํ Fragment๊ฐ ํ๋ฉด์์ UI ์กฐ๊ฐ์ด๋ผ๊ณ ๋ ์ฌ๋ฆด ์ ์์ ๊ฒ์ด๋ค. ์ฆ, Fragment๋ ์ผ๋ฐ์ ์ผ๋ก UI์ ์ฐ๊ด๋์ด ์๋ค๋ ๊ฒ์ด๋ค. Activity ๋ํ ๋ง์ฐํ๊ฒ ๊ทธ๋ค์ ๋ผ์ดํ์ฌ์ดํด๊ณผ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ์ ์ค์ํ ์ปจํธ๋กค๋ฌ๋ผ๊ณ ์๊ฐํ ์ ์๋ค. ๊ทธ๋ฌ๋, ๋ค์ ์ญํ ๋ค์์ ์ฐจ์ด๋ฅผ ์ฝ๊ฒ ์ฐพ์๋ณผ ์ ์๋ค: Activity๋ UI ์ญํ ์ ์ํํ๊ณ (delivering transitions between screens), Fragment๋ ๋ ๋ฆฝ์ ์ผ๋ก ์ปจํธ๋กค๋ฌ์ ์ญํ ์ ์ํํ๋ค. ๊ทธ๋์ ์ฐ๋ฆฌ๋ Fragment ํน์ Activity, ๋๋ View ์ ์ค ํ๋๋ง์ ์ด์ฉํ ๊ตฌ์กฐ๋ฅผ ์ ํํจ์ ์์ด์ ๊ฒฐํจ์ด ์๋ค๋ ์ ์ ํ์ ํ๊ณ ์ ํํ ๊ทผ๊ฑฐ๋ฅผ ๊ฐ๋ ๊ฒฐ์ ์ ํ์ฌ ์กฐ์ฌ์ค๋ฝ๊ฒ ์์ํ๊ธฐ๋ฅผ ๊ถํ๋ค. ๋ค์์ ์ฃผ์ํด์ผ ํ ๊ฒ๋ค์ ๋ํ ์กฐ์ธ์ธ๋ฐ, ์ ๋นํ ๊ฑธ๋ฌ์ ์์ฉํ์:
- Nested fragments๋ฅผ ๋๋ฆฌ ์ฌ์ฉํ๋ ๊ฒ์ ํผํด์ผ ํ๋๋ฐ, matryoshka bugs๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ค์ฒฉ๋ Fragment๋ ๊ผญ ํ๋นํ ๊ฒฝ์ฐ(์๋ฅผ ๋ค๋ฉด, ์ํ์ผ๋ก ์ฌ๋ผ์ด๋ฉํ๋ ViewPager ๋ด๋ถ์ Fragment๋ค)๋ ์ ์ค๋ช ํ ์ ์์ ๋งํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ์.
- Activity์ ๋๋ฌด ๋ง์ ์ฝ๋๋ฅผ ๋ฃ๋ ๊ฒ์ ํผํด์ผ ํ๋ค. ๊ฐ๋ฅํ๋ฉด ์ธ์ ๋ ์ง ๊ฐ๋ฒผ์ด ์ปจํ ์ด๋๋ก์ ์ ์งํ๊ณ , ์ฃผ๋ก ๋ผ์ดํ์ฌ์ดํด๊ณผ ๋ค๋ฅธ ์ค์ํ Android์์ ์ธํฐํ์ด์ฑ API๋ฅผ ์ํด์๋ง ์กด์ฌํ๋๋ก ํ์. ์์ Activity ๋ณด๋ค๋ ๋จ์ผ Fragment๋ก ๊ตฌ์ฑ๋ Activity๊ฐ ์ข๋ค - UI ์ฝ๋๋ฅผ Activity์ Fragment์ ๋ฃ์. ์ด๋ ๋ค๋ฅธ ์ ๊ตฌ์ฑ๋ ๋ ์ด์์, ํน์ ์ฌ๋ฌ Fragment๋ก ๊ตฌ์ฑ๋ ํ๋ธ๋ ํ๋ฉด์ผ๋ก ์ฎ๊ธธ ํ์๊ฐ ์์ ๋์ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋๋ก ๋ง๋ ๋ค. ์ ํํ ๊ทผ๊ฑฐ๊ฐ ์๋ ๊ฒฐ์ ์ด๋ผ๋ฉด Fragment์ ์ํธ ์์ฉํ์ง ์๋ Activity๋ ํผํ์.
- ์ฑ์ ๋ด๋ถ์ ๋์์ด Intent์ ๊ฐํ๊ฒ ์์กด์ ์ธ Android ๋ ๋ฒจ์ API๋ฅผ ๋จ์ฉํด์๋ ์๋๋ค. ์ด๋ ๋ฒ๊ทธ์ ๋ ์ ์ ๋ฐํ์ฌ Android OS๋ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ๋ค์ ์ํฅ์ ์ค ์ ์๋ค. ์๋ฅผ ๋ค์ด, ๋ง์ฝ ์ฑ์ด ๋น์ ์ ํจํค์ง ์ฌ์ด์์ ๋ด๋ถ์ ์ธ ์ปค๋ฎค๋์ผ์ด์ ์ ์ํด Intent๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์ฑ์ด OS ๋ถํ ๋ฐ๋ก ํ์ ์คํ๋์์ ๋ ์ฌ์ฉ์ ๊ฒฝํ ์์์ ๋ช ์ด๊ฐ์ ๋ ์ ๋ฐ์์ํฌ ์ ์๋ค๊ณ ์๋ ค์ ธ ์๋ค.
Android ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ Java ์ค๊ณ ๊ฐ๋จํ Model-View-Controller ๊ฐ๋ตํํ ์ ์๋ค. Android์์๋, Fragment and Activity are actually controller classes ๋ผ๊ณ ์ค๋ช ๋๋๋ฐ, ๋ค๋ฅธ ๋ฉด์์ Fragment์ Activity๋ค์ ๋ช ์์ ์ผ๋ก ์ฌ์ฉ์ ์ธํฐํ์ด์ค, ์ฆ View ์ด๊ธฐ๋ ํ๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์, Fragment(ํน์ Activity)๋ฅผ ์ ํํ Conteroller๋ View ๊ตฌ๋ถํ ์ ์๋ค. ๊ทธ๋์ ๊ทธ์ ํด๋นํ๋ fragments
ํจํค์ง์ ๋๋ ๊ฒ์ด ๋ซ๋ค. Activity๋ ์ด์ ์น์
์์์ ์กฐ์ธ์ ๋ฐ๋ผ ์ต์์ ํจํค์ง์ ๋ ์ ์๋ค. 2, 3๊ฐ ์ด์์ Activity๋ค์ ๊ณํํ๊ณ ์๋ค๋ฉด, activities
ํจํค์ง๋ฅผ ๋ง๋ค์ด ๋์.
๋ค๋ฅธ ๊ฒฝ์ฐ์๋, API ์๋ต์ ๋ํ JSON ํ์์ ์ํด ์ฑ์์ง POJO๋ค์ ๋ด๋ models
ํจํค์ง, ์ปค์คํ
View, Notification, Action bar view, Widget ๋ฑ์ ๋ด๋ views
ํจํค์ง๋ฅผ ๋์ด ์ค๊ณ๊ฐ ์ผ๋ฐ์ ์ธ MVC๋ก ํํ๋ ์ ์๋ค. Adapter๋ค์ ๋ฐ์ดํฐ์ View๋ค ์ฌ์ด์ ์กด์ฌํ๋ gray matter๋ผ๊ณ ํ ์ ์์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก getView()
๋ฅผ ํตํด View๋ค์ ์ถ์ถํ๋ ๋ฐ์ ํ์ํ๊ธฐ ๋๋ฌธ์ views
ํจํค์ง ์์ adapters
๋ผ๋ ์๋ธํจํค์ง๋ก ๋ ์ ์๋ค.
Controller ํด๋์ค๋ค์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์, Android ์์คํ
์ ๊ฐ๊น๊ฒ ์กด์ฌํ๋ค. ์ด๋ค์ managers
ํจํค์ง์ ๋ ์ ์๋ค. "DateUtils"์ ๊ฐ์ ๋ค์ํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํด๋์ค๋ค์ utils
ํจํค์ง์, ๋ฐฑ์๋์ ์ธํฐ๋์
ํ๋ ์ญํ ์ ๋งก๋ ํด๋์ค๋ค์ network
ํจํค์ง์ ๋์.
์ข ํฉ์ ์ผ๋ก, ๋ฐฑ์๋์ ๊ฐ๊น์ด ๊ฒ๋ถํฐ ์ ์ ์ ๊ฐ๊น์ด ์์๋๋ก ์ ๋ ฌํด๋ณด๋ฉด ์ด๋ ๋ค:
com.futurice.project
โโ network
โโ models
โโ managers
โโ utils
โโ fragments
โโ views
โโ adapters
โโ actionbar
โโ widgets
โโ notifications
์ด๋ฆ ์ ํ๊ธฐ. type_foo_bar.xml
๊ณผ ๊ฐ์ด ํ์
์ ์ ๋์ด๋ก ๋๋ ์ปจ๋ฒค์
์ ๋ฐ๋ฅด์. ์์: fragment_contact_details.xml
, view_primary_button.xml
, activity_main.xml
.
๋ ์ด์์ XML์ ์ฒด๊ณํํ๊ธฐ. ๋ ์ด์์ XML์ ์ด๋ค ํํ๋ก ๋ง๋ค์ง ํ์ค์น ์๋ค๋ฉด, ๋ค์ ์ปจ๋ฒค์ ์ด ๋์์ด ๋ ๊ฒ์ด๋ค.
- ํ ์์ฑ๋น ํ ์ค, ๋ค์ฌ์ฐ๊ธฐ๋ 4์นธ์ ์คํ์ด์ค
android:id
๋ฅผ ํญ์ ์ฒซ ์์ฑ์ผ๋กandroid:layout_****
์์ฑ๋ค์ ์์ชฝ์style
์์ฑ์ ๋งจ ์๋์- ํ๊ทธ๋ฅผ ๋ซ๋
/>
๋ ์ ๋ ฌ๊ณผ ์ ์์ฑ ์ถ๊ฐ๋ฅผ ์ํด ๋ ๋ฆฝ์ ์ธ ์ค์ - Rather than hard coding
android:text
์ ํ๋์ฝ๋ฉํ๋ ๊ฒ๋ณด๋ค, Android Studio์์ ์ฌ์ฉ ๊ฐ๋ฅํ Designtime attributes๋ฅผ ๊ณ ๋ คํ์.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="@string/name"
style="@style/FancyText"
/>
<include layout="@layout/reusable_part" />
</LinearLayout>
๊ฐ์ฅ ์ค์ํ ๊ฒ์, android:layout_****
๋ฅผ ๋ ์ด์์ XML์ ๋๊ณ android:****
๋ฅผ ์คํ์ผ XML์ ์ ์ํ๋ ๊ฒ์ด๋ค. ์ด ๊ท์น์ ์์ธ๊ฐ ์์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก ์ ๋์ํ๋ค. ์ด๋ ๋ ์ด์์(์์น, ์ฌ๋ฐฑ, ํฌ๊ธฐ)๊ณผ ๋ด์ฉ์ ๊ดํ ์์ฑ์ ๋ ์ด์์ ํ์ผ์ ๋๊ณ , ์์ธํ ๋ชจ์(์, ์์ชฝ ์ฌ๋ฐฑ, ํฐํธ)์ ๋ํ ๋ด์ฉ์ ์คํ์ผ ํ์ผ์ ๋๊ธฐ ์ํจ์ด๋ค.
์์ธ๋ ์ด๋ฐ ๊ฒฝ์ฐ๊ฐ ์๋ค:
android:id
๋ ์ ํํ ๋ ์ด์์ ํ์ผ์ ๋์ด์ผ ํ๋ค. should obviously be in the layout filesLinearLayout
์android:orientation
์์ฑ์ ์ผ๋ฐ์ ์ผ๋ก ๋ ์ด์์ ํ์ผ์ ์๋ ๊ฒ์ด ํ๋นํ๋ค.android:text
๋ ๋ด์ฉ์ ์ ์ํ๋ ์์ฑ์ด๊ธฐ ๋๋ฌธ์ ๋ ์ด์์ ํ์ผ์ ๋์ด์ผ ํ๋ค.- ๊ฐ๋ ์ผ๋ฐ์ ์ธ ์คํ์ผ๋ก
android:layout_width
์android:layout_height
์์ฑ๋ค์ ๋์ด์ผ ๋ง์ด ๋ ๊ฒ ๊ฐ์ง๋ง, ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ค์ ๋ ์ด์์ ํ์ผ์ ๋ณด์ฌ์ง๋ค.
์คํ์ผ์ ์ฌ์ฉํ์. View์ ์ค๋ณต๋๋ ๋ชจ์์ด ์ฌ์ฉ๋๋ ๊ฒ์ ๋งค์ฐ ์ผ๋ฐ์ ์ธ ์ผ์ด๊ธฐ ๋๋ฌธ์, ๊ฑฐ์ ๋ชจ๋ ํ๋ก์ ํธ๋ค์ด ์คํ์ผ์ ์ ์ ํ ์ฌ์ฉํด์ผํ๋ค. ์ ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋๋ถ๋ถ์ ํ ์คํธ ๋ด์ฉ๋ค์ ์ผ๋ฐ ์คํ์ผ์ ๊ฐ์ ธ์ผํ๋ค. ์๋ฅผ ๋ค๋ฉด ์ด๋ ๋ค:
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
TextView์ ์ ์ฉํด๋ณด๋ฉด:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
์๋ง ๋ฒํผ๋ค์๋ ๊ฐ์ ์ผ์ ํด์ฃผ์ด์ผ ํ๊ฒ ์ง๋ง, ๋ฉ์ถ์ง ๋ง์. ๊ณ์ ์งํํ๋ฉด์ ์ฐ๊ด๋์ด์๊ณ ์ค๋ณต๋ android:****
์์ฑ๋ค์ ์ผ๋ฐ ์คํ์ผ๋ก ๋ฌถ์.
ํฐ ์คํ์ผ ํ์ผ์ ๋ค๋ฅธ ํ์ผ๋ค๋ก ๋๋์. ๋จ ํ๋์ styles.xml
ํ์ผ์ ๊ฐ์ง ํ์๋ ์๋ค. Android SDK๋ ๋ฐ์ค ์ธ๋ถ์ ํ์ผ๋ค๋ ์ง์ํ๋๋ฐ, styles
๋ผ๋ ์ด๋ฆ์ ์ ํ ๋ง๋ฒ๊ฐ์ ๋ฌด์ธ๊ฐ๊ฐ ์์ด ํ์ผ ์์ <style>
XML ํ๊ทธ๊ฐ ์๋ค๋ฉด ์๊ด์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ์ผ ์ด๋ฆ์ styles.xml
, styles_home.xml
, styles_item_details.xml
, styles_forms.xml
๋ฑ๊ณผ ๊ฐ์ด ์ ํ ์ ์๋ค. ๋น๋ ์์คํ
์์ ์๋ฏธ๋ฅผ ๊ฐ๋ ๋ฆฌ์์ค ๋๋ ํ ๋ฆฌ์๋ ๋ค๋ฅด๊ฒ res/values
์์ ํ์ผ๋ช
์ ์์๋ก ์ค์ ๊ฐ๋ฅํ๋ค.
colors.xml
๋ ์ ํ๋ ํธ์ด๋ค. colors.xml
์๋ ์ ์ด๋ฆ๊ณผ RGBA ๊ฐ์ ๋งคํํด๋๋ ์ผ ์ธ์ ๋ ํด์ผํ ์ผ์ ์๋ค. ๊ฐ๊ฐ ๋ค๋ฅธ ๋ฒํผ ํ์
๋ค์ RGBA ๊ฐ๋ค์ ์ ์ํ์ง ์๋๋ก ํ์.
์๋์ ๋ฐฉ์์ ํผํ์:
<resources>
<color name="button_foreground">#FFFFFF</color>
<color name="button_background">#2A91BD</color>
<color name="comment_background_inactive">#5F5F5F</color>
<color name="comment_background_active">#939393</color>
<color name="comment_foreground">#FFFFFF</color>
<color name="comment_foreground_important">#FF9D2F</color>
...
<color name="comment_shadow">#323232</color>
์ด๋ฌํ ํ์์ผ๋ก RGBA ๊ฐ๋ค์ ๋ฐ๋ณตํ๊ธฐ ์ฌ์ด๋ฐ, ์ด๋ ๊ธฐ๋ณธ ์๊น์ ํ์์ ๋ฐ๋ผ ๋ณ๊ฒฝํ๊ธฐ ๋ณต์กํ๊ฒ ๋ง๋ ๋ค. ๋ํ ์ด๋ฌํ ๋ฐฉ์์ ์ ์๋ "button" ํน์ "comment" ์ฒ๋ผ ํน์ ๋ฌธ๋งฅ์ ๊ด๊ณ๋์ด์์ด colors.xml
์ด ์๋ ์คํ์ผ์ ๋ ์ ํฉํ๋ค.
๋์ , ์ด๋ ๊ฒ ํ์:
<resources>
<!-- grayscale -->
<color name="white" >#FFFFFF</color>
<color name="gray_light">#DBDBDB</color>
<color name="gray" >#939393</color>
<color name="gray_dark" >#5F5F5F</color>
<color name="black" >#323232</color>
<!-- basic colors -->
<color name="green">#27D34D</color>
<color name="blue">#2A91BD</color>
<color name="orange">#FF9D2F</color>
<color name="red">#FF432F</color>
</resources>
์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ด๋์๊ฒ ์ด ํ๋ ํธ๋ฅผ ์์ฒญํด๋ณด์. ์ด๋ฆ์ด ๊ผญ "green", "blue" ์ฒ๋ผ ์์ ์ด๋ฆ์ผ ํ์๋ ์๋ค. "brand_primary", "brand_secondary", "brand_negative" ๊ฐ์ ์ด๋ฆ๋ค์ด ๋์ฑ ๋ฐ์๋ค์ด๊ธฐ ์ฝ๋ค. ์ด๋ ๊ฒ ์์ ํ์์ ์ง์ ํ๊ฒ ๋๋ฉด ์ ๊ฐ๋ค์ ๋ฆฌํฉํ ๋งํ๊ธฐ ์ฌ์์ง๊ณ , ์ผ๋ง๋ ๋ง์ ์๋ค์ด ์ฌ์ฉ๋๊ณ ์๋์ง ๋ช ์์ ์ผ๋ก ์ ์ ์๋ค. ๋ณดํต ์ฌ๋ฏธ๊ฐ์ ์ค์์ํ๋ ์ฑ์์๋, ์ฌ์ฉ๋๋ ์์ ์ข ๋ฅ๋ฅผ ์ค์ด๋ ๊ฒ์ด ์ค์ํ๋ค.
dimens.xml์ colors.xml์ฒ๋ผ ๋ค๋ฃจ์. ๊ธฐ๋ณธ์ ์ผ๋ก ์๊ณผ ๊ฐ์ ๋ชฉ์ ์ ์ํด ์ผ๋ฐ์ ์ธ ์ฌ๋ฐฑ๊ณผ ํฐํธ ํฌ๊ธฐ ๋ฑ์ "ํ๋ ํธ"๋ฅผ ์ ์ํ์. ๋ค์์ dimens.xml ํ์ผ์ ์ข์ ์์์ด๋ค:
<resources>
<!-- font sizes -->
<dimen name="font_larger">22sp</dimen>
<dimen name="font_large">18sp</dimen>
<dimen name="font_normal">15sp</dimen>
<dimen name="font_small">12sp</dimen>
<!-- typical spacing between two views -->
<dimen name="spacing_huge">40dp</dimen>
<dimen name="spacing_large">24dp</dimen>
<dimen name="spacing_normal">14dp</dimen>
<dimen name="spacing_small">10dp</dimen>
<dimen name="spacing_tiny">4dp</dimen>
<!-- typical sizes of views -->
<dimen name="button_height_tall">60dp</dimen>
<dimen name="button_height_normal">40dp</dimen>
<dimen name="button_height_short">32dp</dimen>
</resources>
๋ฌธ์์ด๋ค์ด ์ผ๋ฐ์ ์ผ๋ก ๋ค๋ฃจ์ด์ง๋ฏ์ด, ๋ ์ด์์, ๋ฐ๊นฅ์ชฝ/์์ชฝ ์ฌ๋ฐฑ์ ํ๋์ฝ๋ฉ๋ ๊ฐ๋ค ๋์ spacing_****
์ ์ฌ์ฉํ์. ์ด๋ ์คํ์ผ๊ณผ ๋ ์ด์์์ ์ฒด๊ณํํ๊ณ ๋ณ๊ฒฝํ๋ ๊ฒ์ ์ฝ๊ฒ ํด์ค๊ณผ ๋์์ ์ผ๊ด๋ ๋ฃฉ์คํ(Look-and-feel)์ ์ ๊ณตํ๋ค.
strings.xml
strings.xml์ ๋ฌธ์์ด๋ค์ ๋ค์์คํ์ด์ค์ ํํ์ ๋น์ทํ๊ฒ ์ด๋ฆ์ ์ง๊ณ , 2๊ฐ ์ด์์ Key์ ๊ฐ์ ์ค๋ณตํด์ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ ค์ํ์ง ์๋๋ก ํ์. ์ธ์ด๋ ๋ณต์กํ๊ธฐ ๋๋ฌธ์, ๋ค์์คํ์ด์ค๊ฐ ๋ฌธ๋งฅ์ ๊ฐ๊ณ ์ ๋งคํจ์ ์์ ๋ ๊ฒ์ด ํ์์ด๋ค.
์๋ชป๋ ์
<string name="network_error">Network error</string>
<string name="call_failed">Call failed</string>
<string name="map_failed">Map loading failed</string>
์ข์ ์
<string name="error.message.network">Network error</string>
<string name="error.message.call">Call failed</string>
<string name="error.message.map">Map loading failed</string>
๋ฌธ์์ด ๊ฐ์ ๋ชจ๋ ๋๋ฌธ์๋ก ์ฐ์ง ์๋๋ก ํ๋ค. ์ผ๋ฐ์ ์ธ ํ
์คํธ ์ปจ๋ฒค์
์ ๋ฐ๋ฅด๋(e.g., ์ฒซ ๊ธ์๋ง ๋๋ฌธ์๋ก), ๋ง์ฝ ๋ฌธ์์ด์ ๋ชจ๋ ๋๋ฌธ์๋ก ํ์ํด์ผ ํ๋ค๋ฉด, TextView์ textAllCaps
์์ฑ์ ์ด์ฉํ์.
์๋ชป๋ ์
<string name="error.message.call">CALL FAILED</string>
์ข์ ์
<string name="error.message.call">Call failed</string>
๊น์ View ๊ณ์ธต์ ํผํ์. ๊ฐ๋ View๋ฅผ ํธ์ฑํ๊ธฐ ์ํด LinearLayout์ ํ๋ ๋ ์ถ๊ฐํ๋ คํ ๊ฒ์ด๋ค. ์ด ์ํฉ์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ์ผ์ผํจ๋ค:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
๋ ์ด์์ ํ์ผ์์ ๋ช ์์ ์ผ๋ก ์ด๋ฐ ํํ๋ฅผ ๋ณด์ง ๋ชปํ๋ค ํ๋๋ผ๋, ๊ฒฐ๊ตญ ๋ค๋ฅธ View์ ๋ ๋ค๋ฅธ View๋ค์ ์ฑ์ธ ๋(Java์์) ์ด๋ ๊ฒ ๋๋๊ฒ ๋ ๊ฒ์ด๋ค.
๋ ๊ฐ์ง ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ํ๋ก์ธ์๊ฐ ๋ค๋ฃจ์ด์ผ ํ UI ํธ๋ฆฌ๊ฐ ๋ณต์กํด์ง๋ฉด์ ํผํฌ๋จผ์ค ๋ฌธ์ ๋ฅผ ๊ฒฝํํ๊ฒ ๋ ๊ฒ์ด๋ค. ๋ ํ๋์ ์ฌ๊ฐํ ๋ฌธ์ ๋ StackOverflowError์ ๊ฐ๋ฅ์ฑ์ด๋ค.
๊ทธ๋ฌ๋ฏ๋ก, ์ต๋ํ View ๊ณ์ธต์ ์ํํ๊ฒ ์ ์งํ์: RelativeLayout๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ, optimize your layouts ๋ฐฉ๋ฒ๊ณผ <merge>
tag๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ํ์ธํ์.
WebView์ ๊ด๋ จ๋ ๋ฌธ์ ๋ค์ ์ ์ํ์. ๋ด์ค ๊ธฐ์ฌ์ ๊ฐ์ ์นํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ์ด์ผ ํ ๋, ๋ฐฑ์๋ ํ๋ก๊ทธ๋๋จธ๋ค์๊ฒ "์์ํ" HTML์ ์์ฒญํ์ง ์๊ณ HTML์ ์ ๋ฆฌํ๊ธฐ ์ํด ํด๋ผ์ด์ธํธ๋จ์์ ํ๋ก์ธ์ฑํ๋ ๊ฒ์ ํผํ์. ApplicationContext๊ฐ ์๋ Activity๋ก์ ์ฐธ์กฐ๋ฅผ ์ ์งํ ๋, WebViews can also leak memory. ๊ฐ๋จํ ํ ์คํธ์ ๋ฒํผ์ ์ฌ์ฉํ ๋์๋ WebView๋ฅผ ํผํ๊ณ TextView์ Button์ ์ฌ์ฉํ๋๋ก ํ๋ค.
Android SDK์ ํ
์คํ
ํ๋ ์์ํฌ๋ ์ฌ์ ํ ๋ฏธํกํ๋ฐ, ํนํ UI ํ
์คํธ์ ๊ดํด์๋ ๋๋์ฑ ๊ทธ๋ ๋ค. Android Gradle์ ํ์ฌ extension of JUnit with helpers for Android์ ์ฌ์ฉํ์ฌ ๋ง๋ค์ด์ง JUnit ํ
์คํธ๋ค์ ์คํํ๋ connectedAndroidTest
๋ผ๋ ํ
์คํธ Task๋ฅผ ๊ตฌํํ๊ณ ์๋ค. ์ด๋ ๊ธฐ๊ธฐ ํน์ ์๋ฎฌ๋ ์ดํฐ์ ์ฐ๊ฒฐ๋ ํ
์คํธ๋ฅผ ์คํํด์ผ ํ ๊ฒ์ ์๋ฏธํ๋ค. ๊ณต์ ๊ฐ์ด๋์ธ [1] [2]๋ฅผ ๋ฐ๋ผ ํ
์คํธํ์.
View๊ฐ ์๋ ์ ๋ ํ ์คํธ์๋ Robolectric๋ฅผ ์ฌ์ฉํ์. ์ด๋ ๊ฐ๋ฐ ์๋์ ๋ง์กฑ์ ์ํด "๊ธฐ๊ธฐ์ ์ฐ๊ฒฐ๋์ง ์์" ํ ์คํธ์ ์ ๊ณต์ ์ถ๊ตฌํ๋ ํ ์คํธ ํ๋ ์์ํฌ์ด๋ค. ํนํ ๋ชจ๋ธ๊ณผ View ๋ชจ๋ธ๋ค์ ์ ๋ ํ ์คํธ์ ์ ํฉํ๋ค. ํ์ง๋ง, UI ํ ์คํธ์์์ Robolectric๋ฅผ ์ฌ์ฉํ ํ ์คํธ๋ ๋ถ์ ํํ๊ณ ๋ถ์์ ํ๋ค. ์ ๋๋ฉ์ด์ , ๋ํ ์์ ๋ฑ์ ๊ดํ UI ์์๋ค์ ํ ์คํธ์์๋ ๋ฌธ์ ๋ฅผ ๋ณด์ผ ๊ฒ์ด๊ณ , ์ด๋ "์ด๋ ์์์ ๊ฑท๋ ๊ฒ"(์กฐ์ํ ๋งํ ํ๋ฉด์ ๋ณด์ง ์๊ณ ํ ์คํธ)์ฒ๋ผ ๋งค์ฐ ๋ณต์กํ ๊ฒ์ด๋ค.
Robotium์ UI ํ ์คํธ ์์ฑ์ ์ฝ๊ฒ ํด์ค๋ค. UI์ ์ฐ๊ฒฐ๋ ํ ์คํธ๋ฅผ ์คํํ๋ ๋ฐ์ Robotium์ด ํ์ํ์ง ์์ง๋ง, View๋ฅผ ๊ฐ์ ธ์ ๋ถ์ํ๊ณ , ํ๋ฉด์ ์กฐ์ํ ์ ์๊ฒ ํด์ฃผ๋ Robotium์ ๋ง์ ๋์ฐ๋ฏธ๋ค์ด ์ ์ฉํ๊ฒ ์์ฉํ ๊ฒ์ด๋ค. ํ ์คํธ ์ผ์ด์ค๋ ์ด์ฒ๋ผ ์ฝ๊ฒ ํํ๋๋ค:
solo.sendKey(Solo.MENU);
solo.clickOnText("More"); // searches for the first occurence of "More" and clicks on it
solo.clickOnText("Preferences");
solo.clickOnText("Edit File Extensions");
Assert.assertTrue(solo.searchText("rtf"));
์ ๋ฌธ์ ์ผ๋ก Android ์ฑ์ ๊ฐ๋ฐํ๊ณ ์๋ค๋ฉด, Genymotion emulator์ ๋ผ์ด์ผ์ค๋ฅผ ๊ตฌ๋งคํ์. Genymotion ์๋ฎฌ๋ ์ดํฐ๋ ์ผ๋ฐ์ ์ธ AVD ์๋ฎฌ๋ ์ดํฐ์ ๋นํด ๋น ๋ฅธ FPS๋ก ์คํ๋๋ค. ์ฑ์ ๋ฐ๋ชจํ๊ณ , ๋คํธ์ํฌ ์ฐ๊ฒฐ ํ์ง, GPS ํฌ์ง์ ์ ์ํํด๋ณด๋ ํด๋ค์ ์ ๊ณตํ๋ค. ๋ํ ์ฐ๊ฒฐ๋ ํ ์คํธ์ ์ด์์ ์ผ๋ก ์๋ํ๋ค. ๋ง์(์ ๋ถ๋ ์๋) ๋ค๋ฅธ ๊ธฐ๊ธฐ๋ค์ ์ ํ ๋, Genymotion์ ๋ผ์ด์ผ์ค๋ ์ค์ ์ฌ๋ฌ๊ฐ์ง ๊ธฐ๊ธฐ๋ฅผ ๊ตฌ๋งคํ๋ ๊ฒ ๋ณด๋ค ํจ์ฌ ์ ๋ ดํ๋ค.
์ฃผ์ํ ์ : Genymotion ์๋ฎฌ๋ ์ดํฐ๋ Google Play Store์ Maps๊ณผ ๊ฐ์ ๋ชจ๋ Google ์๋น์ค๋ค์ ํฌํจํ๊ณ ์์ง๋ ์๋ค. ์ผ์ฑ์ ํน์ API๋ฅผ ํ ์คํธํด์ผ ํ๋ค๋ฉด, ์ค์ ์ผ์ฑ ๊ธฐ๊ธฐ๊ฐ ํ์ํ๋ค.
ProGuard๋ ์ผ๋ฐ์ ์ผ๋ก Android ํ๋ก์ ํธ์ ํจํค์ง๋ ์ฝ๋๋ฅผ ์ถ์ํ๊ณ , ๋๋ ํํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
ProGuard๋ฅผ ์ฌ์ฉํ๊ณ ์๋์ง ์๋์ง๋ ํด๋น ํ๋ก์ ํธ ์ค์ ์ ๋ฌ๋ ค์๋ค. ๋ณดํต ๋ฆด๋ฆฌ์ฆ apk๋ฅผ ๋น๋ํ ๋ gradle์ ์ค์ ํ๊ณ ProGuard๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค.
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
์ด๋ค ์ฝ๋๊ฐ ๋ณด์กด๋์ด์ผ ํ๊ณ ์ด๋ค ์ฝ๋๊ฐ ๋ฒ๋ ค์ง๊ฑฐ๋ ๋๋
ํ๋์ด์ผํ ์ง ๊ฒฐ์ ํ๊ธฐ ์ํด, ์ฝ๋์ ํ ๊ฐ ์ด์์ ์ํธ๋ฆฌ ํฌ์ธํธ๋ฅผ ์ค์ ํด์ผํ๋ค. ์ด ์ํธ๋ฆฌ ํฌ์ธํธ๋ ์ผ๋ฐ์ ์ผ๋ก main ๋ฉ์๋, applets, midlets, Activity์ ๊ฐ์ ๊ฒ๋ค์ด๋ค.
Android ํ๋ ์์ํฌ๋ SDK_HOME/tools/proguard/proguard-android.txt
์์ ์ฐพ์๋ณผ ์ ์๋ ๊ธฐ๋ณธ ์ค์ ์ ์ฌ์ฉํ๋ค. ์์ ์ค์ ์ ์ฌ์ฉํ์ฌ, my-project/app/proguard-rules.pro
์ ์ฌ์ฉ์ํ๋ ํน์ ํ๋ก์ ํธ ProGuard ๊ท์น์ ๊ธฐ๋ณธ ์ค์ ์ ์ถ๊ฐ๋ก ์ค์ ๋ ๊ฒ์ด๋ค.
ProGuard์ ๊ด๋ จ๋ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ๋ ์ด๋ ํ Warning๋ ์์ด ๋น๋ ์ปค๋งจ๋๊ฐ ์ฑ๊ณตํ๋๋ฐ๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ์์์์ ClassNotFoundException
๋ NoSuchFieldException
์ ๋น์ทํ ์์ธ๋ก ํฌ๋์๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
์ด๋ ๋ ๊ฐ์ง ์ค ํ๋๋ฅผ ์๋ฏธํ๋ค:
- ProGuard๊ฐ ํด๋์ค, Enum, ๋ฉ์๋, ํ๋ ํน์ ์ด๋ ธํ ์ด์ ๋ค์ ํ์ํ์ง ์๋ค๊ณ ์ฌ๊ฒจ ์ ๊ฑฐํ ๊ฒ์ด๋ค.
- ProGuard๊ฐ ํด๋์ค, Enum, ํ๋ ์ด๋ฆ๋ค์ด ๊ฐ์ ์ ์ผ๋ก ๊ณ ์ ์ ์ด๋ฆ๋๋ก ์ฌ์ฉ๋๊ณ ์์์๋ ๋ถ๊ตฌํ๊ณ (์๋ฅผ ๋ค๋ฉด Java reflection์ ํตํด) ์ด๋ค์ ๋๋ ํ(์ด๋ฆ ๋ณ๊ฒฝ)ํ ๊ฒ์ด๋ค.
๋ฌผ์์ ๊ฐ์ฒด๊ฐ ์ ๊ฑฐ๋์๋์ง ๋ณด๋ ค๋ฉด app/build/outputs/proguard/release/usage.txt
์ ํ์ธํ์.
๋ฌผ์์ ๊ฐ์ฒด๊ฐ ๋๋
ํ๋์๋์ง ๋ณด๋ ค๋ฉด app/build/outputs/proguard/release/mapping.txt
์ ํ์ธํ์.
ProGuard๊ฐ ํ์ํ ํด๋์ค ํน์ ํด๋์ค ๋ฉค๋ฒ๋ค์ ๋ฒ๊ฒจ๋ด๋ ๊ฒ์ ๋ง๊ธฐ ์ํด์๋, ProGuard ์ค์ ์ keep
์ต์
์ ์ถ๊ฐํ์:
-keep class com.futurice.project.MyClass { *; }
ProGuard๊ฐ ํด๋์ค ํน์ ํด๋์ค ๋ฉค๋ฒ๋ค์ ๋๋
ํ์ ๋ง๊ณ ์ถ๋ค๋ฉด, keepnames
์ ์ถ๊ฐํ์:
-keepnames class com.futurice.project.MyClass { *; }
์์๋ก this template's ProGuard config๋ฅผ ํ์ธํ์. ๋ ๋ค๋ฅธ ์์๋ก Proguard๋ฅผ ์ฝ์ด๋ณด์.
ํ๋ก์ ํธ์ ์ด๊ธฐ์, ๋ฆด๋ฆฌ์ฆ ๋น๋๋ฅผ ๋ง๋ค์. ์ด๋ ProGuard ๊ท์น๋ค์ด ์ค์ํ ๊ฒ๋ค์ ์ ํํ๊ฒ ๋ณด๊ดํ๊ณ ์๋์ง ํ์ธํ๊ธฐ ์ํจ์ด๋ค. ๋ํ ์ธ์ ๋ ์ง ์๋ก์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํฌํจ์์ผฐ์ ๋, ๋ฆด๋ฆฌ์ฆ ๋น๋๋ฅผ ๋ง๋ค๊ณ ๊ธฐ๊ธฐ์์ apk๋ฅผ ํ ์คํธํด๋ณด์. ๋ฆด๋ฆฌ์ฆ ๋น๋๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ฑ์ด "1.0" ๋ฒ์ ์ด ๋๊ธฐ๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ๋ง๊ณ , ์ ์ฐจ๋ก ์์ธ์ ๋ฌธ์ ๋ค์ ๋ฐ๊ฒฌํ๊ณ ์์ ํ๋ ์งง์ ์๊ฐ์ ๊ฐ์.
ํ. ๋ฐฐํฌ์์ mapping.txt
ํ์ผ๋ค์ ๋งค ๋ฆด๋ฆฌ์ฆ๋ง๋ค ์ ์ฅํ์. ๊ฐ ๋ฆด๋ฆฌ์ฆ ๋น๋๋ง๋ค mapping.txt
ํ์ผ์ ๋ณด๊ดํด๋๋ฉด, ์ฌ์ฉ์๊ฐ ๋ฒ๊ทธ๋ฅผ ๋ง๋๊ณ ์์๋ณด๊ธฐ ํ๋ ์คํ ํธ๋ ์ด์ค๋ฅผ ๋ณด๋ด์์ ๋ ๋ฌธ์ ๋ฅผ ํ์คํ๊ฒ ๋๋ฒ๊น
ํ ์ ์๋ค.
DexGuard. ๋ฆด๋ฆฌ์ฆ ์ฝ๋๋ฅผ ์ต์ ํํ๊ณ , ํนํ ์๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค๊ธฐ ์ํด ํ๋์ฝ์ดํ ํด์ด ํ์ํ๋ค๋ฉด, ProGuard๋ฅผ ๋น๋ํ ๊ฐ์ ํ์์ ๋ง๋ ์์ ์ํํธ์จ์ด์ธ DexGuard๋ฅผ ๊ณ ๋ คํด๋ณด์. ์ด๋ 65,000 ๋ฉ์๋ ์ ์ ํ์ ํด๊ฒฐํ๊ธฐ ์ํด Dex ํ์ผ๋ค์ ์ฝ๊ฒ ๋๋๋ค.
Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mรคntylรค, Mark Voit, Andre Medeiros, Paul Houghton and other Futurice developers for sharing their knowledge on Android development.
Futurice Oy Creative Commons Attribution 4.0 International (CC BY 4.0)
Translated to Korean (ko
) by Minsoo Park.
Original content by Futurice Oy.