前言

混淆是保护apk的必要手段,通过混淆,如果apk被反编译,会对一些关键词使用a,b,c之类的字符替换,加大解读难度

如何混淆

在build.gradle文件中配置如下代码:

android {buildTypes {debug {...}release {//混淆开关minifyEnabled true// 是否zip对齐zipAlignEnabled true// 移除无用的resource文件shrinkResources false// 是否打开debuggable开关debuggable false// 是否打开jniDebuggable开关jniDebuggable falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.myConfig}}
}

minifyEnabled为true,就是打开混淆。主要的配置规则是在proguard-rules.pro文件中进行,

混淆规则

混淆设置参数

-optimizationpasses 5                       # 代码混淆的压缩比例,值介于0-7,默认5
-verbose                                    # 混淆时记录日志
-dontoptimize                               # 不优化输入的类文件
-dontshrink                                 # 关闭压缩
-dontpreverify                              # 关闭预校验(作用于Java平台,Android不需要,去掉可加快混淆)
-dontoptimize                               # 关闭代码优化
-dontobfuscate                              # 关闭混淆
-ignorewarnings                             # 忽略警告
-dontwarn com.squareup.okhttp.**            # 指定类不输出警告信息
-dontusemixedcaseclassnames                 # 混淆后类型都为小写
-dontskipnonpubliclibraryclasses            # 不跳过非公共的库的类
-printmapping mapping.txt                   # 生成原类名与混淆后类名的映射文件mapping.txt
-useuniqueclassmembernames                  # 把混淆类中的方法名也混淆
-allowaccessmodification                    # 优化时允许访问并修改有修饰符的类及类的成员
-renamesourcefileattribute SourceFile       # 将源码中有意义的类名转换成SourceFile,用于混淆具体崩溃代码
-keepattributes SourceFile,LineNumberTable  # 保留行号
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod # 避免混淆注解、内部类、泛型、匿名类
-optimizations !code/simplification/cast,!field/ ,!class/merging/   # 指定混淆时采用的算法

保持不被混淆的设置

[保持命令] [类] {[成员]
}-keep                           # 防止类和类成员被移除或被混淆;
-keepnames                      # 防止类和类成员被混淆;
-keepclassmembers               # 防止类成员被移除或被混淆;
-keepclassmembernames           # 防止类成员被混淆;
-keepclasseswithmembers         # 防止拥有该成员的类和类成员被移除或被混淆;
-keepclasseswithmembernames     # 防止拥有该成员的类和类成员被混淆;

具体的类
访问修饰符 → public、private、protected
通配符(*) → 匹配任意长度字符,但不包含包名分隔符(.)
通配符(**) → 匹配任意长度字符,且包含包名分隔符(.)
extends → 匹配实现了某个父类的子类
implements → 匹配实现了某接口的类
$ → 内部类

成员

匹配所有构造器 → <init>
匹配所有域 → <field>
匹配所有方法 → <methods>
访问修饰符 → public、private、protected
除了 * 和 ** 通配符外,还支持 *** 通配符,匹配任意参数类型
... → 匹配任意长度的任意类型参数,如void test(...)可以匹配不同参数个数的test方法

常用自定义混淆规则范例

# 不混淆某个类的类名,及类中的内容
-keep class cn.coderpig.myapp.example.Test { *; }# 不混淆指定包名下的类名,不包括子包下的类名
-keep class cn.coderpig.myapp*# 不混淆指定包名下的类名,及类里的内容
-keep class cn.coderpig.myapp* {*;}# 不混淆指定包名下的类名,包括子包下的类名
-keep class cn.coderpig.myapp**# 不混淆某个类的子类
-keep public class * extends cn.coderpig.myapp.base.BaseFragment# 不混淆实现了某个接口的类
-keep class * implements cn.coderpig.myapp.dao.DaoImp# 不混淆类名中包含了"entity"的类,及类中内容
-keep class **.*entity*.** {*;}# 不混淆内部类中的所有public内容
-keep class cn.coderpig.myapp.widget.CustomView$OnClickInterface {public *;
}# 不混淆指定类的所有方法
-keep cn.coderpig.myapp.example.Test {public <methods>;
}# 不混淆指定类的所有字段
-keep cn.coderpig.myapp.example.Test {public <fields>;
}# 不混淆指定类的所有构造方法
-keep cn.coderpig.myapp.example.Test {public <init>;
}# 不混淆指定参数作为形参的方法
-keep cn.coderpig.myapp.example.Test {public <methods>(java.lang.String);
}# 不混淆类的特定方法
-keep cn.coderpig.myapp.example.Test {public test(java.lang.String);
}# 不混淆native方法
-keepclasseswithmembernames class * {native <methods>;
}# 不混淆枚举类
-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);
}#不混淆资源类
-keepclassmembers class **.R$* {public static <fields>;
}# 不混淆自定义控件
-keep public class * entends android.view.View {*** get*();void set*(***);public <init>;
}# 不混淆实现了Serializable接口的类成员,此处只是演示,也可以直接 *;
-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();
}# 不混淆实现了parcelable接口的类成员
-keep class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator *;
}# 注意事项:
#
# ① jni方法不可混淆,方法名需与native方法保持一致;
# ② 反射用到的类不混淆,否则反射可能出问题;
# ③ 四大组件、Application子类、Framework层下的类、自定义的View默认不会被混淆,无需另外配置;
# ④ WebView的JS调用接口方法不可混淆;
# ⑤ 注解相关的类不混淆;
# ⑥ GSON、Fastjson等解析的Bean数据类不可混淆;
# ⑦ 枚举enum类中的values和valuesof这两个方法不可混淆(反射调用);
# ⑧ 继承Parceable和Serializable等可序列化的类不可混淆;
# ⑨ 第三方库或SDK,请参考第三方提供的混淆规则,没提供的话,建议第三方包全部不混淆;

