1.6.1 應用國際化與代碼混淆

一、為什么要國際化

全球化的Internet需要全球化的軟件,全球化的軟件自然也就意味著軟件自身能夠適用于不同的地去和市場,Android應用自然也不例外。

引入國際化的目的是為了在不改變程序自身邏輯功能的前提下提供自適應的、更友好的用戶界面,這樣我們無需為不同語言的國家或地區單獨編寫一套程序,更何況同一個應用程序的代碼邏輯幾乎都是一模一樣的,僅僅為了更改語言而單獨編寫編譯太不值得,這種情況下就用到了我們的應用國際化。

二、為Android應用提供國際化資源

為Android提供國際化不必像Java那樣需要將程序中的標簽、提示信息等放在資源文件中,程序需要支持那些國家、語言環境就需要提供相應的資源文件,因為Android本身就采用了XML資源文件來管理所有的字符串消息,我們只需為各消息提供不同國家、語言對應的內容即可。

例如:如果我們希望某個應用支持簡體中文、美式英語兩種語言環境,那么就在Res目錄下添加values-zh-rCN、values-en-rUS兩個目錄即可。

如果希望應用程序的圖片也能隨國家、語言環境改變,那么為Drawable目錄添加幾個不同的語言國家版本就行了。

如果還需為Drawable目錄按分辨率提供文件夾,則可以再文件夾后面追加分辨率信息,比如drawable-zh-rCN-mdpi、drawable-zh-rCN-hdpi等。

如下圖即是AndroidStudio中為應用進行國際化操作后的視圖,甚至在這里我們可以看到該應用還為不同手機不同的Android版本、不同的分辨率提供了不同樣式、不同尺寸的支持:


應用國際化

三、常見的國際化文件夾名稱

  • zh_cn: 簡體中文
  • zh_hk: 繁體中文(中國香港)
  • zh_tw: 繁體中文(中國臺灣地區)
  • en-hk: 英語(香港)
  • en_us: 英語(美國)
  • en_gb: 英語(英國)
  • en_ww: 英語(全球)
  • ja_jp: 日語(日本)
  • ko_kr: 韓文(韓國)

四、代碼混淆

我們都知道Java是一種跨平臺的、解釋型語言,Java 源代碼編譯成中間”字節碼”存儲于 class 文件中。

由于跨平臺的需要,Java 字節碼中包括了很多源代碼信息,如變量名、方法名,并且通過這些名稱來訪問變量和方法,這些符號帶有許多語義信息,很容易被反編譯成 Java 源代碼。為了防止這種現象,我們可以使用 Java 混淆器對 Java 字節碼進行混淆。

混淆就是對發布出去的程序進行重新組織和處理,使得處理后的代碼與處理前代碼完成相同的功能,而混淆后的代碼很難被反編譯,即使反編譯成功也很難得出程序的真正語義。

被混淆過的程序代碼,仍然遵照原來的檔案格式和指令集,執行結果也與混淆前一樣,只是混淆器將代碼中的所有變量、函數、類的名稱變為簡短的英文字母代號,在缺乏相應的函數名和程序注釋的況下,即使被反編譯,也將難以閱讀。

同時混淆是不可逆的,在混淆的過程中一些不影響正常運行的信息將永久丟失,這些信息的丟失使程序變得更加難以理解。

所以為什么需要代碼混淆呢?
原因很簡單,我們的apk很容易被反編譯出來,我們寫的代碼都會被看到,因此我們需要在編譯過程中對代碼進行一定程度的混淆,使得別人不能反編譯不出我們的代碼。

另外混淆器的作用不僅僅是保護代碼,它也有精簡編譯后程序大小的作用。由于以上介紹的縮短變量和函數名以及丟失部分信息的原因, 編譯后 jar 文件體積大約能減少25% ,這對當前費用較貴的無線網絡傳輸是有一定意義的。

下面介紹下具體混淆過程:
  • 新建一個項目,Android Studio默認關閉代碼混淆開關,在build.gradle文件中,如下圖所示的minifyEnabled 開關,因此如果需要混淆代碼,需將false改為true。


    minifyEnabled開關
  • 然后在文件proguard-rules.pro添加具體混淆規則,下面是常見的的proguard.cfg配置項:

# 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 *;
}

# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
-renamesourcefileattribute SourceFile

    -optimizationpasses 5           # 指定代碼的壓縮級別
    -dontusemixedcaseclassnames     # 混淆時不使用大小寫混合,混淆后的類名為小寫 windows下的同學還是加入這個選項吧(windows大小寫不敏感)
    -ignorewarnings     # 忽略警告
    -dontpreverify      # 不做預校驗(Android不需要preverify,去掉這一步可以加快混淆速度)
    -verbose            # 混淆時是否記錄日志(混淆后會生成映射文件,包含有類名->混淆后類名的映射關系,然后使用printmapping指定映射文件的名稱)
    -printmapping priguardMapping.txt
    -dontskipnonpubliclibraryclasses        # 指定不去忽略非公共的庫的類(默認跳過,有些情況下編寫的代碼與類庫中的類在同一個包下,并且持有包中內容的引用,此時就需要加入此條聲明)
    -dontskipnonpubliclibraryclassmembers   # 指定不去忽略非公共的庫的類的成員
    -dontoptimize       # 優化 不優化輸入的類文件
    -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*    #混淆時所采用的算法(后面的參數是一個過濾器,這個過濾器是谷歌推薦的算法,一般不改變)

    -keepattributes *Annotation*    # 保護代碼中的Annotation不被混淆(這在JSON實體映射時非常重要,比如fastJson)
    -keepattributes Signature       # 避免混淆泛型,如果混淆報錯建議關掉

    -keepattributes SourceFile,LineNumberTable  # 拋出異常時保留代碼行號

    # 保持哪些類不被混淆(不需要混淆系統組件等)
    -keep public class * extends android.app.Fragment
    -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
    -keep public class com.android.vending.licensing.ILicensingService
    # 如果有引用v4包可以添加下面這兩行
    -keep public class * extends android.support.v4.app.Fragment
    -keep public class com.null.test.ui.fragment.** {*;}

    -keep class com.wgh.willflowaicollection.R{*;}
    -keep class com.wgh.willflowaicollection.MyApplication{*;}
    -keep class com.wgh.willflowaicollection.ui.MainActivity
    -keep class com.wgh.willflowaicollection.ui.SplashActivity
    -keep class com.wgh.willflowaicollection.util.PreDefine
    -keep class com.wgh.willflowaicollection.helper.**{*;}
    -keep class com.wgh.willflowaicollection.greendao.**{*;}
    -keep class com.wgh.willflowaicollection.service.**{*;}
    -keep class com.wgh.willflowaicollection.model.**{*;}

    # 保留Activity中的方法參數是view的方法,
    # 從而我們在layout里面編寫onClick就不會影響
    -keepclassmembers class * extends android.app.Activity {
        public void * (android.view.View);
    }

    #apk 包內所有 class 的內部結構
    -dump class_files.txt
    #未混淆的類和成員
    -printseeds seeds.txt
    #列出從 apk 中刪除的代碼
    -printusage unused.txt
    #混淆前后的映射
    -printmapping mapping.txt

    ########記錄生成的日志數據,gradle build時 在本項目根目錄輸出-end######

    #####混淆保護自己項目的部分代碼以及引用的第三方jar包library#######
    #-libraryjars libs/umeng-analytics-v5.2.4.jar

    #三星應用市場需要添加:sdk-v1.0.0.jar,look-v1.0.1.jar
#    -libraryjars libs/sdk-v1.0.0.jar
#    -libraryjars libs/look-v1.0.1.jar

    #如果不想混淆 keep 掉
#    -keep class com.lippi.recorder.iirfilterdesigner.** {*; }
    #友盟
#    -keep class com.umeng.**{*;}
    #項目特殊處理代碼

    #忽略警告
    -dontwarn com.lippi.recorder.utils**
    #保留一個完整的包
    -keep class com.lippi.recorder.utils.** {
        *;
     }

    -keep class com.lippi.recorder.utils.AudioRecorder{*;}

    #如果引用了v4或者v7包
    -dontwarn android.support.**

    ####混淆保護自己項目的部分代碼以及引用的第三方jar包library-end####

    -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*(...);
    }

    # 保留所有的本地native方法不被混淆
    -keepclasseswithmembernames class * {
        native <methods>;
    }

    ## 保護指定的類和類的成員,但條件是所有指定的類和類成員是要存在
    -keepclasseswithmembernames class * {
        public <init>(android.content.Context);
    }
    -keepclasseswithmembernames class * {
        public <init>(android.content.Context, int);
    }

    # 保留自定義控件(繼承自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*(***);
        *** get* ();
    }

    # 保持自定義控件類不被混淆
    -keepclassmembers class * extends android.widget.ImageView {
        public void *(android.view.View);
    }
    -keepclasseswithmembers class * {
        public <init>(android.content.Context);
    }
    # 保持自定義控件類不被混淆
    -keepclasseswithmembers class * {
        public <init>(android.content.Context, android.util.AttributeSet);
    }
    # 保持自定義控件類不被混淆
    -keepclasseswithmembers class * {
        public <init>(android.content.Context, android.util.AttributeSet, int);
    }

    # 保留Parcelable序列化的類不能被混淆
    -keep class * implements android.os.Parcelable {
      public static final android.os.Parcelable$Creator *;
    }
    # 保持 Serializable 不被混淆
    -keepnames class * implements java.io.Serializable

    # 保持 Serializable 不被混淆并且 enum 類也不被混淆
    -keepclassmembers class * implements java.io.Serializable {
        static final long serialVersionUID;
        private static final java.io.ObjectStreamField[] serialPersistentFields;
        !static !transient <fields>;
        !private <fields>;
        !private <methods>;
        private void writeObject(java.io.ObjectOutputStream);
        private void readObject(java.io.ObjectInputStream);
        java.lang.Object writeReplace();
        java.lang.Object readResolve();
    }

    # 保持枚舉 enum 類不被混淆 如果混淆報錯,建議直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可
    -keepclassmembers enum * {
      public static **[] values();
      public static ** valueOf(java.lang.String);
    }

    -keepclassmembers class * {
        public void *ButtonClicked(android.view.View);
    }

    # 對R文件下的所有類及其方法,都不能被混淆
    -keepclassmembers class **.R$* {
        *;
    }

    # 對于帶有回調函數onXXEvent的,不能混淆
    -keepclassmembers class * {
        void *(**On*Event);
    }

    #移除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(...);
    }

    #如果用用到Gson解析包的,直接添加下面這幾行就能成功混淆,不然會報錯。
    #gson
