Java 8 기능을 Android 에서 써봅시다.

지난 주말에 하던 일의 1차 마무리가 되어서 간만에 시간이 난 김에 평소 궁금하던 내용을 조사해보았습니다.....

.....라고 시작했던 2016년 4월 11일에 포스팅했던 글이 6월 29일, 두 달 반 정도 지나고 나서 업데이트한 이후 2017년 4월 17일에 또 다시 포스팅과 예제앱의 업데이트를 하게 되었습니다.

이 글을 보시면서 다음 글도 같이 참고하시면 최신 Java 8 지원사항을 정리하실 수 있습니다.

(Android 용 배경화면 공유 SNS 앱인 Fastground 를 만들고 있습니다. 한 번씩 들러봐주세요. ^^)

SDK 24 버전과 Build-tool 24 버전이 정식출시가 되었습니다.
이제 Android 에서도 Java 8 의 일부 기능이 정식 지원되는 셈인데요.

이 글에서는 Android Studio 2.2 alpha 4 버전으로 진행해보겠습니다.

(Google 공식 기술블로그Google 공식 문서의 내용을 기반으로 해서 제가 직접 테스트해본 내용을 정리했습니다.)

Jack 컴파일러를 통하면 Android 에서 Java 8 의 일부 기능을 써먹을 수 있는데요. 주요하게 살펴볼 Java 8 의 일부 기능은 다음과 같습니다.

  • Lambdas
  • Method Reference
  • Interface’s Static Method
  • Interface’s Default Method
  • Function / Stream Package Support

하지만, 이들 기능들은 전부 같은 설정에서 개발할 수는 없습니다.
그래서, 어떤 기능은 어떤 설정에서 개발가능한지를 테스트해보았습니다.

테스트해본 예제는 https://github.com/mindwing/Java8_for_Android 에 올려두었고요.

다음 소스코드는 github 에 올라가 있는 테스트 예제의 소스인데, 이걸 따로 띄워놓고 보시면 이해하기 쉽습니다.

이제부터는 Android Studio 2.2 에서 테스트 예제 프로젝트를 설정해둔 시점으로 가정하고 설명하겠습니다.

우선, Android Gradle 플러그인의 버전은 2.2 로 맞춰줘야 합니다.
이를 위해 Project 레벨 build.gradle 파일에 다음과 같이 되어 있는지 확인합니다. (현재 최신 버전은 alpha 4 인데, 이 글을 쓰는 시점의 Android Studio 2.2 preview 4 의 버전이 업그레이드될 때마다 새로운 버전이 나올 예정입니다.)

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0-alpha4'
...

그리고, app 레벨 build.gradle 에서 build tool 버전도 24 로 맞춰주고요.
Jack & Jill 툴체인도 활성화하고, Java Source Version 도 1.8 로 맞춥니다.
그리고, 먼저 낮은 API Level 에서 써볼 수 있는 기능부터 테스트해볼 것이니 minSdkVersion 과 targetSdkVersion 은 9 로 맞춰봅니다.

(참고로, github 상의 테스트예제는 24 로 설정되어 있습니다. Android Nougat 단말이 있거나 에뮬레이터에서 전체 테스트 예제를 실행하실 분들은 그냥 24로 고정해서 진행하셔도 됩니다.)

apply plugin: 'com.android.application'

