一、为什么要国际化
全球化的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。
然后在文件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 (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
native ;
}
## 保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在
-keepclasseswithmembernames class * {
public (android.content.Context);
}
-keepclasseswithmembernames class * {
public (android.content.Context, int);
}
# 保留自定义控件(继承自View)不能被混淆
-keep public class * extends android.view.View {
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (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 (android.content.Context);
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public (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 ;
!private ;
!private ;
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 ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
(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 *;
}
感谢优秀的你跋山涉水看到了这里,不如关注下让我们永远在一起!