proguard-rules.pro

ProGuard在AS中默认的配置文件是proguard-rules.pro
我们常见的ProGuard文件主要包含如下几个部分:
1.基本配置,设定混淆的规则等,基本配置是每个混淆文件必须存在的,并且此块内容大部分通用,可以直接copy。
2.基本的keep项,多数Android工程都需要非混淆的内容,包括有四大组件等内容,此项内容也大部分通用,也可以直接copy。
3.三方引入的lib包混淆,这个内容需要去各自的官网去查找对应的混淆添加代码。
4.其他需要不混淆的内容,包括:实体类,json解析类,WebView及js的调用模块,与反射相关的类和方法。

基本配置
基本配置基本上变化不大,一般的项目可以直接copy

#指定压缩级别
-optimizationpasses 5#不跳过非公共的库的类成员
-dontskipnonpubliclibraryclassmembers#混淆时采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*#把混淆类中的方法名也混淆了
-useuniqueclassmembernames#指定不去忽略非公共的库的类
-dontskipnonpubliclibraryclasses#不做预检验,preverify是proguard的四大步骤之一,可以加快混淆速度
#-dontpreverify# 忽略警告(?)
#-ignorewarnings#混淆时不使用大小写混合,混淆后的类名为小写(大小写混淆容易导致class文件相互覆盖)
-dontusemixedcaseclassnames#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification#将文件来源重命名为“SourceFile”字符串
#-renamesourcefileattribute SourceFile
#保留行号
-keepattributes SourceFile,LineNumberTable
#保持泛型
-keepattributes Signature
# 保持注解
-keepattributes *Annotation*,InnerClasses# 保持测试相关的代码
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**

基本的项目配置
多数是包括序列化,Android四大组件等基本内容的混淆keep,对于常规的项目而言区别不大,也可以进行copy

