我們都知道在Android內如果要傳送資料必須透過Intent或Bundle
在送基本型態(int,String,boolean..)時應該沒有什麼爭議
但是如果要送一個 Object 呢?
這時你就有兩個選擇了,你可以使用
● Serializable
● Parcelable
稍微解釋一下這兩個interface
Serializable 是 Java 的 interface
Parcelable 是 Android 的 interface
在使用他們時,你的 Class 必須 implements 他們
既然都可以達到資料傳送的目的
那為什麼Android還要特地出一個 Parcelable 給大家使用呢?
當然是因為 performance 的問題
Serializable 在使用上非常方便
只需要 implements 他就結束了,但是
Serializable 在Java就一直被大家所詬病
他是利用反射來實例化物件
但這種方法會造成大量的temporary objects(臨時物件)
VM也要花時間對這些對象進行garbage collection(GC)
當然 Parcelable 就不是利用反射了
而是我們在 implement 他時就實作他的method
透過泛型直接給定他Class
下面有個簡單的Sample
讓我們實際測試看看兩者之間的效能落差
很簡單,就一個Layout 與 Activity 與兩個 Model Object
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_marginBottom="20dp" android:padding="10dp" android:text="測試執行50000次的\nParcelable/Serializable效能差異" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:padding="10dp" android:text="Parcelable 所花時間(ms):" android:id="@+id/pTimeTv" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:padding="10dp" android:text="Serializable 所花時間(ms):" android:id="@+id/sTimeTv" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:textColor="#cc0000" android:padding="10dp" android:text="效能落差(ms):" android:id="@+id/parcelableTimeTv" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="執行測試" android:id="@+id/button" /> </LinearLayout>
DataP
import android.os.Parcel; import android.os.Parcelable; public class DataP implements Parcelable{ private int a; private String b; private boolean c; public DataP(int a, String b, boolean c) { this.a = a; this.b = b; this.c = c; } public int getA() { return a; } public void setA(int a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } public boolean isC() { return c; } public void setC(boolean c) { this.c = c; } protected DataP(Parcel in) { a = in.readInt(); b = in.readString(); } public static final Creator<DataP> CREATOR = new Creator<DataP>() { @Override public DataP createFromParcel(Parcel in) { return new DataP(in); } @Override public DataP[] newArray(int size) { return new DataP[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(a); dest.writeString(b); } }
DataS
import java.io.Serializable; public class DataS implements Serializable { private int a; private String b; private boolean c; public DataS(int a, String b, boolean c) { this.a = a; this.b = b; this.c = c; } public int getA() { return a; } public void setA(int a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } public boolean isC() { return c; } public void setC(boolean c) { this.c = c; } }
MainActivity
在兩個for迴圈內做5萬次的 put 與 get (單純模擬)
最後把執行時間相減來做出比較
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final Button button = (Button)findViewById(R.id.button); final TextView pTimeTv = (TextView)findViewById(R.id.pTimeTv); final TextView sTimeTv = (TextView)findViewById(R.id.sTimeTv); final TextView parcelableTimeTv = (TextView)findViewById(R.id.parcelableTimeTv); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Bundle bundle = new Bundle(); long pStart = System.currentTimeMillis(); for(int i=0;i<50000;i++){ DataP p1 = new DataP(i,String.valueOf(i),true); bundle.putParcelable("keyP" + i, p1); DataP p2 = bundle.getParcelable("keyP"+i); } long pEnd = System.currentTimeMillis(); long sStart = System.currentTimeMillis(); for(int i=0;i<50000;i++){ DataS s1 = new DataS(i,String.valueOf(i),true); bundle.putSerializable("keyS" + i,s1); DataS s2 = (DataS)bundle.getSerializable("keyS"+i); } long sEnd = System.currentTimeMillis(); long pSub = pEnd - pStart; long sSub = sEnd - sStart; long parcelableSub = (sSub>pSub)?(sSub-pSub):(pSub-sSub); pTimeTv.setText("Parcelable 所花時間(ms):"+pSub); sTimeTv.setText("Serializable 所花時間(ms):"+sSub); parcelableTimeTv.setText("效能落差(ms):"+parcelableSub); } }); } }
從我的不專業測試得知結果為
兩者優缺點比較:
創建5萬個物件(序/反列化)兩者之間時間差為0.7秒
Parcelable 大約是 Serializable 的兩倍快
試想如果今天是10萬或20萬個物件呢,整體速度都會被拖慢!
你的App使用起來就會很卡頓!
所以還是多使用 Parcelable 吧!
Serializable:
● 程式碼簡潔,閱讀容易
● 反序列化時需要強制轉型
● 速度較慢
Parcelable:
● 程式碼較複雜
● 速度較快,佔用資源較少
國外的說法是兩者之間效能差約10倍
個人是覺得沒那麼誇張
不過兩倍以上是有的
大家可以download sample code回去自己測試看看
歡迎將測試結果貼上來這裡~~