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와 큰 차이는 없게 되어 매우 좋은 기능이라 생각된다.

 

Kotlin – Functions in Java

Functions in Java

어느 날, 코틀린으로 구성된 유틸 클래스에 아래와 같은 메소드를 작성했다.

/**
 * run code inside of Background Thread after given delay
 *
 * @param[delayMillis] delay in ms
 * @param[action] code to execute
 */
fun runDelayed(delayMillis: Long, action: () -> Unit) = Handler().postDelayed(Runnable(action), delayMillis)

이걸 코틀린 액티비티에서 사용할려고 할때, 어떻게 하면 될까.

간단하다.

runDelayed(delayMillis = 1000, action = {
            
})

그럼.. 자바에서는 어떻게 해야할까. 내키는대로 써보자.

Function0<T> 라는 제너릭 클래스인 것 같다.

Utils.runDelayed(1000, new Function0<Unit>() {
            @Override
            public Unit invoke() {
                getLastestRelease();
                return null;
            }
 });

즉, 이렇게 된다. 안에 행동을 넣고 돌리면? 강제종료된다. 코틀린 코드에서 action 변수가 Nullable가 아닌데 null을 넘기니 NullPointerException가 발생하는 것이다.

해결방법은 Unit 클래스를 넘겨주면 된다.

Utils.runDelayed(1000, new Function0<Unit>() {
            @Override
            public Unit invoke() {
                getLastestRelease();
                return Unit.INSTANCE;
            }
});

참고로 유닛 클래스는 아래와 같다.

package kotlin

/**
 * The type with only one value: the Unit object. This type corresponds to the `void` type in Java.
 */
public object Unit {
    override fun toString() = "kotlin.Unit"
}

자바의 Void에 해당한다고 작성되어있다.

제법 간단한 해결방법 이었지만, 처음에 적잖이 당황한것도 사실이다;-;