# Parcelable
-keep class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator *;
}
# Serializable
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {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$* {*;}# 保留四大组件,自定义的Application,Fragment等这些类不被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Fragment
-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## support
-dontwarn android.support.**
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *;}
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**-keep public class * extends android.support.v4.view.ActionProvider {public <init>(android.content.Context);
}# 保留枚举类不被混淆
-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);
}# 保留本地native方法不被混淆
-keepclasseswithmembers class * {native <methods>;
}# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {void *(**On*Event);void *(**On*Listener);
}-keepclassmembers public class * extends android.view.View {void set*(***);*** get*();
}#保留在Activity中的方法参数是view的方法,
-keepclassmembers class * extends android.app.Activity {public void *(android.view.View);
}# For XML inflating, keep views' constructoricon.png    自定义view
-keep public class * extends android.view.View {public <init>(android.content.Context);public <init>(android.content.Context, android.util.AttributeSet);public <init>(android.content.Context, android.util.AttributeSet, int);public void set*(...);
}# 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.**
-printconfiguration
-keep,allowobfuscation @interface androidx.annotation.Keep-keep @androidx.annotation.Keep class *
-keepclassmembers class * {@androidx.annotation.Keep *;
}

三方SDK的混淆
三方SDK的混淆代码需要去各自的SDK官网上去查找,例如网易云信的

### nimlib
-dontwarn com.netease.nim.**
-keep class com.netease.nim.** {*;}-dontwarn com.netease.nimlib.**
-keep class com.netease.nimlib.** {*;}-dontwarn com.netease.share.**
-keep class com.netease.share.** {*;}-dontwarn com.netease.mobsec.**
-keep class com.netease.mobsec.** {*;}

其他混淆内容
其他混淆内容比较杂,包括:实体类,json解析类,WebView及js的调用模块,与反射相关的类和方法。这个根据自己需要来做


# WebView
-dontwarn android.webkit.WebView
-dontwarn android.net.http.SslError
-dontwarn android.webkit.WebViewClient
-keep public class android.webkit.WebView
-keep public class android.net.http.SslError
-keep public class android.webkit.WebViewClient
-keepclass com.mandala.healthserviceresident.vo.** { *; } #实体类不参与混淆
-keep class com.mandala.healthserviceresident.http.** { *; } #实体类不参与混淆
-keep class com.hacker.okhttputil.** { *; } #实体类不参与混淆
-keep class net.sqlcipher.** { *; }

-keep class 类所在的包.** { *; }
log过滤

-assumenosideeffects class android.util.Log {public static *** d(...);public static *** v(...);
}

混淆结果
在添加混淆之后,在排查时我们可以对混淆和未混淆的类名进行记录和打印,在混淆文件中添加如下代码可以查看混淆编译的类及文件结构:

# 混淆映射,生成映射文件
-verbose
-printmapping proguardMapping.txt
#输出apk包内所有的class的内部结构
-dump dump.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从apk中删除的代码
-printusage unused.txt

添加上述代码之后,会在生成release包时也同时生成三个日志文件,我们在排查混淆问题时可以以上述三个文件作为参考,快速排查。

Bug排查

我这边混淆完成以后,打包测试,发现网络能访问,但是提示失败,刚开始想的办法是抓包,然后发现实体类被设置成a,b之类的,这个是被混淆了,然后在混淆文件里keep一下,但是还有其他bug ,这样打包完成以后,如果没有有效的手段,只能是看着现象和混淆文件一个一个猜了,如果能在debug的时候发现Bug就很好排查了,排查方法就是在debug模式下,打开混淆
打开方法很简单

  buildTypes {debug {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}release {zipAlignEnabled trueshrinkResources trueminifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.myConfig}}

就这样,然后debug进行排查就好了。正常开发的时候记得关掉,比较耗性能

我用到的配置

# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\AndroidStudio\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 *;
#}# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags.  Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik.  The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.)  Make sure you
# test thoroughly if you go this route.-optimizations !code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version.  We know about them, and they are safe.
-dontwarn android.support.**-dontwarn org.apache.http.**
-dontwarn com.amap.**
-dontwarn com.alibaba.**
-dontwarn com.netease.**
-dontwarn io.netty.**
-dontwarn com.autonavi.amap.**### keep options
#system default, from android example
-keep public class * extends android.app.Activity
-keep public class * extends androidx.fragment.app.Fragment
-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-keepattributes *Annotation*,InnerClasses
#-keepattributes SourceFile,LineNumberTable### 3rd party jars
-keep class android.support.** {*;}
-keep class com.amap.** {*;}
-keep class android.webkit.** {*;}### 3rd party jars(lucene)
-dontwarn java.nio.channels.SeekableByteChannel
-dontwarn org.apache.lucene.**
-keep class org.apache.lucene.** {*;}### nimlib
-dontwarn com.netease.nim.**
-keep class com.netease.nim.** {*;}-dontwarn com.netease.nimlib.**
-keep class com.netease.nimlib.** {*;}-dontwarn com.netease.share.**
-keep class com.netease.share.** {*;}-dontwarn com.netease.mobsec.**
-keep class com.netease.mobsec.** {*;}-keepclasseswithmembernames class * {native <methods>;
}-keepclasseswithmembers class * {public <init>(android.content.Context, android.util.AttributeSet);public <init>(android.content.Context, android.util.AttributeSet, int);
}-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);
}-keep class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator *;
}-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();
}-keep class **.R$* {*;
}-keepclassmembers class * {public <init> (org.json.JSONObject);
}-keep   class com.amap.api.maps.**{*;}
-keep   class com.amap.api.trace.**{*;}-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}-keep class com.alibaba.sdk.android.**{*;}-dontwarn demo.**-keep class demo.**{*;}# Bugly异常上报-dontwarn com.tencent.bugly.**-keep public class com.tencent.bugly.**{*;}-keep class com.umeng.** {*;}-keep class java.security.interfaces.*-keep class java.util.Base64-keep class org.repackage.** {*;}-keepclassmembers class * {public <init> (org.json.JSONObject);}-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);}-keep class org.greenrobot.greendao.**{*;}
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao{public static java.lang.String TABLENAME;
}
-keep class **$Properties-keep class com.mandala.healthserviceresident.greendao.gen.**{*;}
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.**#Gson
-keep class com.google.gson.** {*;}
-keep class com.google.**{*;}
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
#okhttp
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {@butterknife.* <methods>;
}-keep class com.mandala.healthserviceresident.vo.** { *; } #实体类不参与混淆
-keep class com.mandala.healthserviceresident.http.** { *; } #实体类不参与混淆
-keep class com.hacker.okhttputil.** { *; } #实体类不参与混淆
-keep class net.sqlcipher.** { *; }# WebView
-dontwarn android.webkit.WebView
-dontwarn android.net.http.SslError
-dontwarn android.webkit.WebViewClient
-keep public class android.webkit.WebView
-keep public class android.net.http.SslError
-keep public class android.webkit.WebViewClient-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-ignorewarnings

重点看一下greendao的配置,如果没配置,会提示daoConfig出错

-keep class org.greenrobot.greendao.**{*;}
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao{public static java.lang.String TABLENAME;
}
-keep class **$Properties-keep class com.mandala.healthserviceresident.greendao.gen.**{*;}
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.**

