Proguard - 为什么不需要保留 Parcelable CREATOR 的 createFromParcel 名称
Proguard - Why preserving createFromParcel name of Parcelable CREATOR isn't necessary
我的项目链接了多个混淆文件。如果我查看所有混淆文件,这里是处理 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;
}
我有以下 class 文件,它是在 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
这样的分块过程中,事情会失败,因为 OS 系统不再能找到 createFromParcel
。
然而,令我惊讶的是,下面这行代码仍然可以正常执行
Log.i("CHEOK", "createFromParcel");
请问为什么会这样?我虽然 Android OS 预计会执行 createFromParcel(Parcel)
。 OS 如何知道它需要执行 a(Parcel)
?
我也很好奇这个。事实证明,编译器创建了一个指向混淆方法的合成方法。这个用apktool
反apk,深挖smali代码就可以看出来
这是 class Foo
的相关 smali,它实现了 Parcelable
并且被混淆了 createFromParcel()
-> a()
.
# The synthetic method definition
.method public synthetic createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;
invoke-virtual {p0, p1}, Lcom/example/Foo;->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
class 调用合成方法,然后调用混淆方法的堆栈跟踪。
at com.example.Foo.a(SourceFile:52)
at com.example.Foo.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)
我的项目链接了多个混淆文件。如果我查看所有混淆文件,这里是处理 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;
}
我有以下 class 文件,它是在 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
这样的分块过程中,事情会失败,因为 OS 系统不再能找到 createFromParcel
。
然而,令我惊讶的是,下面这行代码仍然可以正常执行
Log.i("CHEOK", "createFromParcel");
请问为什么会这样?我虽然 Android OS 预计会执行 createFromParcel(Parcel)
。 OS 如何知道它需要执行 a(Parcel)
?
我也很好奇这个。事实证明,编译器创建了一个指向混淆方法的合成方法。这个用apktool
反apk,深挖smali代码就可以看出来
这是 class Foo
的相关 smali,它实现了 Parcelable
并且被混淆了 createFromParcel()
-> a()
.
# The synthetic method definition
.method public synthetic createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;
invoke-virtual {p0, p1}, Lcom/example/Foo;->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
class 调用合成方法,然后调用混淆方法的堆栈跟踪。
at com.example.Foo.a(SourceFile:52)
at com.example.Foo.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)