1. 程式人生 > >google gson 使用proguard混淆程式碼注意事項

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

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

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