Android - Declarative vs Programmatic UI
Has anyone seen or compiled benchmarks comparing declarative (XML) versus programmatically created UI's in Android?
There are things that Google has done to speed up the declarative approach, but you still do have the layout in开发者_StackOverflow中文版flation step done at runtime.
Have you ever switched (or considered) changing your UI from declarative to programmatic for any reason?
Very little of the layout inflation is done at runtime. As hinted in the LayoutInflator API docs:
For performance reasons, view inflation relies heavily on pre-processing of XML files that is done at build time. Therefore, it is not currently possible to use LayoutInflater with an XmlPullParser over a plain XML file at runtime
If you take a look at the source, many of the views are pulled from a hash map based on their XML tag.
In answer to your question of whether I have benchmarked the inflater, I have to say no. Personally I find the idea of benchmarking the layout inflater in Android for your app to be the equivalent of benchmarking the DOM parser in Firefox for your website. I don't think the exercise is pointless, but you should have a much better reason than "my activity layout is too complicated for the inflater"...
If you require a dynamically generated layout, you would be best off creating it programmatically. If your view is simply taking a long time to inflate, you should simplify your view XML.
I've developed this class to pre-inflate a pool of views and reuse it everytime I need. I've gained seconds in performance while updating UI, which is quite impressive.
My Logcat says:
updating UI inflating on demand >> 2136mS
updating UI reusing from pool >> 937mS
Here is my class, just don't mind my clumsy Java coding style, I'm a C++ embedded programmer.
import java.util.ArrayList;
import java.util.List;
import android.view.LayoutInflater;
import android.view.View;
public class ViewPool {
private List<View> mViews;
private LayoutInflater mInf;
private int mIdx;
private int mResource;
/**
* Constructor, gives Inflater and resource ID to inflate
* @param mInf Layout inflater
* @param rID Resource ID of view to inflate
* @para number number of views that must inflate on first initialization
*/
public ViewPool(LayoutInflater mInf, int rID, int number) {
super();
int idx;
mViews = new ArrayList<View>();
this.mInf = mInf;
mResource = rID;
mIdx=0; // index of first used item
for(idx=0; idx<number;++idx)
{
mViews.add((View)mInf.inflate(mResource, null));
}
}
/**
* Start from first item of pool
*/
public void Reset()
{
mIdx=0;
}
/**
* Get a view from pool, if no more views on pool, inflate more
* @return
*/
public View GetView()
{
View retval;
retval = mViews.get(mIdx);
++mIdx;
if(mIdx == mViews.size()) // no more views in pool??
mViews.add((View)mInf.inflate(mResource, null)); // inflate more
return(retval);
}
}
I did some VERY informal/hackish tests on this, and found that taking the programmatic approach, whilst not as nice to work with, shaved between a third and a half of the total time taken. The test was run on a Samsung 7" Galaxy only, and not on an AVD.
As I say this was a very informal/hackish test (as you'll see by the code), with very limited circumstances, the sort of thing you put together quickly to satisfy your own curiosity and not usually for public consumption.
R.layout.ll and R.layout.tv are simple layout files containing blank LinearLayouts and TextViews respectively.
If you're just working with a handful of views I'd stick with XML/inflaters, but for hundreds then maybe you'll want to consider the programmatic approach if speed is an issue.
package com.inflatervscode;
import java.util.Calendar;
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
public class InflaterVSCodeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
// Generates a few nested LinearLayouts/TextViews, a number of
// times, and works out how many milliseconds this took.
@Override
public void onResume() {
super.onResume();
setContentView(R.layout.main);
int num_repeats = 500; // Change this to however many times you want to
// create a set of nested views.
LinearLayout masterLL = (LinearLayout)findViewById(R.id.test);
TextView results = (TextView)findViewById(R.id.results);
Calendar c = Calendar.getInstance();
long startTime = c.getTimeInMillis();
for (int i=0;i<num_repeats;i++) {
// Replace the section below with LinearLayout fll = new LinearLayout(this); etc
LinearLayout fll = (LinearLayout)getLayoutInflater().inflate(R.layout.ll, null);
LinearLayout sll = (LinearLayout)getLayoutInflater().inflate(R.layout.ll, null);
LinearLayout tll = (LinearLayout)getLayoutInflater().inflate(R.layout.ll, null);
TextView tv = (TextView)getLayoutInflater().inflate(R.layout.tv, null);
tv.setText(i+"");
tll.addView(tv);
sll.addView(tll);
fll.addView(sll);
masterLL.addView(fll);
}
c = Calendar.getInstance();
long endTime = c.getTimeInMillis();
String tt = Long.toString((endTime-startTime));
results.setText("Results for "+num_tests+" tests:\n\nStart:"+Long.toString(startTime)+"\nEnd :"+Long.toString(endTime)+"\n\nDifference (ms):"+tt);
}
}
精彩评论