Proguard - 为什么不保留parcelable CREATOR的createFromParcel名称是不必要的

时间:2017-07-21 10:33:01

标签: android proguard

我的项目与多个proguard文件相关联。如果我查看所有proguard文件,这里的规则处理Parcelable

-keepclassmembers class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

-keep class * implements android.os.Parcelable {
*;
}

-keepnames class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

我有以下类文件,这是在proguard进程之前。

在proguard过程之前

package org.yccheok.jstock.engine;

import android.os.Parcel;
import android.os.Parcelable;

/**
 *
 * @author yccheok
 */
public class Code implements Parcelable {
    private Code(String code) {
        this.code = code;
    }

    public static Code newInstance(String code) {
        if (code == null) {
            throw new java.lang.IllegalArgumentException("code cannot be null");
        }

        return new Code(code);
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + code.hashCode();

        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof Code)) {
            return false;
        }

        return this.code.equals(((Code)o).code);
    }

    @Override
    public String toString() {
        return code;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Handling Parcelable nicely.

    public static final Parcelable.Creator<Code> CREATOR = new Parcelable.Creator<Code>() {
        public Code createFromParcel(Parcel in) {
            android.util.Log.i("CHEOK", "createFromParcel");
            return new Code(in);
        }

        public Code[] newArray(int size) {
            android.util.Log.i("CHEOK", "newArray");
            return new Code[size];
        }
    };

    private Code(Parcel in) {
        code = in.readString();
        android.util.Log.i("CHEOK", "Code parcel " + code);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(code);        
    }

    // Handling Parcelable nicely.    
    ////////////////////////////////////////////////////////////////////////////

    private String code;
}

在proguard过程之后(我通过逆向工程得到以下代码)

package org.yccheok.jstock.engine;

import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable.Creator;
import android.util.Log;

public class Code
  implements Parcelable
{
  public static final Parcelable.Creator<Code> CREATOR = new Parcelable.Creator()
  {
    public Code a(Parcel paramAnonymousParcel)
    {
      Log.i("CHEOK", "createFromParcel");
      return new Code(paramAnonymousParcel, null);
    }

    public Code[] a(int paramAnonymousInt)
    {
      Log.i("CHEOK", "newArray");
      return new Code[paramAnonymousInt];
    }
  };
  private String code;

  private Code(Parcel paramParcel)
  {
    this.code = paramParcel.readString();
    Log.i("CHEOK", "Code parcel " + this.code);
  }

  private Code(String paramString)
  {
    this.code = paramString;
  }

  public static Code newInstance(String paramString)
  {
    if (paramString == null)
      throw new IllegalArgumentException("code cannot be null");
    return new Code(paramString);
  }

  public int describeContents()
  {
    return 0;
  }

  public boolean equals(Object paramObject)
  {
    if (paramObject == this)
      return true;
    if (!(paramObject instanceof Code))
      return false;
    return this.code.equals(((Code)paramObject).code);
  }

  public int hashCode()
  {
    return 527 + this.code.hashCode();
  }

  public String toString()
  {
    return this.code;
  }

  public void writeToParcel(Parcel paramParcel, int paramInt)
  {
    paramParcel.writeString(this.code);
  }
}

如您所见,CREATOR createFromParcel(Parcel)newArray(int)已重命名为a(Parcel)newArray(int)

我希望在像Bundle.putParcelable这样的parceble过程中,事情会失败,因为OS系统不再能找到createFromParcel

然而,令我惊讶的是,以下代码行仍然执行而没有问题

Log.i("CHEOK", "createFromParcel");

我可以知道为什么会这样吗?我希望Android OS能够执行createFromParcel(Parcel)。操作系统如何知道需要执行a(Parcel)

1 个答案:

答案 0 :(得分:1)

我也很好奇。事实证明,编译器创建了一个指向混淆方法的合成方法。如果您使用apktool来反转apk并深入挖掘smali代码,则可以看到这一点。

以下是实现Foo的类Parcelable的相关smali,并且被混淆为createFromParcel() - &gt; a()

# The synthetic method definition
.method public synthetic createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;

    invoke-virtual {p0, p1}, Lcom/example/Foo$1;->a(Landroid/os/Parcel;)Lcom/example/Foo;

    move-result-object v0

    return-object v0
.end method

# The obfuscated method definition
.method public a(Landroid/os/Parcel;)Lcom/example/Foo;

    new-instance v0, Lcom/example/Foo;

    invoke-direct {v0, p1}, Lcom/example/Foo;-><init>(Landroid/os/Parcel;)V

    return-object v0
.end method

这是堆栈跟踪,它显示Parcel类调用合成方法,然后调用模糊方法。

at com.example.Foo$1.a(SourceFile:52)
    at com.example.Foo$1.createFromParcel(SourceFile:49)
    at android.os.Parcel.readParcelable(Parcel.java:2471)
    at android.os.Parcel.readValue(Parcel.java:2365)
    at android.os.Parcel.readListInternal(Parcel.java:2793)
    at android.os.Parcel.readArrayList(Parcel.java:2036)
    at android.os.Parcel.readValue(Parcel.java:2386)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:2732)
    at android.os.BaseBundle.unparcel(BaseBundle.java:269)
    at android.os.BaseBundle.getString(BaseBundle.java:992)