Android之代码混淆

Posted JoHany.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android之代码混淆相关的知识,希望对你有一定的参考价值。

应用混淆(ProGuard)

  • ProGuard是一个免费的JAVA类文件压缩,优化,混淆器。
  • 它探测并删除没有使用的类,字段,方法和属性,它删除没有用的说明并使用字节码得到最大优化,它使用无意义的名字重命名类,字段和方法。

我们先来介绍下ProGuard

我们为啥要使用ProGuard?

  • 优化应用:创建紧凑的代码文档是为了更快的网络传输,快速装载和更小的内存占用;
  • 防止反向:创建的程序和程序库很难使用反向工程;
  • 预处理应用:充分利用JAVA的快捷加载的优点来提前检测和返回JAVA中存在的类文件。

这是我开发中用到的一些混淆规则,大家可以根据需要复制到自己的项目中的混淆规则的文件中即可。

# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\\androidstudio\\sdk\\android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the javascript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}
#-keepattributes EnclosingMethod

#
#--------------------------基本不用动区域--------------------------
#
# -----------------------------基本 -----------------------------
#

# 指定代码的压缩级别 0 - 7(指定代码进行迭代优化的次数,在Android里面默认是5,这条指令也只有在可以优化时起作用。)
-optimizationpasses 5
# 混淆时不会产生形形色色的类名(混淆时不使用大小写混合类名)
-dontusemixedcaseclassnames
# 指定不去忽略非公共的库类(不跳过library中的非public的类)
-dontskipnonpubliclibraryclasses
# 指定不去忽略包可见的库类的成员
-dontskipnonpubliclibraryclassmembers
#不进行优化,建议使用此选项,
-dontoptimize
 # 不进行预校验,Android不需要,可加快混淆速度。
