android 下Protobuff常用的框架有三个: protobuff自身, square出的wire , protostuff
由于protobuff会为每个属性生成大量不常用的方法,当程序比较复杂时容易超过android的60K个方法的上限, 所以本次测试未包括protobuff
测试逻辑是循环100次序列化100个元素的数组,并反序列化,求平均值,代码如下:
wire的测试代码:
public void onClickButton(View view){ if (TestTask.isCancel){ TestTask.isCancel = false; TestTask.sumDeserializeTime = 0; TestTask.sumTime = 0; TestTask.runCount = 0; TextView text1 = (TextView) findViewById(R.id.textView2); text1.setText(""); new TestTask( (TextView) findViewById(R.id.textView2) , (TextView) findViewById(R.id.button1)).execute(); ((TextView)view).setText("测试中,点击中断"); }else{ ((TextView)view).setText("正在中断..."); TestTask.isCancel = true; } } static class TestTask extends AsyncTask<Void,Void,Long>{ long serializeTime=0; long deserializeTime=0; static long sumTime=0; static long sumDeserializeTime=0; static int runCount=0; static boolean isCancel=true; TextView text1; TextView btn; public TestTask(TextView text1,TextView btn) { this.text1 = text1; this.btn = btn; } @Override protected Long doInBackground(Void... params) { long startTime = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { List<ListItem> itemList = new ArrayList<ListItem>(); ListItem.Builder itemBuilder = new ListItem.Builder(); ListItem item; for (int j = 0; j < 100; j++) { item = itemBuilder.title("test Title"+i+":"+j) .remark("test Remark"+i+":"+j) .coverUrl("http://pic.ozreader.com/abc.pic") .uri("PKB:TESTURI") .build(); itemList.add(item); } ScrollList.Builder listBuilder= new ScrollList.Builder(); ScrollList list = listBuilder.haveMore(false).tags(itemList).build(); byte[] dataBuffer = list.toByteArray(); serializeTime = System.currentTimeMillis()-startTime; Wire wire = new Wire(); try { ScrollList resultList = wire.parseFrom(dataBuffer, ScrollList.class); if (resultList == null){ Log.e("TEST", "resultList is null"); break; }else if (resultList.tags == null){ Log.e("TEST", "resultList.tags is null"); break; }else if (resultList.tags.size() <= 0){ Log.e("TEST", "resultList.tags is empty"); break; }else if (resultList.tags.size() != 100){ Log.e("TEST", "resultList.tags is wrong"); break; }else if (!resultList.tags.get(0).uri.equals("PKB:TESTURI")){ Log.e("TEST", "resultList.tags content is wrong"); break; } deserializeTime = System.currentTimeMillis()-startTime-serializeTime; } catch (IOException e) { e.printStackTrace(); } } return System.currentTimeMillis() - startTime; } @Override protected void onPostExecute(Long result) { sumTime += result; sumDeserializeTime += deserializeTime; runCount ++; text1.append("result:"+result+", serializeTime:"+serializeTime+", deserializeTime:"+deserializeTime+", runCount:"+runCount+", avg:"+sumTime/runCount+", avg deserializeTime:"+sumDeserializeTime/runCount+"\n"); if (isCancel){ text1.append("测试中断."); btn.setText("开始测试"); }else if (runCount < 100){ new TestTask(text1,btn).execute(); }else{ isCancel = true; text1.append("测试完成."); btn.setText("开始测试"); } } }
protobuff的测试代码:
public void onClickButton(View view){ if (TestTask.isCancel){ TestTask.isCancel = false;
TestTask.sumDeserializeTime = 0;
TestTask.sumTime = 0;
TestTask.runCount = 0;
TextView text1 = (TextView) findViewById(R.id.textView2);
text1.setText(""); new TestTask( (TextView) findViewById(R.id.textView2) , (TextView) findViewById(R.id.button1)).execute(); ((TextView)view).setText("测试中,点击中断"); }else{ ((TextView)view).setText("正在中断..."); TestTask.isCancel = true; } } static class TestTask extends AsyncTask<Void,Void,Long>{ long serializeTime=0; long deserializeTime=0; static long sumTime=0; static long sumDeserializeTime=0; static int runCount=0; static boolean isCancel=true; TextView text1; TextView btn; public TestTask(TextView text1,TextView btn) { this.text1 = text1; this.btn = btn; } @Override protected Long doInBackground(Void... params) { long startTime = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { List<ListItem> itemList = new ArrayList<ListItem>(); ScrollList list = new ScrollList(); list.setHaveMore(false); list.setTagsList(itemList); ListItem item; for (int j = 0; j < 100; j++) { item = new ListItem(); item.setTitle("test Title"+i+":"+j); item.setRemark("test Remark"+i+":"+j); item.setCoverUrl("http://pic.ozreader.com/abc.pic"); item.setUri("PKB:TESTURI"); itemList.add(item); } LinkedBuffer buffer = LinkedBuffer.allocate(1024); byte[] dataBuffer = ProtobufIOUtil.toByteArray(list, ScrollList.getSchema(), buffer); serializeTime = System.currentTimeMillis()-startTime; ScrollList resultList = new ScrollList(); ProtobufIOUtil.mergeFrom(dataBuffer, resultList, ScrollList.getSchema()); if (resultList.getTagsList() == null){ Log.e("TEST", "resultList.tags is null"); break; }else if (resultList.getTagsList().size() <= 0){ Log.e("TEST", "resultList.tags is empty"); break; }else if (resultList.getTagsList().size() != 100){ Log.e("TEST", "resultList.tags is wrong"); break; }else if (!resultList.getTagsList().get(0).getUri().equals("PKB:TESTURI")){ Log.e("TEST", "resultList.tags content is wrong"); break; } deserializeTime = System.currentTimeMillis()-startTime-serializeTime; } return System.currentTimeMillis() - startTime; } @Override protected void onPostExecute(Long result) { sumTime += result; sumDeserializeTime += deserializeTime; runCount ++; text1.append("result:"+result+", serializeTime:"+serializeTime+", deserializeTime:"+deserializeTime+", runCount:"+runCount+", avg:"+sumTime/runCount+", avg deserializeTime:"+sumDeserializeTime/runCount+"\n"); if (isCancel){ text1.append("测试中断."); btn.setText("开始测试"); }else if (runCount < 100){ new TestTask(text1,btn).execute(); }else{ isCancel = true; text1.append("测试完成."); btn.setText("开始测试"); } } }
测试结果为(单位豪秒):
手机环境:魅族MX2
1) wire 1.5.1 ,网址 https://github.com/square/wire
avg: 1860~1907 , 最小值约为1500左右,极少出现, 全过程随机分布
avg deserializeTime: 9~10, 最小值约为5,极少出现,全过程随机分布
2) protostuff 1.0.8 ,网址 https://code.google.com/p/protostuff/
avg: 1100~1150,非常稳定 , 最小值约为450左右,主要集中在前几次循环,循环10次后就稳定在11XX左右,偶有600~800的情况,多次重复测试情况基本一致,观察自动GC并无内存泄漏的问题, 暂时不确定速度变化的原因.
avg deserializeTime: 6, 最小值约为3,极少出现,主要集中在刚开始,绝大多数为5,偶有达到16的情况.
分析: wire的性能明显比不上protostuff, 速度也不太稳定,起落较大. 考虑到protostuff的开发模式是普通的pojo方式,比较方便,不像protobuff和wire的builder方式,超麻烦. 所以最终推荐不是逻辑超复杂的企业app都使用protostuff .