主頁 > 移動端開發 > Proguard 常用規則

Proguard 常用規則

2020-09-10 06:59:00 移動端開發

思維導圖

入口

為了決定哪些代碼要被保留哪些代碼要出丟棄和混淆,必須指定入口點,這些入口點通常是 main方法,activity,service等,

  • 在壓縮階段,Proguard從這些入口點開始遞回確定哪些類或類成員要被使用,其余的都會被丟棄,

  • 在優化階段,ProGuard 會進一步優化代碼,在其他優化中,可以將不是入口點的類和方法設為 private,static 或 final ,洗掉未使用的引數,并且可以行內一些方法,

  • 在混淆階段,ProGuard 會重新命名不屬于入口點的類和類成員,在整個程序中,保證入口點仍然可以通過其原始名稱訪問,

查看 Proguard 輸出結果

為了避免引入 bug 我們有必要對 結果進行檢查,

在Android中,開啟了混淆構建會在 /build/outputs/mapping/ 目錄下會輸出以下檔案:

  • dump.txt 描述APK檔案中所有類的內部結構

  • mapping.txt 提供混淆前后類、方法、類成員等的對照表

  • seeds.txt 列出沒有被混淆的類和成員

  • usage.txt 列出被移除的代碼

    ?

我們可以根據 seeds.txt 檔案檢查未被混淆的類和成員中是否已包含所有期望保留的,再根據 usage.txt 檔案查看是否有被誤移除的代碼,

過濾器

ProGuard 為許多配置提供了不同方面的過濾選項:檔案名稱,目錄,類別,軟體包,屬性,優化等,

過濾器是可以包含通配符的,以逗號分隔的,名稱串列,

只有與串列中的專案匹配的名稱才會通過過濾器,

每種配置的通配符可能有所不同,但以下通配符是通用的:

  • ? 匹配名稱中的任何單個字符,

  • * 匹配不包含包分隔符或目錄分隔符的名稱的任何部分

  • ** 匹配名稱的任何部分,可能包含任意數量的包分隔符或目錄分隔符,

此外,名稱前可以加上否定感嘆號 ! 排除名稱與進一步嘗試匹配后續名稱,

因此,如果名稱與過濾器中的某個專案相匹配,則會立即接受或拒絕該專案,具體取決于專案是否具有否定符,

如果名稱與專案不匹配,則會針對下一個專案進行測驗,依此類推,

它如果與任何專案不匹配,則根據最后一項是否具有否定符而被接受或拒絕,

如,"!foobar,*.bar" 匹配除了foobar之外的所有以bar結尾的名稱,

下面以過濾檔案具體舉例,

檔案過濾器

像通用過濾器一樣,檔案過濾器是逗號分隔的檔案名串列,可以包含通配符,只有具有匹配檔案名的檔案被讀取(在輸入的情況下),或者被寫入(在輸出的情況下),支持以下通配符:

  • ? 匹配檔案名字中的任何單個字符

  • * 匹配不包含目錄分隔符的檔案名的任何部分,

  • ** 匹配檔案名的任何部分,可以包含任意數目的目錄分隔符,

例如 "java/**.class ,javax/**.class" 可以匹配 java和javax目錄下所有的 class 檔案,

此外,檔案名前面可能帶有感嘆號'!'將檔案名排除在與后續檔案名匹配上,

例如 "!**.gif,images/**" 匹配images目錄下所有除了 gif 的檔案

關于更詳細的用法 可以查看官方檔案 filtering

keep 選項

-keep [,modifier,...] class specification

指定類和類成員(欄位,方法)作為入口點被保留,

例如,為了保留一個程式,你要指定Main方法和類,為了保留一個庫,你應該指定所有被公開訪問的元素,

  • 保留 main 類和 main 方法
-keep public class com.example.MyMain { 
      public static void main(java.lang.String[]); 
}
  • 保留所有被公開訪問的元素
-keep public class * { 

      public protected *; 

} 

Note:如果你只保留了類,沒有保留類成員,那么你的類成員將不會被保留

例如 有一個物體類

public class Product implements Serializable {

    public static final int A = 1;

    public static final int B = 2;

    private String name;

    private String url;



    public String getName() {

        return name;

    }



    public void setName(String name) {

        this.name = name;

    }



    public String getUrl() {

        return url;

    }



    public void setUrl(String url) {

        this.url = url;

    }

}

