1. 楔子
json資料的解析是這個星球上99%的Android程式猿都會遇到的問題,而其中的大部分,都會使用
Gson
,故事就是從這裡發生……
2. 一開始
通常json資料是從服務端取的,典型的一段json字元串如下:
{ "name": "老張","age": }
一般程式猿們會在本地寫一個實體類與之對應,比如資料類
Person
:
public class Person {
private String name;
private int age;
//……省略中間get/set
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
然後就可以愉快地使用Gson來進行解析啦,像這樣:
Person someone = new Gson().fromJson(jsonString, Person.class);
Log.d("TAG", "someone: " + someone.toString());
一切順利,程式猿們得到輸出:
Person{ name=’老張’,age=18}
然後他們就愉快地下班去玩耍了。
3. 後來,他們開啟了proguard
意外就是從這裡開始的,開啟了proguard之後,debug運作的程式一切如常,但是proguard之後輸入的apk包,在安裝之後的輸出變成了這樣:
Person{ name=’null’,age=0}
——解析出錯了。
(算了,我編不下去了,從現在開始使用第一人稱……)
4. 問題所在
經過反複排查,發現問題就是由proguard引起的。
我們知道,proguard的過程會執行壓縮、混淆、去除無用代碼等操作。而其中的混淆、去除無用代碼的過程,對于使用了
反射
技術的代碼來說,都是有可能受到影響的。
不幸的是:Gson.fromJson()就是基于反射來實作的。
通過反編譯apk包,發現我們寫的Person類,在沒有被保護的情況下,會被proguard成類似這個樣子的:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TUldnQYl1bs52Y5ZUbZZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TO2MDO0cTMxIjNwQDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
可以看到,類名、字段名都改成了a/b這樣的名字。
這樣在使用Gson的時候,它嘗試去序列化的字段就會變成了a/b,而不是之前的name/age了。
——那還反射個毛啊!
5. 解決
知道問題了,自然也就很好解決了。
方案是在proguard規則中,把Person類給保護起來。
5.1 proguard檔案編輯
在
proguard-rules.pro
檔案中添加規則(也可能是其他檔案,具體見gradle檔案中的定義),把資料類保護起來即可。
具體來說,可以直接保護目前類:
-keep class com.xxx.beans.Person {*;}
也可以保護整個包:
-keep class com.xxx.beans.* {*;}
還可以給Person添加一個對
Serializable
的實作,然後保護所有的
Serializable
-keep class * implements java.io.Serializable {*; }
5.2 @Keep注解
另外,項目如果引用了
注解支援庫
,那麼給Person類加上
@Keep
的注解也可以防止它被混淆。
@Keep
public class Person {/*省略*/}
OK,加上保護之後,重新打包。
反編譯這時的apk,可以看到,字段Person類沒有再被混淆了:
然後安裝、運作,果然一切正常了。
Person{ name=’老張’,age=18}
完美。
一些連結
工具:https://github.com/google/android-classyshark
壓縮代碼和資源:https://developer.android.com/studio/build/shrink-code.html
proguard相關問題排查:https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/troubleshooting.html
關于作者:https://github.com/barryhappy