android {
compileSdkVersion 24
buildToolsVersion '24.0.0'

defaultConfig {
applicationId "kr.mindwing.java8_for_android"
minSdkVersion 9
targetSdkVersion 9
versionCode 1
versionName "1.0"
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

이제 다음 2가지 기능을 써볼 준비가 되었습니다.

  • Lambdas
  • Method Reference

예제코드를 다음과 같이 각각 수정해야 합니다.

[Lambdas 테스트]

Runnable callMe = new LambdaTest();
// Runnable callMe = new MethodReferenceTest();
// Runnable callMe = new InterfaceStaticMethodTest();
// Runnable callMe = new DefaultMethodTest();
// Runnable callMe = new FunctionStreamPackageTest();

[Method Reference 테스트]

// Runnable callMe = new LambdaTest();
Runnable callMe = new MethodReferenceTest();
// Runnable callMe = new InterfaceStaticMethodTest();
// Runnable callMe = new DefaultMethodTest();
// Runnable callMe = new FunctionStreamPackageTest();

그리고, 현재 사용하지 않는 DefaultMethodInterface.java 의 default method 도 주석처리해주세요. (minSdkVersion 을 24 로 맞추셨다면 주석처리하지 않아도 됩니다.)

이제 실행이 되었을텐데요.
컴파일된 클래스를 디컴파일해보면 Inner Class 가 생성되었다는 사실을 확인할 수 있습니다.

디컴파일해본 코드

MainActivity.this.btButton.setOnClickListener(
new -void_run__LambdaImpl0());

(하이픈으로 시작하는 특이한 클래스이름이네요.)

이 2가지 기능들은 API Level 9 에서도 써볼 수 있습니다. 아쉬운 대로 여기까지는 써보는데 큰 문제가 없네요.

이제 다른 기능들을 쓰기 위해서는 설정의 추가 변경이 필요합니다.

  • Interface’s Static Method
  • Interface’s Default Method
  • Function / Stream Package Support

minSdkVersion 과 targetSdkVersion 을 24 로 맞춰주시면 됩니다. (한때 개발버전일 때에는 일부 기능이 API Level 21에서도 동작했었는데 정식버전에서는 24 로 조정된 것 같습니다.)

apply plugin: 'com.android.application'android {
compileSdkVersion 24
buildToolsVersion '24.0.0'
defaultConfig {
applicationId "kr.mindwing.java8_for_android"
minSdkVersion 24
targetSdkVersion 24
versionCode 1
versionName "1.0"
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

이제 다음과 같이 하나씩 주석을 풀어서 실행해봅니다.

[Interface Static Method 테스트]

// Runnable callMe = new LambdaTest();
// Runnable callMe = new MethodReferenceTest();
Runnable callMe = new InterfaceStaticMethodTest();
// Runnable callMe = new DefaultMethodTest();
// Runnable callMe = new FunctionStreamPackageTest();

[Interface Default Method 테스트]

// Runnable callMe = new LambdaTest();
// Runnable callMe = new MethodReferenceTest();
// Runnable callMe = new InterfaceStaticMethodTest();
Runnable callMe = new DefaultMethodTest();
// Runnable callMe = new FunctionStreamPackageTest();

[Interface Static Method 테스트]

// Runnable callMe = new LambdaTest();
// Runnable callMe = new MethodReferenceTest();
// Runnable callMe = new InterfaceStaticMethodTest();
// Runnable callMe = new DefaultMethodTest();
Runnable callMe = new FunctionStreamPackageTest();

제 경우에는 Nexus 5X 에 Android Nougat (누텔라가 아니라니… OTL) DP4 테스트 빌드를 올려서 실행해보았고요. 에뮬레이터를 통해서도 실행가능합니다.

이렇게 Java 8 의 기능이 이제서야 이리도 늦은 시점에 그것도 일부 기능만을 지원하게 된 Android 개발환경에 대해서 알아보았습니다.

개인적으로는 Java 8 의 기능지원을 위해 Jack & Jill 툴체인이 몇 년 동안 개발되어 왔다는 점에서 완성버전에 대해 기대를 가지고는 있지만, 너무 늦은 감이 있습니다.

게다가 Lambdas 와 Method Reference 만 쓰는 개발환경이라면 Retrolambda 를 써도 충분합니다. 사실 Jack & Jill 툴체인은 아직 Instant Run 을 지원하지 않기 때문에 개발속도에 안좋은 영향을 미칠 수 있는데, Retrolambda 는 Instant Run 에 영향을 주지 않기 때문입니다.

게다가, Retrolambda 는 try-with-resource 기능도 훌륭하게 지원해주고 있지만, Android 에서 기본 지원되는 try-with-resource 는 API Level 19 (KitKat) 이상에서만 지원되는 단점이 있습니다.

그래서, 가볍게 쓰고자 할 경우에는 오히려 Retrolambda 를 쓰는 것이 더 효과적일 수도 있겠습니다.

하지만, 그 밖의 기능들을 쓰고자 한다면 API Level 24 이상만 지원한다는 핸디캡을 안고서 Jack & Jill 툴체인을 쓸 수 밖에 없습니다. (언젠가는 Android Nougat 가 안드로이드 세상을 지배하는 날이 오지 않을까요? 한 2년쯤 후에는 말이죠.)

개인적으로는 Java 8 에서 벗어나서 좀 더 모던한 프로그래밍을 할 수 있는 Kotlin 이 Jack & Jill 툴체인을 지원할 예정이기에 Kotlin 을 기대중입니다.

Java 8 이 실질적인 함수형 프로그래밍 능력을 탑재하기 보다는 이전 버전과의 호환성을 위해 기존 기반위에 함수형 코딩이 가능하도록 해서 성능상의 문제가 있는 반면, Kotlin 은 이런 제한으로부터 비교적 자유롭도록 디자인되었기 때문입니다.

그래서, 내년 Google I/O 2017 즈음에는 Kotlin 이 Android 의 1급 개발언어로 부상하길 기대하고 있습니다. (아마 빨라봐야 preview 버전 정도가 아닐까 싶긴 합니다. 물론, 아직 고쳐지지 않고 있는 많은 버그들도 좀 손을 봐야... 그럴거면 Scala 가 Android 공식 개발언어가 되면 참 좋겠지만 오덕스키 교수님은 그럴 생각이 전혀 없으신 것 같고... =_=)

일부 보도에 따르면 Swift 가 차기 Android 개발 언어로 유력하게 검토되고 있다고 나오고 있던데, 역시 개인적인 의견이지만 Kotlin 을 개선해서 탑재하는 작업에 비해 Swift 를 Android 스택에 끼워넣는 것은 상상하기도 힘든 엄청난 작업이 될 것이라 예상합니다.

우선, Android Framework 의 엄청난 Java 코드들을 어떻게 Swift 로 대체할 것인가가 문제가 되겠네요.
게다가 현재 엄청난 생태계를 이루고 있는 Java 라이브러리들은 또 어떻게 해야 할까요?

게다가 이제서야 Jack & Jill 툴체인이 안정화단계에 들어섰는데 이걸 포기하고 Swift 툴체인을 다시 만들어서 현재까지 해왔던 엄청난 코드를 재작성한다는 것은 도저히 상상조차 할 수가 없습니다.

(Android 는 C 와 C 와의 바인딩을 하는 Java 가 유일한 공식개발언어입니다. Swift 나 C#, Python 등 다른 개발언어로 앱을 개발하는 방식도 전부 C 와의 바인딩을 통한 개발방식이지만, 당연히 Java 로 제공되는 풍부한 Android Framework 기능과 Runtime API 를 활용하는데 제한이 있습니다.)

물론, Swift 가 지원된다면 iOS 개발자들을 끌어들일 수 있는 더할 나위없이 좋은 선택이 되겠지만, 기술적 난제가 쉽게 풀리지 않을 것 같기에 그 전에 Kotlin 이 먼저 Android 공식 개발 환경에 무난히 입성하게 되길 바라마지 않습니다.

그리고 당연히 그 전에 Java 8 의 기능을 포함하는 많은 라이브러리들이 Android 에 발을 들여놓게 되길 또한 바랍니다.

http://www.fastcampus.co.kr/dev_camp_adp/ >>> Fast campus 에서 Android 프로젝트 CAMP 를 진행하는 강사입니다. Java 는 알지만 Android 는 아직 모르시는 분들을 위해 상용 수준의 앱을 같이 만들어보는 과정입니다.

http://www.fastcampus.co.kr/dev_camp_adp/ >>> Fast campus 에서 Android 프로젝트 CAMP 를 진행하는 강사입니다. Java 는 알지만 Android 는 아직 모르시는 분들을 위해 상용 수준의 앱을 같이 만들어보는 과정입니다.