規則配置如下

# 保留 Product類

-keep class cn.sintoon.camera.Product

usage.txt檔案中有以下內容 ,可以看到 類中的成員全部被移除了

cn.sintoon.camera.Product:

    public static final int A

    public static final int B

    private java.lang.String name

    private java.lang.String url

    16:16:public java.lang.String getName()

    20:21:public void setName(java.lang.String)

    24:24:public java.lang.String getUrl()

    28:29:public void setUrl(java.lang.String)

-keepclassmembers [,modifier,...] class specification

指定要保留的類成員,前提是它們的類也被保留了

例如,你想保留實作了 Serializable 介面的類中的所有 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(); 

}

Note: 注意欄位型別帶上包名; String 型別為 java.lang.String;另外,如果只保留了類成員沒有保留類跟沒有保留一樣

還是拿上面那個例子,改一下規則

-keepclassmembers class * implements java.io.Serializable{

    private String name;

     public String getName();

     public static final int A;

}

再看 usage.txt 類都被移除了,保留欄位沒毛線用,

cn.sintoon.camera.Product

-keepclasseswithmembers [,modifier,...] class specification

指定要保留的類和類成員,條件是所有指定的類成員都在,

例如,你要保留程式中所有的主程式,不用顯示的列出,

-keepclasseswithmembers public class * { 

    public static void main(java.lang.String[]); 

}

還是用上面那個例子,保留住類和所有的類成員

-keepclasseswithmembers class cn.sintoon.camera.Product{

 public static final int A;

    public static final int B;

    private java.lang.String name;

    private java.lang.String url;

    public java.lang.String getName();

    public void setName(java.lang.String);

    public java.lang.String getUrl();

    public void setUrl(java.lang.String);

}

看 seeds.text 中就會出現這個類和類成員

cn.sintoon.camera.Product

cn.sintoon.camera.Product: int A

cn.sintoon.camera.Product: int B

cn.sintoon.camera.Product: java.lang.String name

cn.sintoon.camera.Product: java.lang.String url

cn.sintoon.camera.Product: java.lang.String getName()

cn.sintoon.camera.Product: void setName(java.lang.String)

cn.sintoon.camera.Product: java.lang.String getUrl()

cn.sintoon.camera.Product: void setUrl(java.lang.String)

Note:一定要注意指定的類成員必須存在,如果不存在的話,這個規則相當于沒有配,一點作用沒有

-keepnames class specification

-keep,allowshrinking class specification的簡寫

指定要保留名稱的類成員和類成員(如果它們在壓縮階段未被洗掉),

例如,你可能希望保留實作 Serializable 介面的類的所有類名,以便處理后的代碼與任何原始序列化的類保持兼容,

完全不用的類仍然可以洗掉,只有在混淆時才適用,

-keepnames class * implements java.io.Serializable

Note: 前提是在壓縮階段沒有被洗掉掉,這里相當于使用了修飾符 allowshrinking

-keepclassmembernames class specification

-keepclassmembers,allowshrinking class specification 的簡寫

指定要保留名稱的類成員(如果它們在壓縮階段未被洗掉),

例如,在處理由JDK 1.2或更早版本編譯的庫時,可能希望保留合成類$方法的名稱,

所以當處理使用處理過的庫的應用程式時,混淆器可以再次檢測到它(盡管ProGuard本身不需要這個),

只有在混淆時才適用,

-keepclassmembernames class * { 

    java.lang.Class class$(java.lang.String); 

    java.lang.Class class$(java.lang.String, boolean); 
} 

Note: 前提是在壓縮階段沒有被洗掉掉,這里相當于使用了修飾符 allowshrinking

-keepclasseswithmembernames class specification

-keepclasseswithmembers,allowshrinking class specification 的簡寫

指定要保留名稱的類和類成員,條件是所有指定的類成員都存在于收縮階段之后,

例如,可能希望保留所有本機方法名稱和類別的名稱,以便處理的代碼仍可以與本機庫代碼鏈接,完全沒有使用的本地方法仍然可以被洗掉,

如果使用了一個類檔案,但它的本地方法都不是,它的名字仍然會被混淆,只有在混淆時才適用,

-keepclasseswithmembernames,includedescriptorclasses class * { 

    native <methods>; 

} 

Note: 前提是在壓縮階段沒有被洗掉掉,這里相當于使用了修飾符 allowshrinking

