René's URL Explorer Experiment


Title: Android 混淆使用手册 · Issue #13 · HankCoder/BlogAndCode · GitHub

Open Graph Title: Android 混淆使用手册 · Issue #13 · HankCoder/BlogAndCode

X Title: Android 混淆使用手册 · Issue #13 · HankCoder/BlogAndCode

Description: 转-学习使用,自己逐步增加 综述 毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。 首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工程的难度。而依靠 Gradle 的 Android 插件,我们将移除未被使用的资...

Open Graph Description: 转-学习使用,自己逐步增加 综述 毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。 首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工...

X Description: 转-学习使用,自己逐步增加 综述 毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。 首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工...

Opengraph URL: https://github.com/HankCoder/BlogAndCode/issues/13

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Android 混淆使用手册","articleBody":"[转-学习使用,自己逐步增加](https://juejin.im/entry/59df172f6fb9a04522067f98?utm_source=gold_browser_extension)\r\n# 综述\r\n\r\n毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。\r\n\r\n首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工程的难度。而依靠 Gradle 的 Android 插件,我们将移除未被使用的资源,可以有效减小 apk 安装包大小。\r\n\r\n本文由两部分构成,第一部分给出混淆的最佳实践,力求让零基础的新手都可以直接使用混淆;第二部分会介绍一下混淆的整体、自定义混淆规则的语法与实践、自定义资源保持的规则等。\r\n\r\n## 一、Android混淆最佳实践\r\n\r\n### 1. 混淆配置\r\n\r\n一般情况下,app module 的 build.gradle 文件默认会有如下结构:\r\n```\r\nandroid {\r\n    buildTypes {\r\n        release {\r\n            minifyEnabled false\r\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\r\n        }\r\n    }\r\n}\r\n```\r\n因为开启混淆会使编译时间变长,所以debug模式下不应该开启。我们需要做的是:\r\n\r\n将release下  minifyEnabled的值改为true,打开混淆;\r\n\r\n加上shrinkResources true,打开资源压缩。\r\n\r\n修改后文件内容如下:\r\n```\r\nandroid {\r\n    buildTypes {\r\n        release {\r\n            minifyEnabled true\r\n            shrinkResources true\r\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\r\n        }\r\n    }\r\n}\r\n```\r\n### 2. 自定义混淆规则\r\n\r\n在 app module 下默认生成了项目的自定义混淆规则文件 proguard-rules.pro,多方调研后,一份适用于大部分项目的混淆规则最佳实践如下:\r\n```\r\n#指定压缩级别\r\n-optimizationpasses 5\r\n\r\n#不跳过非公共的库的类成员\r\n-dontskipnonpubliclibraryclassmembers\r\n\r\n#混淆时采用的算法\r\n-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*\r\n\r\n#把混淆类中的方法名也混淆了\r\n-useuniqueclassmembernames\r\n\r\n#优化时允许访问并修改有修饰符的类和类的成员 \r\n-allowaccessmodification\r\n\r\n#将文件来源重命名为“SourceFile”字符串\r\n-renamesourcefileattribute SourceFile\r\n#保留行号\r\n-keepattributes SourceFile,LineNumberTable\r\n\r\n#保持所有实现 Serializable 接口的类成员\r\n-keepclassmembers class * implements java.io.Serializable {\r\n    static final long serialVersionUID;\r\n    private static final java.io.ObjectStreamField[] serialPersistentFields;\r\n    private void writeObject(java.io.ObjectOutputStream);\r\n    private void readObject(java.io.ObjectInputStream);\r\n    java.lang.Object writeReplace();\r\n    java.lang.Object readResolve();\r\n}\r\n\r\n#Fragment不需要在AndroidManifest.xml中注册,需要额外保护下\r\n-keep public class * extends android.support.v4.app.Fragment\r\n-keep public class * extends android.app.Fragment\r\n\r\n# 保持测试相关的代码\r\n-dontnote junit.framework.**\r\n-dontnote junit.runner.**\r\n-dontwarn android.test.**\r\n-dontwarn android.support.test.**\r\n-dontwarn org.junit.**\r\n```\r\n真正通用的、需要添加的就是上面这些,除此之外,需要每个项目根据自身的需求添加一些混淆规则:\r\n\r\n第三方库所需的混淆规则。正规的第三方库一般都会在接入文档中写好所需混淆规则,使用时注意添加。\r\n\r\n在运行时动态改变的代码,例如反射。比较典型的例子就是会与 json 相互转换的实体类。假如项目命名规范要求实体类都要放在model包下的话,可以添加类似这样的代码把所有实体类都保持住:  -keep public class **.*Model*.** {*;}\r\n\r\n- JNI中调用的类。\r\n- WebView中  JavaScript调用的方法\r\n- Layout布局使用的  View构造函数、android:onClick等。\r\n\r\n### 3. 检查混淆结果\r\n\r\n混淆过的包必须进行检查,避免因混淆引入的bug。\r\n\r\n一方面,需要从代码层面检查。使用上文的配置进行混淆打包后在 \u003cmodule-name\u003e/build/outputs/mapping/release/ 目录下会输出以下文件:\r\n\r\n - dump.txt描述APK文件中所有类的内部结构\r\n\r\n - mapping.txt提供混淆前后类、方法、类成员等的对照表\r\n\r\n - seeds.txt列出没有被混淆的类和成员\r\n\r\n - usage.txt列出被移除的代码\r\n\r\n我们可以根据 seeds.txt 文件检查未被混淆的类和成员中是否已包含所有期望保留的,再根据 usage.txt 文件查看是否有被误移除的代码。\r\n\r\n另一方面,需要从测试方面检查。将混淆过的包进行全方面测试,检查是否有 bug 产生。\r\n\r\n### 4. 解出混淆栈\r\n\r\n混淆后的类、方法名等等难以阅读,这固然会增加逆向工程的难度,但对追踪线上 crash 也造成了阻碍。我们拿到 crash 的堆栈信息后会发现很难定位,这时需要将混淆反解。\r\n\r\n在 \u003csdk-root\u003e/tools/proguard/  路径下有附带的的反解工具(Window 系统为 proguardgui.bat,Mac 或 Linux 系统为 proguardgui.sh)。\r\n\r\n这里以 Window 平台为例。双击运行 proguardgui.bat 后,可以看到左侧的一行菜单。点击 ReTrace,选择该混淆包对应的 mapping 文件(混淆后在 \u003cmodule-name\u003e/build/outputs/mapping/release/ 路径下会生成 mapping.txt 文件,它的作用是提供混淆前后类、方法、类成员等的对照表),再将 crash 的 stack trace 黏贴进输入框中,点击右下角的 ReTrace ,混淆后的堆栈信息就显示出来了。\r\n\r\n以上使用 GUI 程序进行操作,另一种方式是利用该路径下的 retrace 工具通过命令行进行反解,命令是\r\n\r\nretrace.bat|retrace.sh [-verbose] mapping.txt [\u003cstacktrace_file\u003e]\r\n例如:\r\n\r\nretrace.bat -verbose mapping.txt obfuscated_trace.txt\r\n注意事项:\r\n\r\n1) 所有在 AndroidManifest.xml 涉及到的类已经自动被保持,因此不用特意去添加这块混淆规则。(很多老的混淆文件里会加,现在已经没必要)\r\n\r\n2) proguard-android.txt 已经存在一些默认混淆规则,没必要在 proguard-rules.pro 重复添加,该文件具体规则见附录1:\r\n## 二、混淆简介\r\n\r\nAndroid中的“混淆”可以分为两部分,一部分是 Java 代码的优化与混淆,依靠 proguard 混淆器来实现;另一部分是资源压缩,将移除项目及依赖的库中未被使用的资源(资源压缩严格意义上跟混淆没啥关系,但一般我们都会放一起讲)。\r\n\r\n### 1. 代码压缩\r\n\r\n代码混淆是包含了代码压缩、优化、混淆等一系列行为的过程。如上图所示,混淆过程会有如下几个功能:\r\n\r\n压缩。移除无效的类、类成员、方法、属性等;\r\n\r\n优化。分析和优化方法的二进制代码;根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。\r\n\r\n混淆。把类名、属性名、方法名替换为简短且无意义的名称;\r\n\r\n预校验。添加预校验信息。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。\r\n\r\n这四个流程默认开启。\r\n\r\n在 Android 项目中我们可以选择将“优化”和“预校验”关闭,对应命令是-dontoptimize、  -dontpreverify(当然,默认的 proguard-android.txt 文件已包含这两条混淆命令,不需要开发者额外配置)。\r\n\r\n### 2. 资源压缩\r\n\r\n资源压缩将移除项目及依赖的库中未被使用的资源,这在减少 apk 包体积上会有不错的效果,一般建议开启。具体做法是在 build.grade 文件中,将 shrinkResources 属性设置为 true。需要注意的是,只有在用minifyEnabled true开启了代码压缩后,资源压缩才会生效。\r\n\r\n资源压缩包含了“合并资源”和“移除资源”两个流程。\r\n\r\n“合并资源”流程中,名称相同的资源被视为重复资源会被合并。需要注意的是,这一流程不受shrinkResources属性控制,也无法被禁止, gradle 必然会做这项工作,因为假如不同项目中存在相同名称的资源将导致错误。gradle 在四处地方寻找重复资源:\r\n\r\nsrc/main/res/ 路径\r\n\r\n不同的构建类型(debug、release等等)\r\n\r\n不同的构建渠道\r\n\r\n项目依赖的第三方库\r\n\r\n合并资源时按照如下优先级顺序:\r\n\r\n依赖 -\u003e main -\u003e 渠道 -\u003e 构建类型\r\n举个例子,假如重复资源同时存在于main文件夹和不同渠道中,gradle 会选择保留渠道中的资源。\r\n\r\n同时,如果重复资源在同一层次出现,比如src/main/res/ 和 src/main/res/,则 gradle 无法完成资源合并,这时会报资源合并错误。\r\n\r\n“移除资源”流程则见名知意,需要注意的是,类似代码,混淆资源移除也可以定义哪些资源需要被保留,这点在下文给出\r\n## 三、自定义混淆规则\r\n\r\n在上文“混淆配置”中有这样一行代码\r\n\r\nproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\r\n这行代码定义了混淆规则由两部分构成:位于 SDK 的 tools/proguard/ 文件夹中的 proguard-android.txt 的内容以及默认放置于模块根目录的 proguard-rules.pro 的内容。前者是 SDK 提供的默认混淆文件(内容见附录1),后者是开发者自定义混淆规则的地方。\r\n\r\n### 1. 常见混淆命令:\r\n\r\n- optimizationpasses\r\n- dontoptimize\r\n- dontusemixedcaseclassnames\r\n- dontskipnonpubliclibraryclasses\r\n- dontpreverify\r\n- dontwarn\r\n- verbose\r\n- optimizations\r\n- keep\r\n- keepnames\r\n- keepclassmembers\r\n- keepclassmembernames\r\n- keepclasseswithmembers\r\n- keepclasseswithmembernames\r\n\r\n在第一部分 Android 混淆最佳实践中已介绍部分需要使用到的混淆命令,这里不再赘述,详情请查阅官网。需要特别介绍的是与保持相关元素不参与混淆的规则相关的几种命令:\r\n命令\t作用\r\n-keep\t防止类和成员被移除或者被重命名\r\n-keepnames\t防止类和成员被重命名\r\n-keepclassmembers\t防止成员被移除或者被重命名\r\n-keepnames\t防止成员被重命名\r\n-keepclasseswithmembers\t防止拥有该成员的类和成员被移除或者被重命名\r\n-keepclasseswithmembernames\t防止拥有该成员的类和成员被重命名\r\n\r\n### 2. 保持元素不参与混淆的规则\r\n\r\n形如:\r\n```\r\n[保持命令] [类] {\r\n    [成员] \r\n}\r\n```\r\n“类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:\r\n\r\n具体的类\r\n\r\n访问修饰符(public、  protected、private)\r\n\r\n通配符*,匹配任意长度字符,但不含包名分隔符(.)\r\n\r\n通配符**,匹配任意长度字符,并且包含包名分隔符(.)\r\n\r\nextends,即可以指定类的基类\r\n\r\nimplement,匹配实现了某接口的类\r\n\r\n$,内部类\r\n\r\n“成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:\r\n\r\n匹配所有构造器\r\n匹配所有域\r\n匹配所有方法\r\n通配符*,匹配任意长度字符,但不含包名分隔符(.)\r\n\r\n通配符**,匹配任意长度字符,并且包含包名分隔符(.)\r\n\r\n通配符***,匹配任意参数类型\r\n\r\n…,匹配任意长度的任意类型参数。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 这些方法。\r\n\r\n访问修饰符(public、  protected、private)\r\n\r\n举个例子,假如需要将name.huihui.test包下所有继承  Activity的public类及其构造函数都保持住,可以这样写:\r\n\r\n-keep public class name.huihui.test.** extends Android.app.Activity {\r\n    \u003cinit\u003e\r\n}\r\n### 3. 常用的自定义混淆规则\r\n\r\n不混淆某个类\r\n\r\n-keep public class name.huihui.example.Test { *; }\r\n不混淆某个包所有的类\r\n\r\n-keep class name.huihui.test.** { *; }\r\n不混淆某个类的子类\r\n\r\n-keep public class * extends name.huihui.example.Test { *; }\r\n不混淆所有类名中包含了“model”的类及其成员\r\n\r\n-keep public class **.*model*.** {*;}\r\n不混淆某个接口的实现\r\n\r\n-keep class * implements name.huihui.example.TestInterface { *; }\r\n不混淆某个类的构造方法\r\n\r\n-keepclassmembers class name.huihui.example.Test { \r\n  public \u003cinit\u003e(); \r\n}\r\n不混淆某个类的特定的方法\r\n\r\n-keepclassmembers class name.huihui.example.Test { \r\n  public void test(java.lang.String); \r\n}\r\n四、自定义资源保持规则\r\n\r\n1. keep.xml\r\n\r\n用shrinkResources true开启资源压缩后,所有未被使用的资源默认被移除。假如你需要定义哪些资源必须被保留,在  res/raw/ 路径下创建一个 xml 文件,例如 keep.xml。\r\n\r\n通过一些属性的设置可以实现定义资源保持的需求,可配置的属性有:\r\n\r\ntools:keep 定义哪些资源需要被保留(资源之间用“,”隔开)\r\n\r\ntools:discard 定义哪些资源需要被移除(资源之间用“,”隔开)\r\n\r\ntools:shrinkMode 开启严格模式\r\n\r\n当代码中通过 Resources.getIdentifier()  用动态的字符串来获取并使用资源时,普通的资源引用检查就可能会有问题。例如,如下代码会导致所有以“img_”开头的资源都被标记为已使用。\r\n\r\nString name = String.format(\"img_%1d\", angle + 1);\r\nres = getResources().getIdentifier(name, \"drawable\", getPackageName());\r\n我们可以设置 tools:shrinkMode 为 strict 来开启严格模式,使只有确实被使用的资源被保留。\r\n\r\n以上就是自定义资源保持规则相关的配置,举个例子:\r\n\r\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\r\n\u003cresources xmlns:tools=\"http://schemas.android.com/tools\"\r\n    tools:keep=\"@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*\"\r\n    tools:discard=\"@layout/unused2\"\r\n    tools:shrinkMode=\"strict\"/\u003e\r\n2. 移除替代资源\r\n\r\n一些替代资源,例如多语言支持的 strings.xml,多分辨率支持的  layout.xml 等,在我们不需要使用又不想删除掉时,可以使用资源压缩将它们移除。\r\n\r\n我们使用 resConfig 属性来指定需要支持的属性,例如\r\n\r\nandroid {\r\n    defaultConfig {\r\n        ...\r\n        resConfigs \"en\", \"fr\"\r\n    }\r\n}\r\n其他未显式声明的语言资源将被移除。\r\n\r\n参考资料\r\n\r\nShrink Your Code and Resources\r\n\r\nproguard\r\n\r\nAndroid安全攻防战,反编译与混淆技术完全解析(下)\r\n\r\nAndroid混淆从入门到精通\r\n\r\nAndroid代码混淆之ProGuard\r\n\r\n附录\r\n\r\nproguard-android.txt文件内容\r\n\r\n#包名不混合大小写\r\n-dontusemixedcaseclassnames\r\n\r\n#不跳过非公共的库的类\r\n-dontskipnonpubliclibraryclasses\r\n\r\n#混淆时记录日志\r\n-verbose\r\n\r\n#关闭预校验\r\n-dontpreverify\r\n\r\n#不优化输入的类文件\r\n-dontoptimize\r\n\r\n#保护注解\r\n-keepattributes *Annotation*\r\n\r\n#保持所有拥有本地方法的类名及本地方法名\r\n-keepclasseswithmembernames class * {\r\n    native \u003cmethods\u003e;\r\n}\r\n\r\n#保持自定义View的get和set相关方法\r\n-keepclassmembers public class * extends android.view.View {\r\n   void set*(***);\r\n   *** get*();\r\n}\r\n\r\n#保持Activity中View及其子类入参的方法\r\n-keepclassmembers class * extends android.app.Activity {\r\n   public void *(android.view.View);\r\n}\r\n\r\n#枚举\r\n-keepclassmembers enum * {\r\n    **[] $VALUES;\r\n    public *;\r\n}\r\n\r\n#Parcelable\r\n-keepclassmembers class * implements android.os.Parcelable {\r\n  public static final android.os.Parcelable$Creator CREATOR;\r\n}\r\n\r\n#R文件的静态成员\r\n-keepclassmembers class **.R$* {\r\n    public static \u003cfields\u003e;\r\n}\r\n\r\n-dontwarn android.support.**\r\n\r\n#keep相关注解\r\n-keep class android.support.annotation.Keep\r\n\r\n-keep @android.support.annotation.Keep class * {*;}\r\n\r\n-keepclasseswithmembers class * {\r\n    @android.support.annotation.Keep \u003cmethods\u003e;\r\n}\r\n\r\n-keepclasseswithmembers class * {\r\n    @android.support.annotation.Keep \u003cfields\u003e;\r\n}\r\n\r\n-keepclasseswithmembers class * {\r\n    @android.support.annotation.Keep \u003cinit\u003e(...);\r\n}\r\n写在最后\r\n\r\n十分感谢你能看到最后,因本人水平有限,如有错漏还请不吝赐教,可以直接回复公众号或者去博客底下评论,在此先行谢过。\r\n\r\n同时,假如有这方面的困惑或者讨论,也十分欢迎联系我。","author":{"url":"https://github.com/HankCoder","@type":"Person","name":"HankCoder"},"datePublished":"2017-10-20T04:15:08.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/13/BlogAndCode/issues/13"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:2943dc9d-0ca4-a343-6143-0cfa1d8eb29f
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idA5C8:A3218:127D8FB:1918D00:6A5718BD
html-safe-noncede819000b742ec32693631ccea0e0570dc056127c4d895b7abbb2221d82469c8
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNUM4OkEzMjE4OjEyN0Q4RkI6MTkxOEQwMDo2QTU3MThCRCIsInZpc2l0b3JfaWQiOiI0NTQxMjQ1NDE2OTM1OTg3Mzg5IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmacc8818f7f5de4dfdea8ab6d1db7e3002aaaf201903b1018627c78b92014e215b7
hovercard-subject-tagissue:267059273
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/HankCoder/BlogAndCode/13/issue_layout
twitter:imagehttps://opengraph.githubassets.com/72bb5e78afe6783d301eddff293e84a462349d78ae77f8d413a1d9630b0e6886/HankCoder/BlogAndCode/issues/13
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/72bb5e78afe6783d301eddff293e84a462349d78ae77f8d413a1d9630b0e6886/HankCoder/BlogAndCode/issues/13
og:image:alt转-学习使用,自己逐步增加 综述 毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。 首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernameHankCoder
hostnamegithub.com
expected-hostnamegithub.com
None4e7a7296a3830877cf21a6ad2a972c9e618a48915e03966cf0c53eb08e5aad98
turbo-cache-controlno-preview
go-importgithub.com/HankCoder/BlogAndCode git https://github.com/HankCoder/BlogAndCode.git
octolytics-dimension-user_id10301539
octolytics-dimension-user_loginHankCoder
octolytics-dimension-repository_id83747204
octolytics-dimension-repository_nwoHankCoder/BlogAndCode
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id83747204
octolytics-dimension-repository_network_root_nwoHankCoder/BlogAndCode
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release2576d1f0198cf1588faf2edf27f1ed120903be10
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/HankCoder/BlogAndCode/issues/13#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FHankCoder%2FBlogAndCode%2Fissues%2F13
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub Copilot appDirect agents from issue to mergehttps://github.com/features/ai/github-app
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
View all resourceshttps://github.com/resources
GitHub SponsorsFund open source developershttps://github.com/open-source/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/open-source/accelerator
GitHub Starshttps://stars.github.com
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FHankCoder%2FBlogAndCode%2Fissues%2F13
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=HankCoder%2FBlogAndCode
Reloadhttps://github.com/HankCoder/BlogAndCode/issues/13
Reloadhttps://github.com/HankCoder/BlogAndCode/issues/13
Reloadhttps://github.com/HankCoder/BlogAndCode/issues/13
HankCoder https://github.com/HankCoder
BlogAndCodehttps://github.com/HankCoder/BlogAndCode
Notifications https://github.com/login?return_to=%2FHankCoder%2FBlogAndCode
Fork 0 https://github.com/login?return_to=%2FHankCoder%2FBlogAndCode
Star 1 https://github.com/login?return_to=%2FHankCoder%2FBlogAndCode
Code https://github.com/HankCoder/BlogAndCode
Issues 16 https://github.com/HankCoder/BlogAndCode/issues
Pull requests 0 https://github.com/HankCoder/BlogAndCode/pulls
Actions https://github.com/HankCoder/BlogAndCode/actions
Projects https://github.com/HankCoder/BlogAndCode/projects
Security and quality 0 https://github.com/HankCoder/BlogAndCode/security
Insights https://github.com/HankCoder/BlogAndCode/pulse
Code https://github.com/HankCoder/BlogAndCode
Issues https://github.com/HankCoder/BlogAndCode/issues
Pull requests https://github.com/HankCoder/BlogAndCode/pulls
Actions https://github.com/HankCoder/BlogAndCode/actions
Projects https://github.com/HankCoder/BlogAndCode/projects
Security and quality https://github.com/HankCoder/BlogAndCode/security
Insights https://github.com/HankCoder/BlogAndCode/pulse
Android 混淆使用手册https://github.com/HankCoder/BlogAndCode/issues/13#top
Androidhttps://github.com/HankCoder/BlogAndCode/issues?q=state%3Aopen%20label%3A%22Android%22
https://github.com/HankCoder
HankCoderhttps://github.com/HankCoder
on Oct 20, 2017https://github.com/HankCoder/BlogAndCode/issues/13#issue-267059273
转-学习使用,自己逐步增加https://juejin.im/entry/59df172f6fb9a04522067f98?utm_source=gold_browser_extension
Androidhttps://github.com/HankCoder/BlogAndCode/issues?q=state%3Aopen%20label%3A%22Android%22
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.