android 如何混淆以及排查混淆后不明Bug相关推荐

  1. Android制作AAR包并混淆后加载调用

    学更好的别人, 做更好的自己. --<微卡智享> 本文长度为4389字,预计阅读12分钟 前言 最近准备做新版本产品研发,部分相关算法原来用的Webapi调用返回,考虑到速度问题,需要在A ...

  2. 浅谈Android保护技术__代码混淆

    浅谈Android保护技术__代码混淆 浅谈Android保护技术__代码混淆 代码混淆 代码混淆(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读 ...

  3. 【Android 安装包优化】开启 ProGuard 混淆 ( 压缩 Shrink | 优化 Optimize | 混淆 Obfuscate | 预检 | 混淆文件编写 | 混淆前后对比 )

    文章目录 一.开启 ProGuard 混淆 二.混淆文件编写 三.混淆前后对比 四.build.gradle 完整配置文件 五.参考资料 一.开启 ProGuard 混淆 在 build.gradle ...

  4. Android APK代码混淆与资源混淆详解,你确定不看?

    APK的混淆分为资源混淆与代码混淆.一般大部分都使用两者结合.尤其是目前主流的应用. 其中的优点: 防止被恶意破解逆向分析 减少apk体积,也是瘦身的方法 代码可阅读性降低 其中的缺点: 调试不方便( ...

  5. Android混淆(包括混淆四大组件)

    ps:这篇文章主要是自己对混淆的理解 目录 概念: 使用: 基本的混淆规则: 常用的混淆规则: 自定义混淆规则: 混淆四大组件: 注意事项: Demo地址: 概念: 简单的来说就是将源代码变成难以看懂 ...

  6. android代码混淆aar_android代码混淆个人总结及踩坑

    前言 公司项目使用组件化开发的形式,需要对自己负责的模块进行一些混淆配置,关于混淆相信做android开发的都或多或少有过一些接触,通过对混淆文件的配置从而将代码中的类名,方法名,成员变量等进行无意义 ...

  7. Android使用R8压缩,混淆,优化App

    在Android开发中,为了使App尽可能小,可以使用R8来压缩,混淆,优化App,当使用Android Gradle插件3.4.0或更高版本时,插件不再使用ProGuard执行优化而是R8. R8的 ...

  8. android so文件不混淆_Android NDK混淆

    项目原因研究Android的So库代码混淆,增加IDA工具查看的复杂度.简单接触了ollvm 混淆组件.网上查看了很多人的文章,简单做个随笔备忘. 1.ollvm下载编译 我的是macbook环境. ...

  9. android混淆保留内部类,混淆jar包总结

    首先是android混淆配置: http://blog.csdn.net/dianyueneo/article/details/7212012 proguard命令行: 首先定位到proguard.j ...

最新文章

  1. “Uncaught TypeError: string is not a function”
  2. HDU1533(最小权完美匹配)
  3. ssh整合问题总结--使用struts2+Ajax+jquery验证用户名是否已被注册
  4. SVN Server安装配置
  5. javascript中神奇的(+)加操作符
  6. 《Abaqus GUI程序开发指南(Python语言)》——2.5 动态类型简介
  7. SpringBoot中自定义消息转化器
  8. 机器学习——正则化-L2
  9. git commit时出现unable to auto-detect email address错误
  10. qsort()编译器自带快速排序的用法
  11. https网站引用http路径的js和css失效解决办法
  12. Pandas的学习(2.Series的索引和切片、基本概念以及Series的运算)
  13. iOS逆向之hook框架frida的安装和使用
  14. 【2010福建】收稻子 (校BSOJ1114)
  15. 让程序结果在屏幕上暂停一段时间
  16. android接口类命名规范_超全面的UI基础设计规范来啦,还不收藏 ~
  17. 【Android】Android Studio打包生成正式签名的APK
  18. TS流解析 二 *****
  19. python w3cschool_学习Python必去的8个网站
  20. matlab 莫比乌斯曲面,教你Rhino“3步”快速打造循环曲面“莫比乌斯”

热门文章

  1. 阿里、腾讯、百度纷纷涉猎SaaS领域寓意为何?
  2. 2022年中国10大物联网云平台评估:阿里云第一,小米落选
  3. c语言 for语句输出九九乘法表
  4. matlab如何读su文件下载,su二进制文件
  5. 看抖音快手时显示无法进入服务器,手机突然无法进入抖音直播怎么回事?解决进不去抖音直播的方法...
  6. 翻编-JavaScript有关的10个怪癖和秘密
  7. markdown居中文本
  8. 打破学科之间壁垒的STEAM教育
  9. Java--多态(polymorphic),上下转型,多态好处弊端
  10. 交换网络技术-----STP/RSTP协议