-printseeds [filename]

指定詳盡列出由各種-keep選項匹配的類和類成員,串列列印到標準輸出或給定檔案,該串列可用于驗證是否真的找到了預期的類成員,尤其是在使用通配符的情況下,

例如,您可能想要列出您保存的所有應用程式或所有小程式,

參考上面說的 seeds.txt

-whyareyoukeeping class specification

指定列印詳細資訊,說明為什么給定的類和類成員正在壓縮步驟中,

如果想知道為什么某些給定元素出現在輸出中,這會很有用,

一般來說,可能有很多不同的原因,

此選項為每個指定的類和類成員列印最短的方法鏈到指定的種子或入口點,

在當前的實施中,列印出的最短鏈有時可能包含回圈扣除 - 這些并不反映實際收縮程序,

如果指定了 -verbose 選項,則跟蹤包括完整的欄位和方法簽名,只適用于壓縮,

壓縮規則

-dontshrink

指定不被壓縮的類檔案,

默認情況下壓縮是開啟的,除了用各種用 keep 選項直接或間接用到的類或類成員,其他的都會被移除,

壓縮步驟通常在優化之后,因為某些優化可能會打開已經洗掉的類或類成員,

-printusage [filename]

指定列出移除的死代碼,該串列列印到標準輸出或給定檔案,

參考上面說的 usage.txt

例如,您可以列出應用程式的未使用代碼,只適用于壓縮,

優化規則

-dontoptimize

指定不優化輸入類檔案,默認情況下,優化已啟用;所有方法都在位元組碼級別進行了優化

-optimizationpasses n

指定要執行的優化傳遞的數量,

默認情況下,執行一次傳遞,多次通行可能會導致進一步的改進,如果在優化后沒有找到改進,則優化結束,只適用于優化,

混淆規則

-dontobfuscate

指定不混淆輸入的類檔案,

默認情況下,混淆是開啟的,類和類成員會被改成新的短隨機名稱,除了各種-keep選項列出的名稱外,

內部屬性對于除錯很有用,例如源檔案名,變數名和行號被洗掉,

-printmapping [filename]

指定將舊名稱映射到已重命名的類和類成員的新名稱的映射,映射列印到標準輸出或給定檔案,

例如,它是后續增量混淆所必需的,或者如果想再次理解混淆的堆疊跟蹤,只有在混淆時才適用,

參考 上面說的 mapping.txt,

-useuniqueclassmembernames

指定將相同的混淆名稱分配給具有相同名稱的類成員,并將不同混淆名稱分配給名稱不同的類成員(對于每個給定的類成員簽名),

沒有這個選項,更多的類成員可以被映射到相同的短名稱,比如'a','b'等等,

這個選項因此稍微增加了結果代碼的大小,但是它確保了保存的混淆名稱映射總是可以在隨后的增量混淆步驟中受到尊重,

例如,考慮兩個不同的介面,它們包含具有相同名稱和簽名的方法,如果沒有此選項,這些方法可能會在第一個混淆步驟中獲取不同的混淆名稱,

如果添加了包含實作兩個介面的類的補丁程式,則ProGuard必須在增量混淆步驟中為這兩種方法強制執行相同的方法名稱,

原始模糊代碼已更改,以保持結果代碼的一致性,在最初的混淆步驟中使用此選項,這種重命名將永遠不是必需的,

該選項僅適用于混淆,

實際上,如果計劃執行增量混淆,則可能希望完全避免壓縮和優化,因為這些步驟可能會洗掉或修改部分代碼,這些代碼對于以后的添加至關重要,

-dontusemixedcaseclassnames

指定在混淆時不生成混合大小寫的類名,

默認情況下,混淆的類名可以包含大寫字符和小寫字符的混合,

創建的這個完全可接受和可用的jars 只有在不區分大小寫的檔案系統(比如Windows)的平臺上解壓縮jar時,解壓縮工具可能會讓類似命名的類檔案相互覆寫,

解壓縮后自毀的代碼!真正想在Windows上解壓他們的jar的開發人員可以使用這個選項來關閉這種行為,

混淆的jars會因此變得稍大,

只有在混淆時才適用,

-keeppackagenames [package_filter]

指定不混淆給定的軟體包名稱,

可選過濾器是包名稱的逗號分隔串列,包名可以包含?,*和**通配符,并且它們可以在!否定器,只有在混淆時才適用,

