์๋๋ก์ด๋ ๋กค๋ฆฌํ(5.0) ๋ฒ์ ์ ๊ตฌ๊ธ์ด ์๋๋ก์ด๋๋ฅผ ๋ฐํํ ์ดํ๋ก ๊ฐ์ฅ ํฐ ๋ณํ๋ฅผ ์ค ์ด์์ฒด์ ๋ค. ๊ตฌ๊ธ์ ํ๋์ปด ์ดํ ๊ณ์ ์ ์งํ๋ ๋์์ธ ์ธ์ด 'ํ๋ก(Holo)'๋ฅผ ๋ฒ๋ฆฌ๊ณ ์๋ก์ด ๋์์ธ ์ธ์ด์ธ ๋จธํฐ๋ฆฌ์ผ ๋์์ธ์ผ๋ก ์๋๋ก์ด๋ ์ธ๊ด์ ๋๋์ ์ผ๋ก ๋ณ๊ฒฝํ๋ค. ์ด ๋ณํ๋ ๋จ์ํ ๊ฒ๋ชจ์ต์๋ง ๊ทธ์น์ง ์์๋ค. ๋ด๋ถ์ ์ผ๋ก๋ 5000๊ฐ ์ด์์ API๋ฅผ ์ถ๊ฐํ๊ณ ์๋นํ ์์ API๋ฅผ ํ๊ธฐ(deprecated)ํ๋ค. ART์ ํ๋ก์ ํธ ๋ณผํ(Project Volta)๋ฑ์ ๊ตฌ์กฐ์ ์ธ ํ์ ๋ ๋ค๋ฐ๋๋ค. ์๋๋ก์ด๋ ๋กค๋ฆฌํ์ ๋ชจ๋ ๋ณํ๋ฅผ ํ ๋ฒ์ ๊ธ๋ก ์ ๋ถ ๋ค๋ฃฐ ์ ์๊ฒ ์ง๋ง, ๊ฐ๋ฐ์ ์ ์ฅ์์ ๋์์ด ๋ ๋งํ ์ฃผ์ ๋ณํ๋ค์ ๋ชจ์ ์๊ฐํ๊ณ ์ ํ๋ค.
๊น์ฉ์ฑ [email protected] | GDG Korea Android ์ค๊ฑฐ๋์ด์ . ์ํ๋ฅผ ๊ด์ ์ผ๋ก ์ข์ํ๋ ์๋๋ก์ด๋ ๊ฐ๋ฐ์๋ก ์ต๊ทผ์๋ Reactive Programming์ ๋น ์ ธ RxJava, RxAndroid์ ๋งค์งํ๊ณ ์๋ค.
<๊ทธ๋ฆผ 1> ์๋๋ก์ด๋ ๋กค๋ฆฌํ(5.0)
๊ตฌ๊ธ์ ์ ค๋ฆฌ๋น(4.1) ์ดํ ์๋๋ก์ด๋์ ์ฃผ์ ์ ๋ฐ์ดํธ ๋๋ง๋ค ํ๋ก์ ํธ๋ฅผ ํ๋์ฉ ์งํํ๋ค. ์ ค๋ฆฌ๋น์ ํ๋ก์ ํธ ๋ฒํฐ(Project Butter)๋ฅผ ํตํด ์พ์ ํ ์ฌ์ฉ์ ๊ฒฝํ์ ์ป์๊ณ , ํท์บฃ(4.4)์ ํ๋ก์ ํธ ์ค๋ฒจํธ(Project Svelte)๋ฅผ ํตํด 512MiB(Mebibyte) ๋ฉ๋ชจ๋ฆฌ ํ๊ฒฝ์์๋ ์ํํ ์ ์๋ ๋ ๋ ตํ ๋ชธ์ ๊ฐ๊ฒ ๋๋ค. ์ต์ ๋ฒ์ ๋กค๋ฆฌํ(5.0)์ ํ๋ก์ ํธ ๋ณผํ(Project Volta)๋ฅผ ํตํด ์กฐ๊ธ ๋ ์ ์ ์ ๋ ฅ์ ์ฌ์ฉํ๊ฒ ๋๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ํจ์จ์ ๋์ด๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ ๊ณ ์ํ๋ค.
์ํฉ์ ๋ํ ๊ณ ๋ ค๊ฐ ์๋ ๋น๋๊ธฐ ์์ ์ ๋ฐฐํฐ๋ฆฌ ์ฌ์ฉ์ ๋ถ์ ์ ์ด๋ค. ๋คํธ์ํฌ๊ฐ ์์ ์ ์ด์ง ์์ ์ํฉ ์์ ์ฌ์ฉ์๊ฐ ์ดฌ์ํด ๋ ์ฌ์ง์ ์๋ฒ๋ก ๋ฐฑ์ ํ๋ฉด ํด๋ํฐ์ ๋ฐฐํฐ๋ฆฌ๊ฐ ๊ธ๋ฐฉ ๋ฐ๋ฅ์ด ๋ ์ ์๋ค. ๊ทธ ๋๋ฌธ์ ๋คํธ์ํฌ๊ฐ ์์ ์ ์ผ ๋๋ ์ถฉ์ ์ค์ ์ฌ์ง์ ์ ๋ก๋ํ๋ ๊ฒ์ด ํจ์ฌ ํจ์จ์ ์ด๋ค. ๋กค๋ฆฌํ์ JobScheduler API๋ฅผ ๋์ ํด ๋ฐฐํฐ๋ฆฌ์ ๋คํธ์ํฌ ์ํฉ์ ๋ฐ๋ผ ์ ์ ํ ์์ ๊ณํ์ ์ก์ ์ ์๋๋ก ํ๋ค.
JobScheduler API๋ ์ฌ์ฉ์ ์์ค์ ํด๋์ค์ ์๋๋ก์ด๋ ์๋น์ค๋ก ๊ตฌ์ฑ๋ผ ์๋ค. ๋กค๋ฆฌํ ์ฅ๋น๊ฐ ์ฐ๊ฒฐ๋ PC์์ <๋ฆฌ์คํธ 1>์ ์ปค๋งจ๋๋ฅผ ์ ๋ ฅํ๋ฉด JobScheduler์ ์๋๋ก์ด๋ ์๋น์ค๋ฅผ ํ์ธํ ์ ์๋ค.
$ adb shell service list | grep jobscheduler
25 jobscheduler: [android.app.job.IJobScheduler]
<๋ฆฌ์คํธ 1> ์๋๋ก์ด๋ ์๋น์ค ๋ฆฌ์คํธ์์ jobscheduler
์ถ๊ฐ๋ฅผ ํ์ธ
JobScheduler API๋ JobInfo
์ JobService
2๊ฐ ๊ฐ์ฒด๋ก ๊ตฌ์ฑ๋ผ ์๋ค.
JobInfo
- ์์ ์ด ์ํ๋ผ์ผ ํ๋ ์ ์ฝ ์ํฉ์ ๊ธฐ์ ํ๋ค(๋คํธ์ํฌ, ์ถฉ์ , ์ฃผ๊ธฐ ๋ฑ).JobService
- ๊ณํ๋ ์์ ์ ์ํํ๋ ์๋น์ค ๊ฐ์ฒด
์ง์ ์์
์ ์ ์ํ๊ณ ์ํํด๋ณด์. <๋ฆฌ์คํธ 2>๋ ์์ดํ์ด ๋คํธ์ํฌ๊ฐ ์ฐ๊ฒฐ๋ผ ์์ผ๋ฉด์ ์ถฉ์ ์ด ์งํ๋๋ ์์ ํ ํ๊ฒฝ์์๋ง MicrosoftwareService
์๋น์ค๋ฅผ ์ํํ๋๋ก ํ ์ฝ๋๋ค. ์ ์ ํ ์ฃผ๊ธฐ์ ๋ฐ๋๋ผ์ธ๋ ์ค์ ํ๋ค.
JobInfo job = new JobInfo.Builder(JOB_ID, new ComponentName(this, MicroSoftwareService.class))
.setRequiredNetworksCapabilities(JobInfo.NETWORK_TYPE_UNMETERED)
.setPeriodic(15 * DateUtils.HOURS_IN_MILLIS)
.setRequiresCharging(true)
.setOverrideDeadline(3600000)
.build();
JobService mJobService = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
mJobService.scheduleJob(job);
<๋ฆฌ์คํธ 2> JobScheduler API ์ฌ์ฉ
๋ ์์ธํ JobInfo
ํ๋ผ๋ฏธํฐ๋ฅผ ์๊ณ ์ถ์ผ๋ฉด JobInfo.Builder (https://developer.android.com/reference/android/app/job/JobInfo.Builder.html)๋ฅผ ์ฐธ๊ณ ํ์.
JobService
๊ฐ์ฒด์ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ธ ์์
์ํ์๋ ์ถฉ๋ถํ์ง๋ง ๋ค์ํ ์์ฉ ๊ฐ๋ฐ ๊ณผ์ ์ ํ์ฅ์ด ํ์ํ ์ ์๋ค. JobService
๋ ์ค๋ฒ๋ผ์ด๋ฉ ๊ฐ๋ฅํ ๋ฉ์๋ onStartJob
๊ณผ onStopJob
์ ๊ฐ์ง๋ฉฐ, ์ด๋ฅผ ์์ ํด ์กฐ๊ธ ๋ ์ธ๋ฐํ ์์
์ด ๊ฐ๋ฅํ๋๋ก ์กฐ์จํ๋ค.
JobService
์ ํ์ฅ์ ์ํด์ <๋ฆฌ์คํธ 3>๊ณผ ๊ฐ์ด ๋จผ์ AndroidManifest.xml
์ ์๋ก์ด ์๋น์ค๋ฅผ ๋ฑ๋กํด์ผ ํ๋ค.
<service
android:name="kr.co.imaso.MasoJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true" />
<๋ฆฌ์คํธ 3> AndroidManifest.xml
๋ฑ๋ก
<๋ฆฌ์คํธ 4>์ onStartJob
์ ์์
์ด ์์๋ ๋ ํธ์ถ๋๋ฉฐ ๋ณ๋๋ก ์ํํ ์์
์ด ๋ฑ๋ก๋๋ ๊ณณ์ด๋ค. onStopJob
์ ์์
์ด ๋๋ ๋ ํธ์ถ๋๋ ๊ณณ์ด๋ค. ๋ฐํ ๊ฐ true๋ ์ค๋ฒ๋ผ์ด๋ฉ์ด ๋๊ณ ์ปค์คํฐ๋ง์ด์ง๋ ์์
์ด ์ํ๋์์ ์๋ฏธํ๋ค. onStartJob
์ด ๋ฆฌํด๋๊ธฐ ์ ์ ์ถ๊ฐ์ ์ผ๋ก ํด์ผํ ์ผ์ ์ํํ์.
public class MasoJobService extends JobService {
@Override
public boolean onStartJob(JobParameters jobParameters) {
// FIXME: ์์
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
<๋ฆฌ์คํธ 4> ํ์ฅ๋ JobService
, MasoJobService
์ ํ๋ฆฌ์ผ์ด์
์ด ์ํ๋ ํ ๋ฐฐํฐ๋ฆฌ์ ์ํฉ์ ๊ณ์ ๋ณํํ๋ค. ์๋๋ก์ด๋ ๋กค๋ฆฌํ์ ๋ฐฐํฐ๋ฆฌ ์ํฉ ๊ฐ๊ด์ ํ์ธํ ์ ์๋ ๋ช
๋ น dumpsys batterystats
๋ฅผ ์ ๊ณตํ๋ค.
$ adb shell dumpsys batterystats โcharged kr.co.imaso.MasoActivity
<๋ฆฌ์คํธ 5> kr.co.imaso.MasoActivity
ํจํค์ง ๋ฐฐํฐ๋ฆฌ ์ํฉ ํ์ธ
์กฐ๊ธ ๋ ์์ธํ ๋ฐฐํฐ๋ฆฌ ์๋ชจ๋ฅผ ํ์ธํ๊ธฐ ์ํด ๋ฒ๊ทธ๋ฆฌํฌํธ๋ฅผ ์ถ๋ ฅํ ์๋ ์๋ค. ๋ฒ๊ทธ๋ฆฌํฌํธ๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด์๋ <๋ฆฌ์คํธ 6>๊ณผ ๊ฐ์ ๋ช ๊ฐ์ง ์ค์ ์ ์ฐจ๊ฐ ํ์ํ๋ค.
$ adb shell dumpsys batterystats --enable full-wake-history
Enabled: full-wake-history
$ adb shell dumpsys batterystats --reset
Battery stats reset.
<๋ฆฌ์คํธ 6> ์ ์ฒด ์จ์ดํฌ ํ์คํ ๋ฆฌ๋ฅผ ๊ธฐ๋กํ๋๋ก ํ๊ฒฝ ์ค์
๋ถ์ํ๊ณ ์ถ์ ์ฑ์ ์ฌ์ฉํ ํ <๋ฆฌ์คํธ 7>์ ์ปค๋งจ๋๋ฅผ ์ ๋ ฅํด ๋ฒ๊ทธ๋ฆฌํฌํธ๋ฅผ ์ถ์ถํ๋ค.
$ adb bugreport > bugreport.txt
<๋ฆฌ์คํธ 7> ๋ฒ๊ทธ๋ฆฌํฌํธ๋ฅผ ํตํด ์จ์ดํฌ ํ์คํ ๋ฆฌ๋ฅผ ์ถ์ถ
์ถ์ถ๋ ๋ฒ๊ทธ๋ฆฌํฌํธ์ ๋ถ์์ ์ํด ๊ตฌ๊ธ์ด ๊ณต๊ฐํ 'Battery Historian(https://github.com/google/battery-historian)'์ ์ฌ์ฉํ๋ค. ๋ฆฌํฌ์งํ ๋ฆฌ์์ historian.py๋ฅผ ๋ค์ด๋ก๋ ๋ฐ์ ์ ์ ํ ๊ณณ์ ์ค์นํ ํ <๋ฆฌ์คํธ 8>์ ์ปค๋งจ๋๋ฅผ ์ ๋ ฅํ๋ค.
python historian.py bugreport.txt > bugreport.html
<๋ฆฌ์คํธ 8> Battery Historian์ ์ฌ์ฉํ์ฌ HTML ๋ฆฌํฌํธ๋ฅผ ์์ฑ
<๊ทธ๋ฆผ 2> ํ์คํ ๋ฆฌ์ธ HTML ๋ฆฌํฌํธ
๋กค๋ฆฌํ์ ๋
ธํฐํผ์ผ์ด์
(Notification)์ ์กฐ๊ธ ๋ ์ธ์ฌํ๊ณ ๊ฐ๋ ฅํ๊ณ ๊นํ์ค๋ฌ์์ก๋ค. ํด๋ํฐ์ ์ ๊ธ ์ํ๋ก ํด๋จ์์๋ ๋ถ๊ตฌํ๊ณ ๋ฏผ๊ฐํ ๋ฉ์ ์ ์ฑํ
์ด ์๋ฆผ์ ํตํด ๋
ธ์ถ๋ผ ๋๊ฐํ๋ ์ฌ๋๋ค์๊ฒ ํฌ์์์ด๋ค. ๋์ ์๋ก๋ฌ๋กํ ๋
ธํฐํผ์ผ์ด์
์ ์ด์ ๊ธฐ๋ํ ์ ์๊ฒ ๋๋ค. RemoteControlClient
๋ฅผ ์ด์ฉํด ์ปค์คํฐ๋ง์ด์งํ๋ ๊ฑธ ์ฆ๊ฒผ๋ ์ฌ๋๋ค์ ์ด์ ๊ทธ ๋งํผ์ ์์ ๋ฅผ ๋ง๋ฝํ๊ธฐ ์ด๋ ค์ธ ๊ฒ์ด๋ค.
<๊ทธ๋ฆผ 3> ํค์ฆ์ ๋ ธํฐํผ์ผ์ด์ . ์ฐ์ ์์๊ฐ ๋์ ๋ ธํฐํผ์ผ์ด์ ์ ์์ ์ฐฝ์ผ๋ก ํ์ํ๋ค.
์ ๊ธ ์ํ์์ ๋ ธํฐํผ์ผ์ด์ ๊ฐ์์ฑ์ 3๋จ๊ณ๋ก ๋๋๋ค.
VISIBILITY_PRIVATE
- ๋ ธํฐํผ์ผ์ด์ ์์ด์ฝ๊ณผ ๊ฐ์ ๊ธฐ๋ณธ์ ์ธ ์ ๋ณด๋ง ํ์ถ๋๋ฉฐ ์์ธํ ๋ด์ฉ์ ๊ฐ์ถ๋ค.VISIBILITY_PUBLIC
- ๋ ธํฐํผ์ผ์ด์ ์ ์ปจํ ์ธ ๊ฐ ๋ณด์ธ๋ค.VISIBILITY_SECRET
- ์์ด์ฝ์ ํฌํจํด ์๋ฌด ๊ฒ๋ ๋ณด์ด์ง ์๋๋ค.
์ฌ์ฉ์์๊ฒ ๋ฏผ๊ฐํ ๋
ธํฐํผ์ผ์ด์
์ Notification.builder.setVisibility(VISIBILITY_PRIVATE)
๋ VISIBILITY_SECRET
์ผ๋ก ๋ถ๋ฅํด ํ๋ผ์ด๋ฒ์๋ฅผ ๊ฐํํ ์ ์๋ค.
๋ ธํฐํผ์ผ์ด์ ์ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ๋ ์ธ๋ฐํ๊ฒ ์ค์ ํ ์ ์๊ฒ ๋๋ค. ๊ทธ ๋๋ถ์ ๋ฉํ ๋ฐ์ดํฐ์ ๋ง์ถฐ ์ด์์ฒด์ ๊ฐ ๋ ธํฐํผ์ผ์ด์ ์ ์ ๋ฆฌํด ๋ณด์ฌ์ค ์ ์๋ค.
setCategory()
- ์ด์์ฒด์ ๊ฐ ์ฐ์ ์์์ ๋ฐ๋ผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ํํธ๋ฅผ ์ค ์ ์๋ค(๋ ธํฐํผ์ผ์ด์ ์ด ์ ํ, ๋ฉ์์ง, ์๋๊ณผ ๊ด๋ จ์ด ์๋์ง ์ฌ๋ถ๋ฅผ ์ค์ ํ ์ ์๋ค).setPriority()
- ๋ ธํฐํผ์ผ์ด์ ์ ์ฐ์ ์์๋ฅผ ๋ณ๊ฒฝํด ์ค์๋์ ๋ฐ๋ผ ์ ๋ณด๋ฅผ ๋ถ๋ฆฌํ ์ ์๋ค.PRIORITY_MAX
์PRIORITY_HIGH
์ ๊ฒฝ์ฐ ์๊ฒ ๋ ์๋ ํ๋ฉด์ผ๋ก ๋ํ๋๋ฉฐ ๋์์ ์๋ฆฌ๋ฅผ ๋ด๊ฑฐ๋ ์ง๋์ ์ธ๋ฆฌ๊ฒ ๋๋ค.addPerson()
- ๋ ธํฐํผ์ผ์ด์ ์ ๋์์ ํ ์ฌ๋ ์ด์ ์ค์ ํ ์ ์๋ค. ํน์ ์ฌ๋, ๋๋ ์ฌ๋ฌ ์ฌ๋์๊ฒ ์ค์ํ ๋ ธํฐํผ์ผ์ด์ ์ ๋ณด๋ผ ์ ์๋ค.
์นดํ ๊ณ ๋ฆฌ์ ์ฐ์ ์์์ ๋ํ ์์ธํ ๋ด์ฉ์ Notification ํญ๋ชฉ(http://developer.android.com/reference/android/app/Notification.html)์ ์ฐธ๊ณ ํ์.
๋กค๋ฆฌํ์์๋ ๋
ธํฐํผ์ผ์ด์
์์ด์ฝ์ ์ ์ฑ
๋ ๋ณ๊ฒฝ๋๋ค. ์ค๋ชฐ ์์ด์ฝ์ ์์์ ํฐ์๊ณผ ํฌ๋ช
์๋ง ์ธ ์ ์๋ ์ ์ฝ์ด ์๊ฒผ๋ค. ํ์ฌ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ํ๊ฒ ๋ฒ์ ๊ณผ ๋จ๋ง๊ธฐ์ ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋ณด์ด์ง๋ง ๋กค๋ฆฌํ ์ดํ์ ํ๊ฒฝ์ ๊ณ ๋ คํ ๋ ์ค๋ชฐ ์์ด์ฝ์ ํฐ์๊ณผ ํฌ๋ช
์์ผ๋ก๋ง ๋์์ธ ํ๋ ๊ฒ์ด ์ ์ ํ๋ค. ๋กค๋ฆฌํ์ ์ค๋ชฐ ์์ด์ฝ์ setColor
๋ฅผ ํธ์ถํด ๋ฐฐ๊ฒฝ ์์์ ์ ํ ์ ์์ผ๋ ๋จ์ ์์ด์ฝ๊ณผ ๋ฐฐ๊ฒฝ ์์์ ์กฐํ๋ฅผ ๊ณ ๋ คํ๋ ๊ฒ์ด ์ข๋ค(<๋ฆฌ์คํธ 9> ์ฐธ๊ณ ).
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
int color = getResources().getColor(R.color.imaso);
builder.setColor(color);
Notification notif = builder.build();
<๋ฆฌ์คํธ 9> ๋ฐฐ๊ฒฝ ์์์ด ๋ฑ๋ก๋ ๋ ธํฐํผ์ผ์ด์
NotificationCompat
์ ๊ตฌ ๋ฒ์ ๋จ๋ง์์๋ ์ฌ์ฉํ ์ ์๋๋ก ํธํ์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ถ๊ฐ๋ ๋
ธํฐํผ์ผ์ด์
๊ฐ์ฒด์ด๋ค. ํธํ์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ build.gralde
ํ์ผ์ <๋ฆฌ์คํธ 10>์ ๊ธฐ์ ๋ ์์กด์ฑ์ ์ถ๊ฐํด์ผ ํ๋ค.
dependencies {
compile 'com.android.support:appcompat-v7:21.0.3'
}
<๋ฆฌ์คํธ 10> appcompat
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ ๋ฑ๋ก
RemoteControlClient
๊ฐ ํ๊ธฐ๋จ์ ๋ฐ๋ผ ์์
์ฌ์ ์ฑ ๋ฑ์ ๋
ธํฐํผ์ผ์ด์
๋ณ๊ฒฝ์ด ์๊ตฌ๋๋ค. <๋ฆฌ์คํธ 11>์ฒ๋ผ ๋
ธํฐํผ์ผ์ด์
์คํ์ผ Notification.MediaStyle
์ ์์ฑํ๊ณ Notification.Builder
์ ์คํ์ผ๋ก ๋ฑ๋กํด ์ด์ฉํ๋ค.
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("๋ง์ดํฌ๋ก์ํํธ์จ์ด")
.setContentText("์๋๋ก์ด๋ ๋กค๋ฆฌํ")
.setDeleteIntent(pendingIntent)
.setStyle(new Notification.MediaStyle());
<๋ฆฌ์คํธ 11> MediaStyle
ํ์์ ๋
ธํฐํผ์ผ์ด์
<๋ฆฌ์คํธ 12>๊ณผ ๊ฐ์ด MediaSessionManager
์๋น์ค๋ฅผ ์ป์ ํ ์ธ์
์ ๋ง๋ค๊ณ , ๊ทธ ์ธ์
์ผ๋ก๋ถํฐ ํ ํฐ์ ๋ฐ์์ ๋ฏธ๋์ด ์ปจํธ๋กค์ ์ค์ ํ๋ค.
mManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
mSession = mManager.createSession("microsoftware session");
mController = MediaController.fromToken(mSession.getSessionToken());
<๋ฆฌ์คํธ 12> MediaController
ํ๊ฒฝ ์ค์
์ธ์
์ TransportControlsCallback
์ ๋ฑ๋กํ๊ณ , ๊ฐ ์ํฉ์ ๋ฐ๋ผ ๋ค๋ฅธ ๋
ธํฐํผ์ผ์ด์
์ ์์ฑํ๋๋ก onPlay
, onPause
๋ฑ์ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ๋ค. <๋ฆฌ์คํธ 13>์ ์ฝ๋ฐฑ์ ๋ฑ๋กํ๋ ๊ณผ์ ๊ณผ ์ค์ผ๋ ํค์ ๋๋ต์ ์ผ๋ก ๋ณด์ฌ์ค๋ค. ์ค๋ฒ๋ผ์ด๋ฉํ ๋ฉ์๋๋ง๋ค ๊ฐ๋ณ์ ์ผ๋ก ๋
ธํฐํผ์ผ์ด์
์ ์ค์ ํ๊ณ ๋์์ผ ํ๋ค. ์๋ฅผ ๋ค์ด onPlay
๋ฉ์๋๋ ์ฌ์๊ณผ ๊ด๋ จ๋ ๋
ธํฐํผ์ผ์ด์
์ ๋์์ผ ํ๋ค.
mSession.addTransportControlsCallback(new MediaSession.TransportControlsCallback() {
@Override
public void onPlay() {}
@Override
public void onPause() {}
...
}
<๋ฆฌ์คํธ 13> TransportControlsCallback
์ ๊ฐ๊ด๊ณผ ๋ฑ๋ก ๊ณผ์
๋
ธํฐํผ์ผ์ด์
์ ์ก์
์ ๋ค๋ฅธ ์๋น์ค๋ก ์ฐ๊ฒฐ์ํค๊ณ ํด๋น ์๋น์ค์์ mController.getTransportControls().play()
๋ฑ์ ํธ์ถํ๋ค.
์ต๊ทผ ํ๋ฉด(recents screen)์ด ์ค๋ฒ๋ทฐ(overview)๋ก ๋ณ๊ฒฝ๋๋ค. ์ต๊ทผ ์์
๋ค์ 3D๋ก ๊ฐ์ฑ์๊ฒ ๋ ๋๋ง๋๋ฉฐ ์ฌ๋ฌ ๋ฌธ์์ ํญ์ ์ฝ๊ฒ ์ด๋ํ ์ ์๋๋ก AppTask
๋ฅผ ๋ชจ๋ ํ์ถํ๋ค. ์น๋ธ๋ผ์ฐ์ ์ ํญ๋ค์ ์ค๋ฒ๋ทฐ๋ฅผ ํตํด ์ด๋ํ ์ ์๊ณ ๊ตฌ๊ธ ๋
์ค์ ์ฌ๋ฌ ๋ฌธ์๋ฅผ ์ค๋ฒ๋ทฐ ์คํฌ๋ฆฐ์์ ์ด๋ํ ์ ์๋ค.
<๊ทธ๋ฆผ 4> ์ค๋ฒ๋ทฐ(overview), ์ต๊ทผ ๋ฌธ์(์์ , ํญ)๋ฅผ 3D ๋ฐ์ค๋ก ํํํ๋ค.
์๋ก์ด ๋ํ๋จผํธ๋ฅผ ์์ฑํ๊ธฐ ์ํด์๋ <๋ฆฌ์คํธ 14> ์ฒ๋ผ android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT
ํ๋๊ทธ๋ฅผ ํฌํจํ ์กํฐ๋นํฐ๋ฅผ ํธ์ถํ๋ค.
Intent intent = new Intent(this, MicroSoftware.class);
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
startActivity(intent);
<๋ฆฌ์คํธ 14> ๋ํ๋จผํธ๋ฅผ ์ค๋ฒ๋ทฐ์ ์ถ๊ฐํ๊ธฐ ์ํด FLAG_ACTIVITY_NEW_DOCUMENT
ํ๋๊ทธ๋ฅผ ์ฌ์ฉ
์น์ฌ์ดํธ ๋ฌธ์๋ ์ค๋ฒ๋ทฐ์ ๋์ํ ์ ์๋ค. ์น ๋ฌธ์์ <๋ฆฌ์คํธ 15>์ ๊ฐ์ด ๋ฉํ ๋ฐ์ดํฐ theme-color
๋ฅผ ์ค์ ํ๋ฉด ์ค๋ฒ๋ทฐ ๋ฌธ์ ์์ฑ์ผ๋ก ํ
๋ง ์์ด ๋ฑ๋ก๋๋ค.
<meta name="theme-color" content="#3FFFB5">
<๋ฆฌ์คํธ 15> ์ค๋ฒ๋ทฐ๋ฅผ ์ํ ๋ฉํ๋ฐ์ดํฐ ์ ์ฉ ์นํ์ด์ง
์ด๊ธฐ ์๋๋ก์ด๋์ ํ์ฌ๋ ๊ฐ์ ๋จธ์ ๋ฌ๋น (Dalvik)์ ๋ชจ๋ฐ์ผ ํ๊ฒฝ์ ๊ณ ๋ คํด ์ ์ ๋ฉ๋ชจ๋ฆฌ, ์ต์ ํ๋ ๋ฆฌ์์ค ๊ด๋ฆฌ, ์ต์ํ ์ค๋ฒํค๋ ๋ฑ์ด ๋ชฉํ์๋ค. ์์ฃผ ์ํ๋๋ ๊ตฌ๊ฐ(ํธ๋ ์ด์ค, trace)์ ๊ธฐ๊ณ์ด ์ฝ๋๋ก ๋ฐ๊พธ๋ JIT(Just-in-time) ์ปดํ์ผ๋ฌ๋ ์๋๋ก์ด๋ ํ๋ก์(2.2) ๋ฒ์ ์์์ผ ๋์ ๋๋ค. ๋ฌ๋น ์ ๊ธฐ๋ณธ ์ ๋ต์ ๋ฌ๋น ๋ฐ์ดํธ์ฝ๋(Dalvik bytecode)๋ก ๋ ์ฝ๋๋ฅผ ํ ์ค์ฉ ํด์ํ๊ณ ๋ฐ๋ณต ์ํ๋๋ ๊ตฌ๊ฐ์ ๊ธฐ๊ณ์ด ์ฝ๋๋ก ๋ณํํด ํจ์จ์ ๋์ด๋ ๊ฒ์ด์๋ค. ํธ๋ ์ด์ค๋ผ ๋ถ๋ฆฌ๋ ํน์ ๊ตฌ๊ฐ์ ๋ฒ์ญํ๋ ์์ ์ ๋ฉ์๋ ๋จ์๋ก ๊ธฐ๊ณ์ด๋ก ๋ณํํ๋ ๊ฒ๋ณด๋ค ์๊ฐ์ด ์งง๊ฒ ์์๋๋ค. ๋ ๋ณํ๋ ๊ธฐ๊ณ์ด ์ฝ๋์ ์ฉ๋์ด ์์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ๊ณ CPU ํ์๊ฐ ๋ฎ์ ์ํฉ์ ์ ํฉํ๋ค. ๊ทธ๋ฌ๋ ๊ฒฝ๋ ๊ฐ์ ๋จธ์ ์์ ์์ํ ๋ฌ๋น ์ ๊ณ ์ฑ๋ฅ๊ณผ ๊ณ ๊ธฐ๋ฅ์์ ๊ตฌ์กฐ์ ํ๊ณ๋ฅผ ๋๋ฌ๋๋ค.
๊ตฌ๊ธ์ ์๋๋ก์ด๋ ํท์บฃ๋ถํฐ ART(Android Runtime)๋ฅผ ์ค๋นํ๊ณ ๋กค๋ฆฌํ๋ถํฐ๋ ๊ฐ์ ์ฌํญ์ด ๋๋ค. ART๋ AOT(ahead-of-time) ๋ฐฉ์์ ๋ฐํ์ ํ๊ฒฝ์ด๋ค. ART์ ๋ด์ฅ๋ dex2oat ์ ํธ๋ฆฌํฐ๋ ๋ฌ๋น ์์ ์ฐ์ด๋ ๋ฌ๋น ๋ฐ์ดํธ์ฝ๋์ ๋ฆฌ์์ค ํ์ผ์ด ํตํฉ๋ .dex ํ์ผ์ ๋ฆฌ๋ ์ค์์ ๋๋ฆฌ ์ฐ์ด๋ ์คํํ์ผ ํํ ELF(Executable and Linkable Format)๋ก ๋ณํํ๋ค. dex2oat๋ ์ฑ์ ์ด๊ธฐ ์ํ ์ ํธ์ถ๋๋ฉฐ ART์ AOT๋ ์ฑ์ ์ต์ด ์ํ ๊ณผ์ ์ dex2oat ์ ํธ๋ฆฌํฐ๋ฅผ ์ด์ฉ, ์คํ ํ์ผ์ ์ป์ด๋ด๋ ๊ธฐ์ ์ธ ์ ์ด๋ค. ์ด๋ก ์ธํด ๋กค๋ฆฌํ์ ์ํ ์ฑ๋ฅ, ๊ฐ๋น์ง ์ปฌ๋ ์ (GC : Garbage collection)์ ์ฑ๋ฅ, ํ๋กํ์ผ๋ง, ๋๋ฒ๊น ๋ฑ์์ ์ด์ ์ ์ป์๋ค.
<๊ทธ๋ฆผ 5> ๋ฌ๋น ๊ณผ ART์ ์ฐจ์ด. ๋ฌ๋น ์ DEX ํ์ผ์์ ์ต์ ํ๋ Odex๋ฅผ ์ป๊ณ ART๋ ์คํํ์ผ ELF๋ฅผ ์ป๋๋ค.
์๋ก์ด ๋ฐฉ์์ด ์ ์ฉ๋๊ธฐ ๋๋ฌธ์ ์ฑ์ ๋ฐ๋ผ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. AOT์ ๊ณผ๋ จํด์๋ ์๋๋ก์ด๋ ํ๋ซํผ๊ณผ ๊ด๋ จ๋ ์ด์๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ ์๋๋ก์ด๋ ์ด์ ๋ฆฌ์คํธ(https://code.google.com/p/android/issues/list)๋ฅผ ์ฐธ์กฐํ๋ฉฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์.
GC์ ๊ตฌ์กฐ๊ฐ ๋ณ๊ฒฝ๋๊ธฐ ๋๋ฌธ์ GC_FOR_ALLOC
์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ๋น๋๋ฅผ ์ค์ด๊ธฐ ์ํด ๋ช
์์ ์ผ๋ก System.gc()
๋ฅผ ํธ์ถํ ํ์๊ฐ ์์ด์ก๋ค. ํ์ฌ ํ๊ฒฝ์ด ๋ฌ๋น
์ด ์๋ ART์ธ ๊ฒ์ ํ์ํ๊ธฐ ์ํด <๋ฆฌ์คํธ 16>์ ์ปค๋งจ๋๋ฅผ ์ด์ฉํด์ ๋ฒ์ ์ ๋ณด๋ฅผ ์ป๋๋ค.
System.getProperty("java.vm.version")
<๋ฆฌ์คํธ 16> ART ํ๊ฒฝ ํ์ธ
๋ฒ์ ์ ๋ณด๊ฐ 2.0.0 ์ด์์ธ ๊ฒฝ์ฐ ๋ช ์์ ์ธ GC ํธ์ถ์ ํ์๊ฐ ์ค์ด๋ค์๋ค. ๋ฒ์ ์ ๋ณด์ ๋ฐ๋ผ GC ํธ์ถ์ ์ ์ธํ์.
ART์ ์ฑํ์ ๋ฐ๋ผ ๊ธฐ์กด์ ์ ๋์ํ๋ JNI ์ฑ์ ๋์์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค. JNI์ ๋์์ ๋ฌธ์ ๊ฐ ์๋ ๊ฒฝ์ฐ ์๋๋ก์ด๋์ ํฌํจ๋ CheckJNI ํด์ด ๋์์ด ๋๋ค.
$ adb shell setprop debug.checkjni 1
<๋ฆฌ์คํธ 17> JNI ๋๋ฒ๊น ๋ชจ๋ ํ์ฑํ
ํ๊ฒฝ์ด ์ค์ ๋ ํ JNI ์ฝ๋๊ฐ ํฌํจ๋ ์ฑ์ ์ํํ ๋ ์์คํ ์ ์ข ์ข <๋ฆฌ์คํธ 18>๊ณผ ๊ฐ์ด ๊ฒฝ๊ณ ๋ ์๋ฌ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค.
W JNI WARNING: method declared to return 'Ljava/lang/String;' returned '[B'
W failed in LJniTest;.exampleJniBug
I "main" prio=5 tid=1 RUNNABLE
I | group="main" sCount=0 dsCount=0 obj=0x40246f60 self=0x10538
I | sysTid=15295 nice=0 sched=0/0 cgrp=default handle=-2145061784
I | schedstat=( 398335000 1493000 253 ) utm=25 stm=14 core=0
I at JniTest.exampleJniBug(Native Method)
I at JniTest.main(JniTest.java:11)
I at dalvik.system.NativeStart.main(Native Method)
I
E VM aborting
<๋ฆฌ์คํธ 18> ํฅ์๋ ๋๋ฒ๊น ๋ฉ์์ง
ํฅ์๋ ๋๋ฒ๊ทธ ๋ชจ๋๋ฅผ ์ด์ฉํด ์์๋๋ JNI ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์.
๊ธฐ์กด ์๋๋ก์ด๋์์๋ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์ ์ฃผ์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ ๊ฒฝ์ฐ๋ ์์๋ค. ๋ฐ๋ฉด์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ ํจ์จ์ ๋์ด๊ธฐ ์ํด ์๋๋ก์ด๋ ART๋ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์ ์ ๋ฆฌํ๋ Compacting GC๋ฅผ ๋์
ํ๊ณ ์๋ค. Compacting GC๋ ๋ฉ๋ชจ๋ฆฌ์ ์ฐ์์ ์ธ ๋ฐฐ์น๋ฅผ ํตํด ํจ์จ์ฑ์ ๋์ด๋ ๋ฉ์ปค๋์ฆ์ด๋ค. ์ฐ์๋ ๋ฐฐ์น๋ฅผ ์ํ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์ ์ด๋์ ๊ธฐ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์ ์ฃผ์๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค. Get<type>ArrayElements
๋ฅผ ํธ์ถํด ์ฃผ์๋ฅผ ์ป์ ๊ฒฝ์ฐ ์ ์ ํ Release<type>ArrayElements()
๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ART ์์ ์ฃผ์ ๊ฐ์ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ๊ฒ์ด๋ผ๋ ๊ฑธ ์ ์ํ์.
Object
ํด๋์ค์ ํ๋ ์์ฑ์ด private
์ผ๋ก ๋ณ๊ฒฝ๋๋ค. Object
ํ๋๋ฅผ Reflection
์ผ๋ก ์ ๊ทผํ๋ ๊ฒฝ์ฐ์ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค.
<permission android:name= "com.example.gcm.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<๋ฆฌ์คํธ 19> ์ปค์คํ ํผ๋ฏธ์ ์ ๋ณด์ ๋ ๋ฒจ์ ์ธ์ฆํค๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋ค.
์๋๋ก์ด๋ ๋กค๋ฆฌํ๋ถํฐ ์ปค์คํ
ํผ๋ฏธ์
์ ๋์ผํ ์ฌ์ธํค๋ฅผ ๊ฐ์ง ์ฑ์์๋ง ์ฌ์ฉํ ์ ์๋๋ก ๋ณ๊ฒฝ๋๋ค. ์ปค์คํ
ํผ๋ฏธ์
์ ์ฌ์ฉํ ๋๋ <๋ฆฌ์คํธ 19>์ ๊ฐ์ด android:protectionLevel="signature"
๋ฅผ ์ค์ ํด์ผ ํ๋ค.
๋ง์ผ ๊ฐ์ ํผ๋ฏธ์
์ ์ฌ์ฉํ๊ณ ์๋ ์ฑ์ด ๋ค๋ฅธ ์ฌ์ธํค๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด INSTALL_FAILED_DUPLICATE_PERMISSION
์๋ฌ ๋ฉ์์ง์ ํจ๊ป ์ค์น๊ฐ ๊ฑฐ๋ถ๋๋ค. ์ด ๋ณ๊ฒฝ ์ฌํญ์ ์ฑ์ targetSDK
๋ฒ์ ๊ณผ๋ ๋ฌด๊ดํ๋ฉฐ ๋กค๋ฆฌํ ๋๋ฐ์ด์ค์์๋ ๊ฐ์ ๋ก ์ ์ฉ๋๋ ์ฌํญ์ด๋ค.
๊ธฐ๋ณธ ํผ๋ฏธ์ ์ผ๋ก ๊ฐ๋ฅํ๋ค๋ฉด ๊ฐ๋ฅํ ์ปค์คํ ํผ๋ฏธ์ ์ ์ฐ์ง ์๋ ๊ฒ์ด ์ข๋ค. ์ปค์คํ ํผ๋ฏธ์ ์ ๋ฐ๋์ ์จ์ผ ํ๋ค๋ฉด ํจํค์ง๋ช ์ ๋ถ์ฌ ๋ค๋ฅธ ์ปค์คํ ํผ๋ฏธ์ ๊ณผ ์ถฉ๋ํ์ง ์๋๋ก ํ๊ณ ์ฌ๋ฌ ์ฑ์์ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด ์ฌ์ธํค๋ฅผ ์ ๊ด๋ฆฌํด์ผ ํ๋ค.
์๋น์ค ๋ฐ์ธ๋๋ฅผ ํ ๋ ๋ช ์์ ์ธ ์ธํ ํธ๋ง ๊ฐ๋ฅํ๋๋ก ๋ฐ๋์๋ค. <๋ฆฌ์คํธ 20>๊ณผ ๊ฐ์ด ์๋ฌต์ ์ธ ๋ฐ์ธ๋๋ฅผ ์์ฒญํ ๊ฒฝ์ฐ์๋ ์คํ์๊ฐ ์์ธ๊ฐ ๋ฐ์ํ๋ค.
Intent intent = new Intent(MICROSOFTWARE_BINDING);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
<๋ฆฌ์คํธ 20> ์๋ฌ๊ฐ ๋ฐ์ํ๋ ์๋ฌต์ ์๋น์ค ๋ฐ์ธ๋ฉ
๋กค๋ฆฌํ์์ ์ ๋๋ก ๋ ๋ฐ์ธ๋๋ <๋ฆฌ์คํธ 21>๊ณผ ๊ฐ๋ค.
Intent intent = new Intent(this, MicroSoftwareService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
<๋ฆฌ์คํธ 21> ๋กค๋ฆฌํ์์ ๊ถ์ฅํ๋ ๋ช ์์ ์๋น์ค ๋ฐ์ธ๋ฉ
<๊ทธ๋ฆผ 6> ๋จธํฐ๋ฆฌ์ผ ๋์์ธ
๊ตฌ๊ธ์ ํตํฉ ๋์์ธ ์ธ์ด ๋จธํฐ๋ฆฌ์ผ ๋์์ธ์ด ์๋๋ก์ด๋์ ํตํฉ๋๋ค. ์ํฌ์ ์ข
์ด๋ฅผ ์ปจ์
์ผ๋ก ํ ๋ค์ํ UX ์ปจ์
์ด ๋์
๋๊ณ , ๊ทธ์ ๋ฐ๋ผ ๋ณํ๋ ๋ถ๋ถ๋ ๋ง์ด ์กด์ฌํ๋ค. appcompat
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๊ธฐ์กด ์๋๋ก์ด๋ ๋ฒ์ ์์๋ ๋จธํฐ๋ฆฌ์ผ ๋์์ธ์ ๋ถ๋ถ์ ์ผ๋ก ์ ์ฉํ ์ ์๊ฒ ๋๋ค.
๊ทธ๋ฌ๋ ์ ๋๋ฉ์ด์ ์ ๋กค๋ฆฌํ ๋๋ฐ์ด์ค์์๋ง ๊ฐ๋ฅํ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ๋กค๋ฆฌํ ๋ฒ์ ์ ๋ ๋๋ง ์ค๋ ๋๊ฐ ์ถ๊ฐ๋๋ค. ๋ ๋๋ง ์ค๋ ๋๋ ๋ฉ์ธ ์ค๋ ๋(ํน์ UI ์ค๋ ๋)์ ๋ถ๋ฆฌ๋ ๋ณ๋์ ์ค๋ ๋์์ ์ ๋๋ฉ์ด์ ์ ๋ค๋ฃฌ๋ค. ๋ณ๋์ ์ค๋ ๋๋ฅผ ์ฐ๋ ์ ๋๋ฉ์ด์ ์ ์พ์ ํ ๋ฐ๋ฉด์ ์ธ๋ฐํ ์กฐ์์ด ์ด๋ ต๋ค๋ ๋จ์ ์ด ์๋ค. ๋กค๋ฆฌํ์์ ์ถ๊ฐ๋ ์ ๋๋ฉ์ด์ ๋ค๋ ๋๋ถ๋ถ ์๋ ์ค์ ์กฐ์์ด ์ด๋ ค์ด ์์ท ์ ๋๋ฉ์ด์ ์ด๋ค. ์ด๋ ๊ฒ ๋กค๋ฆฌํ์ ์ถ๊ฐ๋ ์ ๋๋ฉ์ด์ ๋ค์ ๋ ๋๋ง ์ค๋ ๋์ ์์กด์ ์ธ ๊ฒฝ์ฐ๊ฐ ๋ง์ ๊ตฌํ ๋จ๋ง์ ์ํ ๋ฐฑ ํฌํ ์ด ์ด๋ ต๋ค.
๋จธํฐ๋ฆฌ์ผ ๋์์ธ์ ์ ์ฉํ ๋๋ ์๊ฐ์ ์ธ ๋ถ๋ถ๊ณผ ์ ๋๋ฉ์ด์ ๋ถ๋ถ์ ๋ถ๋ฆฌํด ์๋๋ก์ด๋ ๋ฒ์ ๋ณ๋ก ์ด๋ป๊ฒ ๋์ํ ๊ฒ์ธ์ง ๊ณ ๋ฏผํ ํ์๊ฐ ์๋ค.
๋ค์ํ ์๋๋ก์ด๋ ๋ฒ์ ์์ ๋จธํฐ๋ฆฌ์ผ ๋์์ธ์ ์ฌ์ฉํ ์ ์๋๋ก Theme.AppCompat
๋ฅผ ํ์ฅํ ๊ฒฝ์ฐ colorPrimary
, colorPrimaryDark
, colorAccent
๋ฑ์ ์์์ ์ค์ ํด์ผ ํ๋ค. Theme.AppCompat
๋ฅผ ์ํ ์์ ์ค์ ์๋ android:
์ ๋์ด๊ฐ ๋ถ์ง ์๋๋ค.
<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
<item name="colorPrimary">@color/material_blue_500</item>
<item name="colorPrimaryDark">@color/material_blue_700</item>
<item name="colorAccent">@color/material_green_A200</item>
</style>
<๋ฆฌ์คํธ 22> ๋ค์ํ ์๋๋ก์ด๋ ๋ฒ์ ์ ์ํ ํ ๋ง Theme.AppCompat์ ํ์ฅ
<๊ทธ๋ฆผ 7> ํด๋ฐ. ์ก์ ๋ฐ๋ฅผ ๋์ฒดํ๋ ์ ์ฐํ ์ฑ ๋ฐ ์ปดํฌ๋ํธ
์๋๋ก์ด๋ ํ๋์ฝค(3.0)๋ถํฐ ์ ์ฉ๋๋ ์ก์ ๋ฐ๊ฐ ํ๊ธฐ๋๋ค. ์ก์ ๋ฐ๋ ๊ตฌ๊ธ์ด ํ๋์ฝค ์ดํ ์ ์ฐฉ์ํค๋ ค๊ณ ํ๋ ๊ฐ์ด๋๋ผ์ธ์ ํต์ฌ์ด์๋ค. ๊ทธ ๋๋ฌธ์ ๊ฐ์ด๋๋ผ์ธ์ ๊ฐ์ ํ๊ธฐ ์ํด ์ปค์คํฐ๋ง์ด์ง์ด ์ด๋ ต๊ฒ ๋ผ ์์๋ค. ์ก์ ๋ฐ๊ฐ ํ๊ธฐ๋๊ณ ํด๋ฐ๊ฐ ๋ค์ด์จ ๊ฒ์ ๋จธํฐ๋ฆฌ์ผ ๋์์ธ์์ ์กฐ๊ธ ๋ ๋ค์ํ ์๋๋ฅผ ํ ์ ์๋๋ก ๋ฌธ์ ์ด์ด์ค ๊ฒ์ผ๋ก ๋ณผ ์ ์๋ค.
ํด๋ฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋จผ์ appcompat
๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํํจ๋ผ์ผ ํ๋ค.
dependencies {
compile "com.android.support:appcompat-v7:21.0.3"
}
<๋ฆฌ์คํธ 23> appcompat ๋ผ์ด๋ธ๋ฌ์ ์์กด์ฑ ์ถ๊ฐ
ํด๋ฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋จผ์ ์กํฐ๋นํฐ๋ ActionBarActivity
๋ฅผ ์์๋ฐ๊ณ ํ
๋ง๋ Theme.AppCompat
๋ฅผ ์์๋ฐ์์ผ ํ๋ค.
ActionBarActivity
์ ๋ ์ด์์์ Toolbar
๋ฅผ ์ถ๊ฐํ๋ค.
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
<๋ฆฌ์คํธ 24> ์ก์ ๋ฐ์ ๋ฌ๋ฆฌ ๋ ์ด์์ ์์์ ํ๋์ธ ํด๋ฐ
๋ ์ด์์์ Toolbar
๋ฅผ ์ถ๊ฐํ ํ ์กํฐ๋นํฐ์ ์ฐ๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๊ฐ ์๋ค.
- ์ก์ ๋ฐ์ฒ๋ผ ์ฐ๊ฒฐํ๊ธฐ
- ๊ทธ๋ฅ ์ฐ๊ฒฐํ๊ธฐ
์ก์
๋ฐ์ฒ๋ผ ํด๋ฐ๋ฅผ ์ฐ๊ฒฐํ ๋๋ setSupportActionBar
๋ฉ์๋๋ฅผ ์ด์ฉํ๋ค.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.microsoftware_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
<๋ฆฌ์คํธ 25> setSupportActionBar
๋ฅผ ์ด์ฉํ ํด๋ฐ์ ์ฌ์ฉ
ํด๋ฐ๋ฅผ ํ์ฉํ๋ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ํด๋ฐ์ setOnMenuItemClickListener
์ inflateMenu
๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด๋ค.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.microsoftware_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
// Set an OnMenuItemClickListener to handle menu item clicks
toolbar.setOnMenuItemClickListener(
new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// Handle the menu item
return true;
}
});
// Inflate a menu to be displayed in the toolbar
toolbar.inflateMenu(R.menu.microsoftware_menu);
}
<๋ฆฌ์คํธ 26> ์ก์ ๋ฐ์ ์๊ด์๋ ๋ ์์ ์ธ ํด๋ฐ์ ์ฌ์ฉ
<๊ทธ๋ฆผ 8> ๋จธํฐ๋ฆฌ์ผ ๋์์ธ ๋ค๋น๊ฒ์ด์ ๋๋ก์ด
๋กค๋ฆฌํ์ ๋ค๋น๊ฒ์ด์
๋๋ก์ด๋ ํ๋ฉด ์ ์ฒด๋ฅผ ๊ฐ๋ฆฌ๋ ํํ๋ค. ์ด๋ ๊ฒ ๋๋ก์ด๊ฐ ํ๋ฉด ์ ์ฒด๋ฅผ ๊ฐ๋ฆฌ๊ธฐ ์ํด์๋ <๋ฆฌ์คํธ 27>๊ณผ ๊ฐ์ด ๋ ์ด์์์์ DrawLayout
์ด ํฌํจ๋๋๋ก ๋ฐ๊ฟ์ผ ํ๋ค.
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
<!-- ์ดํ๋ฆฌ์ผ์ด์
UI -->
</LinearLayout>
<View
android:id="@+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#3F51B5"
android:fitsSystemWindows="true" />
</android.support.v4.widget.DrawerLayout>
<๋ฆฌ์คํธ 27> ๋จธํฐ๋ฆฌ์ผ ๋์์ธ์ ๋ง์ถฐ ๋ณ๊ฒฝ๋ ๋๋ก์ด ๋ ์ด์์
DrawerLayout
์ ๋ฃจํธ ๋ ์ด์์์ผ๋ก ๋ณ๊ฒฝํ๊ณ Toolbar
์ ์ดํ๋ฆฌ์ผ์ด์
UI๋ฅผ ์์ ๋ดํฌํ๋ ํํ๋ก ๋ ์ด์์์ ๋ณ๊ฒฝํ๋ค.
๋กค๋ฆฌํ์์ ๋ณํ๋ ๋ง์ ๋ถ๋ถ์ ํ ์๋ฆฌ์์ ์ค๋ช ํ๋ค๋ ๊ฒ์ ๋งค์ฐ ์ด๋ ค์ด ์ผ์ด๋ค. ๋ ์ด ๊ธ์์ ์ค๋ช ํ์ง ์์ ๋ณํ๋ค์ด ์์ผ๋ก์ ์๋๋ก์ด๋ ๊ฐ๋ฐ์ ๋ง์ ์ํฅ์ ๋ผ์น ๊ฒ์ด๋ค. ๊ฐ๋ฐ์ ์์ด ์ฅ์ ๊ฐ ์์ ์ ์๊ฒ ์ง๋ง, ๋ค๋ฅธ ์ธก๋ฉด์์ ๋ณธ๋ค๋ฉด ์๋ก์ด ๊ธฐ๋ฅ๊ณผ ๋์์ธ์ ์ํ ์ฌ๋ฌ ๊ฐ์ง ๊ธ์ ์ ์ธ ๋ณํ๋ ๊ธฐ๋ํด ๋ณผ ์ ์๋ค. ์๋๋ก์ด๋ ๋กค๋ฆฌํ์ ๋ํ ๋ค์ํ ๊ฒฝํ์ ์๋ก ๊ณต์ ํ๋ฉฐ ์กฐ๊ธ ๋ ๋ฌ์ฝคํ ์๋๋ก์ด๋๋ฅผ ๋ง๋๋ดค์ผ๋ฉด ํ๋ ๋ฐ๋์ด๋ค. ๋ค์์ ๋กค๋ฆฌํ์ ๋ํ ์ ๋ณด๊ฐ ๊ณต์ ๋๋ ์ฌ์ดํธ๋ค์ด๋ค.
- GDG ์ฝ๋ฆฌ์ ์๋๋ก์ด๋ - https://plus.google.com/communities/100903743067544956282
- ๊ตฌ๊ธ ๊ฐ๋ฐ์ ์ฝ๋ฆฌ์ ๋ธ๋ก๊ทธ - http://googledevkr.blogspot.kr/
- ํ์ด์ค๋ถ ์ปค๋ฎค๋ํฐ ์๋ก์ด๋ ํํํ - https://www.facebook.com/groups/junsle