老司机开车带你体验 Parcelable 到底有多快

2,250 阅读5分钟

Parcelable和Serializable都是序列化反序列化的解决方案,众所周知,前者的效率和内存占用相对后者都是有优势的,那么,Parcelable到底比Serializable快多少呢,老司机写个Demo带你来测试一把。

在测试之前我说一下我的思路: 在一个Activity中从把Serializable或者Parcelable对象放入Bundle中开始计时,到带到另一个Activity中在OnCreate中从Bundle中取出Serializable或Parcelable对象时停止,记为一次测试时长,当然,这个时长是有杂质存在的,即它还包含了Activity之间做跳转等一系列内容,这里称之为杂质,我们需要剔除。

杂质的计算方式很简单,把上述的步骤中去除开始的Activity将数据加入Bundle的代码和在跳转到的Activity的OnCreate中从Bundle取出数据的代码,这段时间就是我们的杂质时间。

故(测试Serializable的去除杂质的时间)/(测试Parcelable的去除杂质的时间)=我们要的效率值

这样得出来的结果就能看出说明在从一个Activity传内容基本一致的数据到另一个Activity时使用Parcelable的对象比使用Serializable对象要快多少。

当然,仅仅一次测试是不能说明问题的,各种测试的值都是浮动的,所以我们需要加大这种测试的数据,将这些测试做个几千次甚至上万次然后再取平均值,就能得到一个比较准确的结论了(不懂的人想想抛一万次硬币是不是正反面的概率都在0.5左右)

具体内容看代码

首先来两个相似的类,内容都差不多

这个类是实现了Parcelable接口的(不要在意命名,最近权利的游戏看多了。。。。)

public class JonSnow implements Parcelable {
    private String name;
    private int age;
    private List fuckedWhoreList;
    public JonSnow() {
    }
    public JonSnow(Parcel source) {
        this.name = source.readString();
        this.age = source.readInt();
        this.fuckedWhoreList = new ArrayList();
        source.readTypedList(fuckedWhoreList,FuckedWhore.CREATOR);
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public List getFuckedWhoreList() {
        return fuckedWhoreList;
    }
    public void setFuckedWhoreList(List fuckedWhoreList) {
        this.fuckedWhoreList = fuckedWhoreList;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeTypedList(fuckedWhoreList);
    }
    public static final Creator CREATOR = new Creator() {
        @Override
        public JonSnow createFromParcel(Parcel source) {
            return new JonSnow(source);
        }
        @Override
        public JonSnow[] newArray(int size) {
            return new JonSnow[size];
        }
    };
    public static class FuckedWhore implements Parcelable{
        private String name;
        private int age;
        public FuckedWhore(){
        }
        public FuckedWhore(Parcel source) {
            this.name = source.readString();
            this.age = source.readInt();
        }
        @Override
        public int describeContents() {
            return 0;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(age);
        }
        static final Parcelable.Creator CREATOR = new Creator() {
            @Override
            public FuckedWhore createFromParcel(Parcel source) {
                return new FuckedWhore(source);
            }
            @Override
            public FuckedWhore[] newArray(int size) {
                return new FuckedWhore[size];
            }
        };
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    }

这个是实现了Serializable接口的类

public class IMP implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private List fuckedWhoreList;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public List getFuckedWhoreList() {
        return fuckedWhoreList;
    }
    public void setFuckedWhoreList(List fuckedWhoreList) {
        this.fuckedWhoreList = fuckedWhoreList;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public static class FuckedWhore implements Serializable{
        private static final long serialVersionUID = 1L;
        private String name;
        private int age;
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    }

然后写两个Activity,一个是跳转前提供Bundle的,另一个是跳转后解析Bundle的 跳转前的TestParcelableActivity

public class TestParcelableActivity extends Activity {
    @BindView(R.id.tvParcelable)
    TextView tvParcelable;
    @BindView(R.id.tvResultP)
    TextView tvResultP;
    @BindView(R.id.tvSerializable)
    TextView tvSerializable;
    @BindView(R.id.tvResultS)
    TextView tvResultS;
    @BindView(R.id.tvTest)
    TextView tvTest;
    @BindView(R.id.tvResult)
    TextView tvResult;
    private JonSnow jonSnow;
    private IMP imp;
    private long totalTestTime;
    private long perParcelabel;
    private long perSerializable;
    private long perEmpty;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_parcelabel);
        ButterKnife.bind(this);
        initIMP();
        initJsonSnow();
    }
    @OnClick(R.id.tvTest)
    public void startTest(){
        totalTestTime = 0;
        perParcelabel = 0;
        perSerializable = 0;
        perEmpty = 0;
        testEmpty();
    }
    private void testEmpty(){
        Constant.MODE = 0;
        Bundle bundle = new Bundle();
        Constant.start = System.currentTimeMillis();
        for(long i = 0;i
        }
        Intent intent = new Intent(TestParcelableActivity.this, TestResultActivity.class);
        intent.putExtras(bundle);
        startActivityForResult(intent, Constant.REQ_CDOE);
    }
    @OnClick(R.id.tvParcelable)
    public void testParcelabel(){
        Constant.MODE = 1;
        Bundle bundle = new Bundle();
        Constant.start = System.currentTimeMillis();
        for(long i = 0;i
            bundle.putParcelable("parcelable" + i, jonSnow);
        }
        Intent intent = new Intent(TestParcelableActivity.this, TestResultActivity.class);
        intent.putExtras(bundle);
        startActivityForResult(intent, Constant.REQ_CDOE);
    }
    @OnClick(R.id.tvSerializable)
    public void testSerializable(){
        Constant.MODE = 2;
        Bundle bundle = new Bundle();
        Constant.start = System.currentTimeMillis();
        for(long i = 0;i
            bundle.putSerializable("Serializable" + i, imp);
        }
        Intent intent = new Intent(TestParcelableActivity.this, TestResultActivity.class);
        intent.putExtras(bundle);
        startActivityForResult(intent, Constant.REQ_CDOE);
    }
    private void initJsonSnow(){
        jonSnow = new JonSnow();
        jonSnow.setName("Json Snow");
        jonSnow.setAge(25);
        List whoreList = new ArrayList();
        for(int i = 0;i
            JonSnow.FuckedWhore whore = new JonSnow.FuckedWhore();
            whore.setName("a girl");
            whore.setAge(15);
            whoreList.add(whore);
        }
        jonSnow.setFuckedWhoreList(whoreList);
    }
    private void initIMP(){
        imp = new IMP();
        imp.setName("Tyrion Lannister");
        imp.setAge(30);
        List whoreList = new ArrayList();
        for(int i = 0;i
            IMP.FuckedWhore whore = new IMP.FuckedWhore();
            whore.setName("Shae");
            whore.setAge(14);
            whoreList.add(whore);
        }
        imp.setFuckedWhoreList(whoreList);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode == RESULT_OK){
            totalTestTime++;
            if(totalTestTime
                switch (Constant.MODE){
                    case 0:
                        testEmpty();
                        break;
                    case 1:
                        testParcelabel();
                        break;
                    case 2:
                        testSerializable();
                        break;
                }
            }else {
                switch (Constant.MODE){
                    case 0:
                        resultEmpty();
                        break;
                    case 1:
                        resultParcelable();
                        break;
                    case 2:
                        resultSerializable();
                        break;
                }
            }
        }
    }
    private void resultEmpty(){
        perEmpty = Constant.perTime();
        totalTestTime = 0;
        tvResult.setText("Empty :"+perEmpty);
        testParcelabel();
    }
    private void resultParcelable(){
        perParcelabel = Constant.perTime();
        totalTestTime = 0;
        tvResultP.setText("parcelable:"+perParcelabel);
        testSerializable();
    }
    private void resultSerializable(){
        perSerializable = Constant.perTime();
        totalTestTime = 0;
        tvResultS.setText("serializable"+perSerializable);
        float result = (perSerializable-perEmpty)/(perParcelabel-perEmpty);
        tvResult.setText("Result:"+result);
    }
    }