-flattenpackagehierarchy [package_name]

指定將所有重命名的軟體包重新打包,方法是將它們移動到單個給定的父軟體包中,如果沒有引數或空字串(''),程式包將移動到根程式包中,

該選項是進一步混淆軟體包名稱的一個示例,它可以使處理后的代碼更小,更難理解,

只有在混淆時才適用,

-repackageclasses [package_name]

指定將所有重命名的類檔案重新打包,方法是將它們移動到單個給定的包中,沒有引數或者使用空字串(''),該軟體包將被完全洗掉,

該選項將覆寫 -flattenpackagehierarchy 選項,

這是進一步模糊軟體包名稱的另一個例子,

它可以使處理后的代碼更小,更難理解,

其不推薦使用的名稱是-defaultpackage,

只有在混淆時才適用,

警告:如果在別處移動它們,則在其包目錄中查找資源檔案的類將不再正常作業,如有疑問,請不要使用此選項,以免觸及包裝,

-keepattributes [attribute_filter]

指定要保留的任何可選屬性,這些屬性可以用一個或多個-keepattributes指令來指定,

可選過濾器是Java虛擬機和ProGuard支持的屬性名稱的逗號分隔串列,

屬性名稱可以包含?,*和**通配符,并且可以在之前加上!否定器,

例如,在處理庫時,您至少應保留Exceptions,InnerClasses和Signature屬性,

您還應該保留SourceFile和LineNumberTable屬性以生成有用的混淆堆疊跟蹤,

最后,如果你的代碼依賴于它們,你可能需要保留注釋,

只有在混淆時才適用,

# 保留Annotation不混淆

-keepattributes *Annotation*,InnerClasses



# 避免混淆泛型

-keepattributes Signature



# 拋出例外時保留代碼行號

-keepattributes SourceFile,LineNumberTable



-keepparameternames

指定保留所保存方法的引數名稱和型別,

該選項實際上保留了除錯屬性LocalVariableTable和LocalVariableTypeTable的修剪版本,

處理庫時它可能很有用,

一些IDE可以使用這些資訊來幫助使用該庫的開發人員,

例如工具提示或自動完成,

只有在混淆時才適用,

-renamesourcefileattribute [string]

指定要放入類檔案的SourceFile屬性(和SourceDir屬性)中的常量字串,請注意,該屬性必須首先出現,所以它也必須使用-keepattributes指令明確保留,

例如,您可能希望讓處理過的庫和應用程式生成有用的混淆堆疊跟蹤,

只有在混淆時才適用

預校驗 規則

-dontpreverify

指定不預先驗證已處理的類檔案,

默認情況下,如果類檔案針對Java Micro Edition或Java 6或更高版本,則會對其進行預驗證,

對于Java Micro Edition,需要進行預驗證,因此如果指定此選項,則需要在處理的代碼上運行外部預驗證程式,

對于Java 6,預驗證是可選的,但從Java 7開始,它是必需的,

只有在最終對Android時,它才不是必需的,因此您可以將其關閉以縮短處理時間,

-android

指定已處理的類檔案針對Android平臺,然后ProGuard確保一些功能與Android兼容,

例如,如果您正在處理Android應用程式,則應該指定此選項,

一般規則

-verbose

指定在處理期間寫出更多資訊,如果程式以例外終止,則此選項將列印出整個堆疊跟蹤,而不僅僅是例外訊息,

-dontnote [class_filter]

指定不列印有關配置中可能的錯誤或遺漏的注釋,

例如類名中的拼寫錯誤或缺少可能有用的選項,

可選的過濾器是一個正則運算式;

ProGuard不列印有關匹配名稱的類的注釋,

-dontwarn [class_filter]

指定不警告有關未解決的參考和其他重要問題,

可選的過濾器是一個正則運算式; ProGuard不列印關于具有匹配名稱的類的警告,忽略警告可能是危險的,

例如,如果處理確實需要未解決的類或類成員,則處理后的代碼將無法正常作業,

只有在你知道自己在做什么的情況下才使用此選項!

-ignorewarnings

指定列印任何關于未解決的參考和其他重要問題的警告,但在任何情況下都繼續處理,忽略警告,

忽略警告可能是危險的,

例如,如果處理確實需要未解決的類或類成員,則處理后的代碼將無法正常作業,

