google gson 使用proguard混淆程式碼注意事項

NO IMAGE

這裡不介紹怎樣開啟程式碼混淆功能。具體方法可以參照:

http://developer.android.com/tools/help/proguard.html

http://proguard.sourceforge.net/#manual/usage.html

主要介紹混淆使用 google gson的程式碼的時候需要注意的問題和解決方法。

在使用Android Studio 自帶的Proguard去混淆程式碼的時候。程式碼混淆可以通過,但是執行的時候出現下面錯誤:

07-13 14:25:41.549  22817-22817/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.project.mocha_patient, PID: 22817
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.project.mocha_patient.login.c.a(Unknown Source)
at com.project.mocha_patient.network.p.a(Unknown Source)
at com.project.mocha_patient.network.p.onPostExecute(Unknown Source)
at android.os.AsyncTask.finish(AsyncTask.java:636)
at android.os.AsyncTask.access$500(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:653)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

最後定位程式碼位置在於:

public void ServerResponse(HttpTaskResult ret) {
SignResponseData obj = JsonUtils.objectFromJson (ret.getResponesMsg(), SignResponseData.class);
if (obj == null){
Toast.makeText(LoginActivity.this, "Server replied the wrong data, please check.", Toast.LENGTH_SHORT).show();
return ;
}
if (!obj.getStatus().equals(MCSConstants.POST_SUCCESSFUL)){ //異常位置
Toast.makeText(LoginActivity.this, "Server Error: "   obj.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
....
}

除錯程式碼可以顯示obj為非null。但是內部成員都為 null

public final class JsonUtils {
private static Gson sGson = new Gson();
public static String jsonStringFromObject(Object object) {
return sGson.toJson(object);
}
public static <T> T objectFromJson(String json, Class<T> clz) {
try {
return sGson.fromJson(json, clz);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public static<T> T objectFromJson(InputStream in, Class<T> clz) {
try {
JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
T object = sGson.fromJson(reader, clz);
reader.close();
return object;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

SignResponseData.java

public final class SignResponseData {
private String regType;
private String token;
private String message;
private String status;
...
}

分析原因:

由於gson使用對映的方法找到SignResponseData.class中對應的成員變數名稱,將json格式的的欄位對應的值賦值給SignResponseData類中對應的成員變數。

但是由於經過proguard後 SignResponseData內部的成員變數名稱發生了變化。已經無法找到json欄位中對應的類成員變數。所以導致所有生成的SignResponseData成員變數值都為null 。

解決方法 1:

在proguard-rules.pro中加入下面規則。 對SignResponseData.java的所有private 物件不進行obfuscation。

##---------------Begin: proguard configuration for Gson ----------
-keep public class com.google.gson.**
-keep public class com.google.gson.** {public private protected *;}
-keepattributes Signature
-keepattributes *Annotation*
-keep public class com.project.mocha_patient.login.SignResponseData { private *; }
##---------------End: proguard configuration for Gson ----------

解決方法2:

在SignResponseData.java中,將所有被gson使用的變數都加下面宣告,這樣gson就可以識別對應的變數。

@SerializedName("name")

public final class SignResponseData {
@SerializedName("regType")
private String regType;
@SerializedName("token")
private String token;
@SerializedName("message")
private String message;
@SerializedName("status")
private String status;
...
}

如果要保留的類是內部類。可以使用下面宣告:

-keep class com.project.mocha_patient.login.FindForgotInfoActivity$ForgetResponse {*;}
-keep class com.project.mocha_patient.account_setting.ChangePasswordActivity$ChangePasswordResponse {*;}