-dontpreverify
# 屏蔽警告
-ignorewarnings
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !retCode/simplification/arithmetic,!field/*,!class/merging/*
# 保护代码中的Annotation不被混淆
-keepattributes *Annotation*
# 避免混淆泛型, 这在JSON实体映射时非常重要
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
 #优化时允许访问并修改有修饰符的类和类的成员,这可以提高优化步骤的结果。
# 比如,当内联一个公共的getter方法时,这也可能需要外地公共访问。
# 虽然java二进制规范不需要这个,要不然有的虚拟机处理这些代码会有问题。当有优化和使用-repackageclasses时才适用。
#指示语:不能用这个指令处理库中的代码,因为有的类和类成员没有设计成public ,而在api中可能变成public
-allowaccessmodification
#当有优化和使用-repackageclasses时才适用。
-repackageclasses ''
 # 混淆时记录日志(打印混淆的详细信息)
 # 这句话能够使我们的项目混淆后产生映射文件
 # 包含有类名->混淆后类名的映射关系
-verbose

#
# ----------------------------- 默认保留 -------------------------
#

# 保持哪些类不被混淆
#继承activity,application,service,broadcastReceiver,contentprovider....不进行混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View

# AndroidX的混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**

# javax.annotation

#表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致
-keepclasseswithmembernames class * {
    native <methods>;
}

#这个主要是在layout 中写的onclick方法android:onclick="onClick",不进行混淆
#表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,
#当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}

#表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

#表示不混淆任何一个View中的setXxx()和getXxx()方法,
#因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

#表示不混淆Parcelable实现类中的CREATOR字段,
#毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
# 这指定了继承Serizalizable的类的如下成员不被移除混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}
# 保留R下面的资源
-keep class **.R$* {
 *;
}
#不混淆资源类下static的
-keepclassmembers class **.R$* {
    public static <fields>;
}

# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
    void *(**On*Event);
    void *(**On*Listener);
}

# 保留我们自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}


#
#-----混淆保护自己项目的部分代码以及引用的第三方jar包 -------------------
#
#-libraryjars libs/source.aar
#-libraryjars libs/tbs_sdk_thirdapp_v4.3.0.67_43967_sharewithdownloadwithfile_withoutGame_obfs_20200923_120452.jar
#-libraryjars libs/com.heytap.msp-push-2.1.0.aar
#-libraryjars libs/jcore-android-2.7.6.jar
#-libraryjars libs/jpush-android-4.0.6.jar
#-libraryjars libs/jpush-android-plugin-huawei-v4.0.6.jar
#-libraryjars libs/jpush-android-plugin-meizu-v3.7.0.jar
#-libraryjars libs/jpush-android-plugin-oppo-v4.0.6.jar
#-libraryjars libs/jpush-android-plugin-vivo-v4.0.6.jar
#-libraryjars libs/jpush-android-plugin-xiaomi-v4.0.6.jar
#-libraryjars libs/meizu-push-3.9.0.jar
#-libraryjars libs/MiPush_SDK_Client_3_8_5.jar
#-libraryjars libs/push_sdk_v3.0.0.jar
#-libraryjars libs/LiteAVSDK_Professional_8.4.9947.aar
#-libraryjars libs/alipaysdk-15.8.02.210308182128.aar
#-libraryjars libs/umeng-analytics-8.1.2.jar
#-libraryjars libs/umeng-common-2.1.0.jar
#-libraryjars libs/umeng-share-core-6.9.6.jar
#-libraryjars libs/umeng-share-QQ-simplify-6.9.6.jar
#-libraryjars libs/umeng-sharetool-6.9.6.jar
#-libraryjars libs/umeng-share-wechat-simplify-6.9.6.jar
#-libraryjars libs/wechat-sdk-android-with-mta-1.0.2.jar
#
#--------------------- 腾讯X5 WebView -------------------
#
-dontwarn dalvik.**
-dontwarn com.tencent.smtt.**

-keep class com.tencent.smtt.** {
    *;
}

-keep class com.tencent.tbs.** {
    *;
}

#webView需要进行特殊处理

-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, jav.lang.String);
}
#在app中与HTML5的JavaScript的交互进行特殊处理
#我们需要确保这些js要调用的原生方法不能够被混淆,于是我们需要做如下处理:
-keepclassmembers class com.centit.theatre.dsBridge.JsApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsAudioApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsDeviceApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsEchoApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsNavigationApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsNotificationApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsPayApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsPermissionApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsPushApi {
    <methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsUtilApi {
    <methods>;
}

#
#---------------------------------实体类------------------------------
#--------(实体Model不能混淆,否则找不到对应的属性获取不到值)----------------
#
-dontwarn centit.theatre.model.**
#对含有反射类的处理
-keep class centit.theatre.model.** { *; }

#
# ----------------------------- 其他的 -----------------------------
#
# 删除代码中Log相关的代码
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

# 保持测试相关的代码
#-dontnote junit.framework.**
#-dontnote junit.runner.**
#-dontwarn android.test.**
#-dontwarn android.support.test.**
#-dontwarn org.junit.**

#
# ----------------------------- 第三方 -----------------------------
#

# FastJson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.**{*; }

# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions

#-------------- okhttp3 -------------
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.{*;}
-keep interface com.squareup.okhttp.** { *; }

-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}

-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**

# Okio
-dontwarn com.squareup.**
-dontwarn okio.**
-keep public class org.codehaus.* { *; }
-keep public class java.nio.* { *; }
#----------okhttp end--------------

# butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
   @butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
   @butterknife.* <methods>;
}

#Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

#eventbus
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
    long producerIndex;
    long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
 # rxandroid-1.2.1
 -keepclassmembers class rx.android.**{*;}

# 路由框架 ARouter
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
-keep class * implements com.alibaba.android.arouter.facade.template.IProvider

# 报错后添加
-keep class * implements com.alibaba.android.arouter.facade.model.RouteMeta
-dontwarn com.alibaba.android.arouter.facade.model.RouteMeta

# autosize适配方案
-keep class me.jessyan.autosize.** { *; }
-keep interface me.jessyan.autosize.** { *; }

# litepal数据库框架
-keep class org.litepal.** {*;}
-keep class * extends org.litepal.crud.DataSupport {*;}
-keep class * extends org.litepal.crud.LitePalSupport {*;}

#jpush极光推送
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }

# AndroidCodeUtils
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**

# Bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}

# Addidional for x5.sdk classes for apps
-keep class com.tencent.smtt.export.external.**{*;}
-keep class com.tencent.tbs.video.interfaces.IUserStateChangedListener {*;}
-keep class com.tencent.smtt.sdk.CacheManager {public *;}
-keep class com.tencent.smtt.sdk.CookieManager {public *;}
-keep class com.tencent.smtt.sdk.WebHistoryItem {public *;}
-keep class com.tencent.smtt.sdk.WebViewDatabase {public *;}
-keep class com.tencent.smtt.sdk.WebBackForwardList {public *;}
-keep public class com.tencent.smtt.sdk.WebView {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebView$HitTestResult {
	public static final <fields>;
	public java.lang.String getExtra();
	public int getType();
}

-keep public class com.tencent.smtt.sdk.WebView$WebViewTransport {
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebView$PictureListener {
	public <fields>;
	public <methods>;
}

-keepattributes InnerClasses

-keep public enum com.tencent.smtt.sdk.WebSettings$** {*;}

-keep public enum com.tencent.smtt.sdk.QbSdk$** {*;}

-keep public class com.tencent.smtt.sdk.WebSettings {
    public *;
}

-keep public class com.tencent.smtt.sdk.ValueCallback {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebViewClient {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.DownloadListener {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebChromeClient {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebChromeClient$FileChooserParams {
	public <fields>;
	public <methods>;
}

-keep class com.tencent.smtt.sdk.SystemWebChromeClient{
	public *;
}
# 1. extension interfaces should be apparent
-keep public class com.tencent.smtt.export.external.extension.interfaces.* {
	public protected *;
}

# 2. interfaces should be apparent
-keep public class com.tencent.smtt.export.external.interfaces.* {
	public protected *;
}

-keep public class com.tencent.smtt.sdk.WebViewCallbackClient {
	public protected *;
}

-keep public class com.tencent.smtt.sdk.WebStorage$QuotaUpdater {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebIconDatabase {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.WebStorage {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.DownloadListener {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.QbSdk {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.QbSdk$PreInitCallback {
	public <fields>;
	public <methods>;
}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.Tbs* {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.utils.LogFileUtils {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.utils.TbsLog {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.utils.TbsLogClient {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.CookieSyncManager {
	public <fields>;
	public <methods>;
}

# Added for game demos
-keep public class com.tencent.smtt.sdk.TBSGamePlayer {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.TBSGamePlayerClient* {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.TBSGamePlayerClientExtension {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.sdk.TBSGamePlayerService* {
	public <fields>;
	public <methods>;
}

-keep public class com.tencent.smtt.utils.Apn {
	public <fields>;
	public <methods>;
}
-keep class com.tencent.smtt.** {*;}
# end

# TakePhoto,拍照、相册、裁剪
-keep class com.jph.takephoto.** { *; }
-dontwarn com.jph.takephoto.**

-keep class com.darsh.multipleimageselect.** { *; }
-dontwarn com.darsh.multipleimageselect.**

-keep class com.soundcloud.android.crop.** { *; }
-dontwarn com.soundcloud.android.crop.**


# Blankj工具类
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**

# 微信过滤出去
-dontwarn com.tencent.**
-keep class com.tencent.** { *; }

# BaseRecyclerViewAdapterHelper
-keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembers  class **$** extends com.chad.library.adapter.base.BaseViewHolder {
     <init>(...);
}

# banner 的混淆代码
-keep class com.youth.banner.** {
    *;
 }

 #leakcanary
 -dontwarn com.squareup.haha.guava.**
 -dontwarn com.squareup.haha.perflib.**
 -dontwarn com.squareup.haha.trove.**
 -dontwarn com.squareup.leakcanary.**
 -keep class com.squareup.haha.** { *; }
 -keep class com.squareup.leakcanary.** { *; }

 # Retrolambda
 -dontwarn java.lang.invoke.*

 # Gson
 -keep class com.google.gson.stream.** { *; }
 -keepattributes EnclosingMethod
 -keep class org.xz_sale.entity.**{*;}
 -keep class com.google.gson.** {*;}
 -keep class com.google.**{*;}
 -keep class sun.misc.Unsafe { *; }
 -keep class com.google.gson.stream.** { *; }
 -keep class com.google.gson.examples.android.model.** { *; }

  # Android10标识
 -keep class com.bun.miitmdid.core.** {*;}

# 找不到类
-dontwarn com.trello.rxlifecycle2.LifecycleProvider
-dontwarn com.trello.rxlifecycle2.LifecycleTransformer
-dontwarn com.trello.rxlifecycle2.OutsideLifecycleException
-dontwarn com.trello.rxlifecycle2.RxLifecycle

# 微信
-keep class com.tencent.mm.opensdk.** {
    *;
}
-keep class com.tencent.wxop.** {
    *;
}
-keep class com.tencent.mm.sdk.** {
    *;
}

#PictureSelector 2.0
-keep class com.luck.picture.lib.** { *; }

#Ucrop
-dontwarn com.yalantis.ucrop**
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }

# kongzue弹出框
-keep class com.kongzue.dialog.** { *; }
-dontwarn com.kongzue.dialog.**
# AndroidX版本请使用如下配置:
-dontwarn androidx.renderscript.**
-keep public class androidx.renderscript.** { *; }

# 乐播Lebo
-keep class com.hpplay.**{*;}
-keep class com.hpplay.**$*{*;}
-dontwarn com.hpplay.**

# 极光推送
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep public class com.sina.** {
    *;
}

# mp4parser相关
-dontwarn com.googlecode.mp4parser.**
-keep public class com.googlecode.mp4parser.** {*;}
-keep public class org.googlecode.mp4parser.** {*;}
-keep public class com.coremedia.** {*;}
-keep public class com.googlecode.** {*;}

# permissions权限工具
-dontwarn com.hjq.permissions.**
-keep class com.hjq.permissions.** {*;}

当设置完毕混淆规则后,紧接着在build.gradle配置如下:

            // 移除无用的资源文件
            shrinkResources true
            // ZipAlign 优化
            zipAlignEnabled true
            // 设置混淆
            minifyEnabled true

            // 混淆配置
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-app.pro'

把属性设置为tue就大功告成啦,接着就可以打包啦。


打完包后我们把apk文件后缀名改成zip,然后解压,找到classes.dex文件。如图:

若想要阅读该文件的内容,可以使用 dex2jar 和 jd-gui 工具。下面介绍这两种工具的安装及使用方法。


一、安装 dex2jar


二、使用 dex2jar

  • 1. 将需要反编译的dex文件(这里是classes.dex)复制到 dex2jar 解压目录下。
  • 2. 打开命令行进入 d2j-dex2jar.bat 文件所在目录,输入命令 d2j-dex2jar.bat classes.dex 。
  • 运行此命令需要安装JDK环境变量哦,否则提示不是内部命令。
  • 此时可以看到目录中多出了classes-dex2jar.jar文件。
  • 那么如何查看该jar文件的内容呢?此时就需要安装 jd-gui 。

三、安装 jd-gui 


四、使用 jd-gui  

双击运行 jd-gui.exe 文件,将jar文件拖到工作区即可打开。
 

打开后就可以看混淆后的文件啦~~~

以上是关于Android之代码混淆的主要内容,如果未能解决你的问题,请参考以下文章

Android代码混淆之部分类不混淆的技巧

Android 代码混淆之部分类不混淆的技巧

Android 代码混淆之proguard

Android之代码混淆

append() 在这个代码片段中是如何工作的?与特定变量混淆[重复]

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情