只有在知道自己在做什么的情況下才使用此選項!

-printconfiguration [filename]

指定使用包含的檔案和替換的變數寫出已決議的整個配置,結構列印到標準輸出或給定檔案,

這對于除錯配置或將XML配置轉換為更易讀的格式有時會很有用,

-dump [filename]

指定在任何處理后寫出類檔案的內部結構,結構列印到標準輸出或給定檔案,

例如,可能希望寫出給定jar檔案的內容,而不進行處理,

參考上面說的 dump.txt,

-addconfigurationdebugging

指定用除錯陳述句來處理已處理的代碼,這些陳述句顯示缺少ProGuard配置的建議,

如果處理后的代碼崩潰,那么在運行時獲得實用提示可能非常有用,因為它仍然缺少一些反射配置,

例如,代碼可能是使用GSON庫序列化類,可能需要一些配置,通常可以將控制臺的建議復制/粘貼到組態檔中,

警告:不要在發行版本中使用此選項,因為它將混淆資訊添加到已處理的代碼中,

keep 選項修飾符

includedescriptorclasses

指定-keep選項所保存的方法和欄位的型別描述符中的任何類也應保存,

在保留方法名稱時,這通常很有用,以確保方法的引數型別不會重命名,他們的簽名保持完全不變,并與本地庫兼容,

includecode

指定保持-keep選項所保存的欄位的方法的代碼屬性也應該保留,即可能未被優化或模糊處理,這對于已優化或混淆的類通常很有用,以確保在優化期間未修改其代碼,

allowshrinking

指定-keep選項中指定的入口點可能會壓縮,即使必須另外保留它們,

也就是說,可以在壓縮步驟中洗掉入口點,但如果它們是必需的,則它們可能未被優化或混淆,

allowoptimization

指定-keep選項中指定的入口點可能會被優化,即使它們必須另外保存,

也就是說,入口點可能會在優化步驟中被更改,但它們可能不會被洗掉或混淆,

此修飾符僅用于實作不尋常的要求,

allowobfuscation

指定在-keep選項中指定的入口點可能會被混淆,即使它們必須另外保存,

也就是說,入口點可能在混淆步驟中被重命名,但它們可能不會被洗掉或優化,

此修飾符僅用于實作不尋常的要求,

keep 選項之間的關系

壓縮和混淆的各種-keep選項起初看起來有點混亂,但實際上它們背后有一個模式,

下表總結了它們之間的關系:

內容 被洗掉或重命名 被重命名
類和類成員 -keep -keepnames
只有類成員 -keepclassmembers -keepclassmembernames
類和類成員,參考成員存在 -keepclasseswithmembers -keepclasseswithmembernames

如果指定了一個沒有類成員的類,ProGuard只保留該類及其無引數的建構式作為入口點,它可能仍會洗掉,優化或混淆其他班級成員,

如果指定了一個方法,則ProGuard僅將該方法作為入口點進行保存,其代碼可能仍會進行優化和調整,

類規范

類規范是類和類成員(欄位和方法)的模板,它用于各種-keep選項和-assumenosideeffects選項中,相應的選項僅適用于與模板匹配的類和類成員,

模板的設計看起來非常類似于Java,并為通配符進行了一些擴展,為了理解語法,你應該看看這些例子,但這是對一個完整的正式定義的嘗試:

