Android Studio 3.0 – Java 8 language feature

2017년 3월 15일, Android Studio 내부에서 Java 8 language feature을 활성화하기 위해 사용하던 Jack compiler 가 Deprecated 되었다.

처음엔 Jack toolchain 을 통해 Java 8 지원 추가를 테스트하였으나, 시간이 지남에 따라 Annotation processors(어노테이션 처리기), Bytecode analyzers and rewriters(바이트 코드 분석, 작성기) 들이 Jack로 전환하는 비용이 높아서 앞으로 Java 8 언어 기능들이 안드로이드 빌드 시스템에서 기본적으로 지원되게 할 예정이라고 한다.

물론 Jack 대신 Retrolambda를 쓰고 있었기에 문제 없었다. 그리고 안드로이드 스튜디오 3.0 Preview가 나오면서 위에 밝혀진 ‘Java 8 언어 기능에 대한 안드로이드 빌드 시스템 내부 지원’ 이 Desugar 란 이름으로 돌아왔다.

Desugar 구조

Desugar 의 구조는 아래와 같다. (출처: Android Developer, Use Java 8 language features)

desugar structure

 

.java 가 javac 를 통해 컴파일 되면 class가 나오는데, 이 class를 기반으로 desugar 작업을 통하여 나온 결과물을 dex 화 시키는 것이다.

즉 desugar를 말로 표현하자면 javac – dex 사이에 Java 8 언어 기능을 사용한 코드를 하위 호환에 맞게 변환시키는 과정이다.

물론 기존과 같이 SDK 버전(minSdkVersion)에 따라 작동 범위 차이는 있다.

Java 8 Language Feature, API

Java 8 언어 기능 / API호환되는 minSdkVersion
람다식모두 지원됨. 하지만 람다에 잡히는 모든 값이 serializable 해야함
메서드 참조모두 지원됨.
형식 주석모두 지원됨. 하지만 타입 어노테이션에 대한 정보는 컴파일 시간에 활성화된다. (런타임 상 정보는 나오지 않음) ElementType.TYPE_USE, ElementType.TYPE_PARAMETER 외의 TYPE들은 24 이상부터 사용 가능
기본, 정적 인터페이스모두 지원됨
반복 가능한 주석모두 지원됨
annotation.Repeatable24 이상
AnnotatedElement.getAnnotationByType(Class)24 이상
스트림24 이상
Functional Interface24 이상
Method.isDefault()24 이상
Function util24 이상

desugar 가 도입되면서 그레들 플러그인에도 변경점이 생겼는데, 바로 Retrolambda 와 Jack, DexGuard를 사용중일 경우 desugar를 도입하도록 경고를 표시하는 점이다.

warning of using desugar

아직까지는 위 3개를 사용중이면 위 3개가 우선적으로 작동하지만 좀 더 desugar가 안정화 되면 마이그레이션 할 예정이다.

결국은 Java8를 컴파일 옵션으로 두는 프로젝트면 기본적으로 활성화되게 되어, 별도 플러그인이 필요없어진 셈이다.

만일 비활성화 하고 싶다면 gradle.properties 파일에 android.enableDesugar=false 를 두면 되는거같다.

 

Android – setLocalOnly (Notification) 정리

setLocalOnly

NotificationCompat.Builder setLocalOnly (boolean b)

기본적으로 안드로이드의 알림은 원격 기기(블루투스 기기 등)에 전송될 수 있는데, 전송되지 않게 하는 메소드이다. 즉 로컬 안드로이드 기기에서만 표시하게 하는 것이다.

알림창에 상주할 필요가 있는 앱에서 많이 필요할 것 같다. 최근 스마트워치 같은게 빠르게 추세화 되기도 하고.

출처: Android Developer / NotificationCompat.Builder.html#setLocalOnly(boolean)

Kotlin – JvmName, JvmMultifileClass

 JvmName , JvmMultifileClass

기본적으로 코틀린은 JVM Class를 생성할 때 아래와 같은 명명규칙을 가지는 것 같다.

AlertUtils.kt -> AlertUtilsKt.java
RKeyHash.kt -> RKeyHashKt.java

생성할 때 이름을 변경해줄 수 있는데, @file:JvmName("Utils") 가 그 역할을 한다.

가령, 안드로이드 앱의 키스토어 해시를 꺼내는 유틸성 코드가 있다고 해보자.

@file:JvmName("Utils")

package pyxis.uzuki.live.richutilskt

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import java.security.MessageDigest

/**
 * get key hash of application
 *
 * it will help to integrate with Facebook, Kakao SDK
 *
 * @return key hash of application
 */
@SuppressLint("PackageManagerGetSignatures")
fun Context.getKeyHash() : String {
    val hashList:ArrayList<String> = ArrayList()
    try {
        val info = this.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
        for (signature in info.signatures) {
            val md = MessageDigest.getInstance("SHA")
            md.update(signature.toByteArray())
            hashList.add(String(Base64.encode(md.digest(), 0)))
        }

        return if (hashList.isNotEmpty()) hashList[0] else ""
    } catch (e: Exception) {
        Log.e("name not found", e.toString())
    }

    return ""
}

[원본 코드 보기, RichUtilsKt/RKeyHash.kt]

패키지 선언문 위에 적어주게 되면 위 명명규칙을 따르지 않고 Utils.java로 생성된다.

만약 유틸성 코드 파일이 수십개를 넘어간다면 한 개의 코틀린 파일로 통합하는 것이 아닌 @file:JvmMultifileClass 를 JvmName 밑에 작성해주면 된다.

결과적으로 자바에서 해당 유틸성 코드를 사용하려면, Utils.getKeyHash(Context) 로 사용하면 된다.

개인적으로 예전에 사용하던 static class였던 Utils.java와 큰 차이는 없게 되어 매우 좋은 기능이라 생각된다.