#    -libraryjars libs/gson-2.3.jar
    -keepattributes Signature
    # Gson specific classes
    -keep class sun.misc.Unsafe { *; }
    # Application classes that will be serialized/deserialized over Gson
    -keep class com.google.gson.**{*;}

#    -libraryjars libs/VerBank-CSTSv3-ClientAPI.jar  #這里這樣寫雖然說是不混淆,但我測試發現,有些類還是混淆了,所以才會寫下面的保持類不混淆的寫法

    -dontwarn allone.**
    -dontnote allone.**
    -keep class allone.**{*;}   #注意**和**{*;}的區別, -keep class allone.**這樣只能保留一層文件夾,如果下面有好多文件夾需要保留,需要攜程**{*;}這是我實驗出來,由于缺少必要的資料,這個坑試了好久才發現.

    -keep class android.app.enterprise.**{*;}
    -keep class android.**{*;}
    -keep class com.android.**{*;}
    -keep class com.google.**{*;}
    -keep class com.google.code.**{*;}
    -keep class com.google.android.**{*;}
    -keep class com.nostra13.**{*;}
    -keep class com.mcxiaoke.**{*;}
    -keep class com.squareup.**{*;}
    -keep class com.jakewharton
    -keep class org.**{*;}
    -keep class de.**{*;}
    -keep class com.github.bumptech.**{*;}
    -keep class com.sun.**{*;}
    -keep class junit
    -keep class com.jakewharton

    -keep class  com.baidu.**{ *; }
    -keep class  org.apache.commons.**{ *; }
    -keep class  com.qualcomm.**{*;}
    -keep class  javax.microedition.khronos.**{*;}
    -keep class  org.codeaurora.**{*;}
    -keep class  com.hianalytics.**{ *; }
    -keep class  huawei.android.**{ *; }
    -keep class  it.sauronsoftware.**{ *; }
    -keep class  com.xiaomi.**{ *; }
    -keep class  org.apache.**{ *; }
    -keep class  maven.com.squareup.**{ *; }
    -keep class  com.zhy.http.**{ *; }
    -keep class  okio.**{ *; }
    -keep class  com.microsoft.**{ *; }
    -keep class  org.etsi.uri.**{ *; }
    -keep class  openxmlformats.**{ *; }
    -keep class  w3.**{ *; }
    -keep class  schemaorg_apache_xmlbeans.**{ *; }
    -keep class  com.google.zxing.**{ *; }
    -keep class  com.google.zxing.**{ *; }

    #greendao
    -keep class de.greenrobot.daogenerator.**{ *; }
    -keep class de.greenrobot.dao.**{*;}
    -keepclassmembers class * extends de.greenrobot.dao.AbstractDao {
        public static java.lang.String TABLENAME;
    }
    -keep class **$Properties

    # EventBus
    -keepclassmembers class ** {
        @org.greenrobot.eventbus.Subscribe <methods>;
    }
    -keep enum org.greenrobot.eventbus.ThreadMode { *; }
    -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
        <init>(Java.lang.Throwable);
    }

    -keepattributes Signature,InnerClasses
    -keepclasseswithmembers class io.netty.** {
        *;
    }
    -dontwarn io.netty.**
    -dontwarn sun.**

    -keep public class [your_pkg].R$*{
        public static final int *;
    }

感謝優秀的你跋山涉水看到了這里,不如關注下讓我們永遠在一起!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • 聲明 這篇文章更多的是做一個整理,內容來自于ProGuard官方文檔以及各種博客等,相關文章的鏈接在參考目錄里,感...
    夷陵小祖閱讀 3,705評論 0 23
  • 以下適用場景:eclipse中的maven項目 為什么將這兩個問題放在一起講?因為反編譯才能將代碼混淆的效果直接展...
    小呀么小黃雞閱讀 1,218評論 0 2
  • 什么是代碼混淆 代碼混淆就是將代碼中的各種元素,如變量,方法,類和包的名字改寫成無意義的名字,增加項目反編譯后被讀...
    蝸牛家族史閱讀 5,193評論 1 4
  • 前言 什么是代碼混淆 代碼混淆,是指將計算機程序的代碼,轉換成一種功能上等價,但是難于閱讀和理解的形式的行為。 代...
    linyb極客之路閱讀 1,909評論 0 1
  • ProGuard是一個開源的Java代碼混淆器。它可以混淆Android項目里面的java代碼,但是不能混淆資源,...
    磨礪營IT閱讀 454評論 0 0