这个是解析Bundle的TestResultActivity

public class TestResultActivity extends Activity {
    private Bundle bundle;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bundle = getIntent().getExtras();
        switch (Constant.MODE){
            case 0:
                testEmpty();
                break;
            case 1:
                testParcelable();
                break;
            case 2:
                testSerializable();
                break;
        }
        Constant.result();
        setResult(RESULT_OK);
        finish();
    }
    private void testEmpty(){
        Constant.end = System.currentTimeMillis();
    }
    private void testParcelable(){
        JonSnow jonSnow;
        for(int i = 0;i
            jonSnow = bundle.getParcelable("parcelable" + i);
        }
        Constant.end = System.currentTimeMillis();
    }
    private void testSerializable(){
        IMP imp;
        for(int i = 0;i
            imp = (IMP) bundle.getSerializable("Serializable" + i);
        }
        Constant.end = System.currentTimeMillis();
    }
    }

为了减少其他部分对测试结果的影响,把一些公用的变量放在常量里了(没事别干这种事)

public class Constant {
    public static long start;
    public static long end;
    public static long tempResult;
    public static long result;
    public static int MODE = 0;//mode = 0 empty 1 perParcelable 2Serializable
    public static final long TEST_TIME = 10;
    public static final long TEST_CYCLE = 500;
    public static final long TEST_MOUNT = 1000;
    public static final int REQ_CDOE = 0x01;
    public static long result (){
        tempResult = tempResult + end-start;
        return tempResult;
    }
    public static long perTime(){
        result = tempResult/TEST_CYCLE;
        tempResult = 0;
        return result;
    }
    }

最后运行一下看看结果:Parcelable比Serializable快了3倍左右,当然,误差是存在的,但是大量实验结果都在3倍左右浮动,说明在从一个Activity传数据到另一个Activity时使用Parcelable的对象比使用Serializable对象要快3倍左右。so…你懂的

当然,除了在速度方面,内存占用上Parcelable也是占优势的,因为Serializable用到了反射,在序列化和反序列化的过程中会造一大堆临时变量,可能造成频繁GC哟,对机器的代价还是要高许多的,特别是当我们数据量比较大的情况下,可能就会有很明显的感觉了,影响用户体验就不好咯

最后的结论是,同志们,如果你现在还在用两个轮子的交通工具,时间较为充裕的情况下,是时候换成四个轮子的东东了。