[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname

    [extends|implements [@annotationtype] classname]

[{

    [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |

      (fieldtype fieldname);

    [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |

      <init>(argumenttype,...) | classname(argumenttype,...) |(returntype methodname(argumenttype,...));

    [@annotationtype] [[!]public|private|protected|static ... ] *;

    ...

}]

方括號 “[]” 表示其內容是可選的,

省略號點“...”表示可以指定任意數量的前述專案,

垂直條“|”劃定了兩種選擇,

非粗體括號“()”只是將屬于規范的部分組合在一起,

縮進嘗試澄清預期的含義,但在實際組態檔中,空白是不相關的,

class關鍵字指的是任何介面或類,interface 關鍵字限制匹配介面類, enum關鍵字限制匹配列舉類,在 interface 或 enum 關鍵字前加上!將匹配限制為不是介面或列舉的類,

每一個類名字都必須是完全限定名,例如 java.lang.String 內部類用美元符號“$”分隔,例如java.lang.Thread$State,類名可以被指定為包含以下通配符的正則運算式:

  • ? 匹配類名稱中的任何單個字符,但不匹配包分隔符,例如 "com.example.Test?" 可以匹配 "com.example.Test1" 和 "com.example.Test2" 但不能匹配 "com.example.Test12"

  • * 匹配不包含包分隔符的類名的任何部分,例如 "com.example.*Test*" 能夠匹配 "com.example.MyTest" 和 "com.example.MyTestProduct" 但不能匹配 "com.example.mxc.MyTest" 或者 "com.example.*" 能夠匹配 "com.example" 但不能匹配 "com.example.mxc"

  • ** 匹配類名稱的任何部分,可能包含任意數量的包分隔符,例如,"**.Testz" 匹配除根包以外的所有包中的所有Test類,或者,"com.example.**" 匹配 "com.example" 中的所有類及其子包,

  • <n> 在相同的選項中匹配第n個匹配的通配符,例如,"com.example.*Foo<1>" 匹配"com.example.BarFooBar",

為了獲得更多的靈活性,類名實際上可以是逗號分隔的類名串列,可以加!,這個符號看起來不是很像java,所以應該適度使用,

為了方便和向后兼容,類名*指任何類,而不考慮它的包,

  • extends 和 **implements ** 通常用來限制使用通配符的類,目前他們是一樣的,他們的意思是 只有繼承或實作了給定類的類才有資格,給定的類本身不包含在這個集合中,如果需要,應該在單獨的選項中指定,

  • @ 可用于將類和類成員限制為使用指定的注釋型別進行注釋的類,annotationtype 就像類名一樣被指定,

  • 除了方法引數串列不包含引數名稱外,欄位和方法在Java中的定義非常類似(就像在javadoc和javap等其他工具中一樣),這些規范還可以包含以下通配符通配符:

通配符 意義
<init> 匹配任何構造方法
<fields> 匹配任何欄位
<methods> 匹配任何方法
* 匹配任何方法和欄位

請注意,上述通配符沒有回傳型別,只有<init>通配符才有一個引數串列,

欄位和方法也可以使用正則運算式來指定,名稱可以包含以下通配符:

通配符 意義
? 匹配方法名的任何單個字符
* 匹配方法名的任何部分
<n> 在相同的選項中匹配第n個匹配的通配符

型別可以包含以下通配符

通配符 意義
% 匹配任何原始型別(boolean,int 等,不包含 void)
? 匹配類名中的單個字符
* 匹配類名中的任何部分但不包含包分隔符
** 匹配類名中的任何部分但不包含包分隔符
*** 匹配任何型別(原始型別或者非原始型別,陣列或者非陣列)
--- 匹配任何型別的任意數量的引數
<n> 在相同的選項中匹配第n個匹配的通配符,

請注意,?,*和**通配符永遠不會匹配基本型別,
而且,只有***通配符才能匹配任何維度的陣列型別,

例如,“** get *()”匹配“java.lang.Object getObject()”,但不匹配“float getFloat()”和“java.lang.Object [] getObjects()”,

  • 也可以使用短類名(無包)或使用完整的類名來指定建構式,和Java語言一樣,建構式規范有一個引數串列,但沒有回傳型別,

  • 類訪問修飾符和類成員訪問修飾符通常用于限制通配類和類成員,它們指定必須為成員設定相應的訪問標志以匹配,前面加 "!" 決定相應的訪問標志應該被取消設定,

允許組合多個標志(例如,public static),這意味著必須設定兩個訪問標志(例如 public static ),除非它們有沖突,在這種情況下,至少必須設定其中一個(例如至少public或 protected),

ProGuard支持可能由編譯器設定的其他修飾符 synthetic,bridge和varargs,

參考資料

  • https://www.guardsquare.com/en/proguard/manual/introduction

  • https://www.diycode.cc/topics/380

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/1219.html

標籤:Android

上一篇:Flutter Weekly Issue 66

下一篇:Android 開發技術周報 Issue#292

標籤雲
其他(123570) Java(13369) Python(12729) C(7542) 區塊鏈(7372) JavaScript(7049) 基礎類(6313) AI(6244) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4120) MySQL(4012) Linux(3394) C語言(3288) C++語言(3117) Java相關(2746) 疑難問題(2699) 單片機工控(2479) Web開發(1951) 網絡通信(1793) 數據庫相關(1767) VB基礎類(1755) PHP(1727) 開發(1646) 系統維護與使用區(1617) .NETCore(1586) 基礎和管理(1579) JavaEE(1566) C++(1527) 專題技術討論區(1515) Windows客戶端使用(1484) HtmlCss(1466) ASP.NET(1428) Unity3D(1354) VCL組件開發及應用(1353) HTML(CSS)(1220) 其他技術討論專區(1200) WindowsServer(1192) .NET技术(1165) 交換及路由技術(1149) 語言基礎算法系統設計(1133) WindowsSDKAPI(1124) 界面(1088) JavaSE(1075) Qt(1074) VBA(1048) 新手樂園(1016) 其他開發語言(947) Go(907) HTML5(901) 新技術前沿(898) 硬件設計(872) 區塊鏈技術(860) 網絡編程(857) 非技術版(846) 一般軟件使用(839) 網絡協議與配置(835) Eclipse(790) Spark(750) 下載資源懸賞專區(743)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 如何在同一頁面上向下滾動并顯示章節?

    我正在開發一個單頁網站,當用戶點擊一個特定的按鈕時,應該向下滾動到頁面的另一個部分。
    因為我使用了粘性標題,所以該部分的標題被隱藏在橫幅后面,所以我使用下面的代碼在點擊...

    uj5u.com 2021-10-16 15:31:29 more
  • 是否有辦法在gradle.build檔案中擴展或創建不同的jib配置?

    我正在使用jib插件來為我的springboot應用構建docker鏡像。然而,我希望在我的構建檔案中有一個新的任務,它將呼叫不同的構建jib任務。
    其原因是,根據我在 gradle 中創建的任務,...

    uj5u.com 2021-10-16 15:31:10 more
  • 未生成Apollo目錄

    我在初步實施中遇到了困難。
    我的問題是,下面的構建無法生成apollo目錄。
    用這個gradle(應用程式級別)
    plugins {
    id 'com.android.application'/span>
    id 'kotlin-androi...

    uj5u.com 2021-10-16 15:30:57 more
  • 引數型別'PointerEvent'不能分配給引數型別'PointerDownEvent'。

    最近,我更新到了flutter 2.5和最新的androids studio,并試圖將我的flutter專案編譯到android設備上。Android studio向我拋出了下面的錯誤。如果我在終端寫flutter run,編譯到...

    uj5u.com 2021-10-16 15:30:43 more
  • 如何使用then()并在Cypress中獲取數值

    我有一個span元素,它的值是2,我想檢查它的值是否大于0,但在網上查詢并實施了所有方法后,它沒有作業......
    下面是我記錄$span時的控制臺> 2</span>
    </div>

    cy.get(" .badge....

    uj5u.com 2021-10-16 15:28:04 more
  • 使用Kotlin中的高階函式在trycatch中包裹一個函式

    我有一組函式,它們有不同的簽名和引數,但它們都可以拋出一個例外。我想寫一個行內函式,將一個函式作為引數,在一個try-catch塊中呼叫這個函式,并回傳某些型別的例外,而不是在每個...

    uj5u.com 2021-10-16 15:25:37 more
  • 如何在運行時指定通用型別?

    我有一段這樣的代碼:
    var senders = new List<MessageSenderBase<object>();
    senders.Add(new MessageSender1<MessageType1>())。

    其中MessageSender1<MessageType1>是...

    uj5u.com 2021-10-16 15:25:20 more
  • Dart中非通用類中的通用欄位/方法

    在 Dart 中,是否有可能像 Java 一樣,在非通用類中擁有通用方法?
    我使用Getx進行狀態管理、DI&路由,但這個問題并不是針對Getx或Flutter的。
    我有AuthService,它處理認證和設定登...

    uj5u.com 2021-10-16 15:25:12 more
  • Typecript中通用的"設定屬性"函式

    我遇到了一個問題,我通過創建下面的函式簡化了這個問題: 我遇到了一個問題。
    function setProperty< T extends Record<string, string>> (obj: T, key: keyof T) {
    obj[key...

    uj5u.com 2021-10-16 15:24:55 more
  • 如何讓我的代碼從乞討開始,而無需用戶重新啟動程式,VisualBasic

    這是我的代碼Module vbModule Sub Main() 'Welcome Text Console.WriteLine("Welcome To Squidwards Password Checker") 'Length Check 'C...

    uj5u.com 2021-10-16 15:11:49 more