LoginSignup
6
6

More than 5 years have passed since last update.

inline funの効果を検証する

Posted at

汎用ロガーを作ったが、リリース時にはバイナリから消え去ってほしいと思い、Inline Functionsを使ってみた。

実際には、そういった運用をするならProguardで消せばいいと思う。

Inline Functions

inline funを呼ぶとバイナリ上に組み込まれるようになる。C言語とかのマクロみたいな感じで使えるはず。

対象

Logger.kt
class Logger(val tag: String) {
    inline fun println(priority: Int, msgCreator: () -> String, e: Throwable? = null) {
        if (BuildConfig.DEBUG) {
            // BuildConfig.DEBUGはconst扱いなので、inlineであればリリース時にはクラスファイルからも消えるはず
            val msg = msgCreator() + (e?.let { "\n" + Log.getStackTraceString(it) } ?: "")
            Log.println(priority, tag, msg)
        }
    }

    inline fun v(msgCreator: () -> String) = println(Log.VERBOSE, msgCreator)
    inline fun d(msgCreator: () -> String) = println(Log.DEBUG, msgCreator)
    inline fun i(msgCreator: () -> String) = println(Log.INFO, msgCreator)
    inline fun w(msgCreator: () -> String) = println(Log.WARN, msgCreator)
    inline fun e(msgCreator: () -> String) = println(Log.ERROR, msgCreator)
    inline fun v(msgCreator: () -> String, e: Throwable) = println(Log.VERBOSE, msgCreator, e)
    inline fun d(msgCreator: () -> String, e: Throwable) = println(Log.DEBUG, msgCreator, e)
    inline fun i(msgCreator: () -> String, e: Throwable) = println(Log.INFO, msgCreator, e)
    inline fun w(msgCreator: () -> String, e: Throwable) = println(Log.WARN, msgCreator, e)
    inline fun e(msgCreator: () -> String, e: Throwable) = println(Log.ERROR, msgCreator, e)
}
MyApplication.kt
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Logger("foo").v { "bar" }
    }
}

方法

出力されたapkをデコンパイルし、jadコマンドで吐かれたコードを比較する。

結果

MyApplication.debug.jad
public final class MyApplication extends Application
{

    public MyApplication()
    {
    }

    public void onCreate()
    {
        Object obj;
        Logger logger;
        super.onCreate();
        logger = new Logger("foo");
        if(false)
            throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: println");
        obj = (Throwable)null;
        if(!BuildConfig.DEBUG) goto _L2; else goto _L1
_L1:
        StringBuilder stringbuilder = (new StringBuilder()).append((String)"bar");
        if(obj == null) goto _L4; else goto _L3
_L3:
        obj = (Throwable)obj;
        obj = (String)(new StringBuilder()).append("\n").append(Log.getStackTraceString(((Throwable) (obj)))).toString();
        if(obj == null) goto _L4; else goto _L5
_L5:
        obj = stringbuilder.append(obj).toString();
        Log.println(2, logger.getTag(), ((String) (obj)));
_L2:
        return;
_L4:
        obj = "";
        if(true) goto _L5; else goto _L6
_L6:
    }
}
MyApplication.release.jad
public final class MyApplication extends Application
{

    public MyApplication()
    {
    }

    public void onCreate()
    {
        super.onCreate();
        new a("foo"); // 注:Logger
        if(false)
        {
            throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: println");
        } else
        {
            Throwable throwable = (Throwable)null;
            return;
        }
    }
}

という感じで、リリース時にログ内容がごっそり消せた。

UnsupportedOperationExceptionはKotlinによるものだと思うが、詳細は把握しきれていない。

ちなみに、app/buildに出力されるProguard前のバイトコードは大差がなかったので、Proguardによる最適化効果と思われる。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6