自Android 4.1引入了“Profile GPU rendering”这个开发工具以帮助分析应用程序性能并并精确定位渲染问题,Android 4.3增加了可视效果:On screen as bars/lines。模拟器中此功能生效要勾选AVD的:Emulation Options:User Host GPU。
这个选项的开关实现位于settings中DevelopmentSettings.java中的函数writeTrackFrameTimeOptions:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Codeprivate void writeTrackFrameTimeOptions(Object newValue) {
SystemProperties.set(HardwareRenderer.PROFILE_PROPERTY,
newValue == null ? "" : newValue.toString());
pokeSystemProperties();
updateTrackFrameTimeOptions();
}
void pokeSystemProperties() {
if (!mDontPokeProperties) {
//noinspection unchecked
(new SystemPropPoker()).execute();
}
}
static class SystemPropPoker extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
String[] services;
try {
services = ServiceManager.listServices();
} catch (RemoteException e) {
return null;
}
for (String service : services) {
IBinder obj = ServiceManager.checkService(service);
if (obj != null) {
Parcel data = Parcel.obtain();
try {
obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
} catch (Exception e) {
Log.i(TAG, "Somone wrote a bad service '" + service
+ "' that doesn't like to be poked: " + e);
}
data.recycle();
}
}
return null;
}
}
其中HardwareRenderer.PROFILE_PROPERTY定义于HardwareRenderer.java:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code /**
* System property used to enable or disable hardware rendering profiling.
* The default value of this property is assumed to be false.
*
* When profiling is enabled, the adb shell dumpsys gfxinfo command will
* output extra information about the time taken to execute by the last
* frames.
*
* Possible values:
* "true", to enable profiling
* "visual_bars", to enable profiling and visualize the results on screen
* "visual_lines", to enable profiling and visualize the results on screen
* "false", to disable profiling
*
* @see #PROFILE_PROPERTY_VISUALIZE_BARS
* @see #PROFILE_PROPERTY_VISUALIZE_LINES
*
* @hide
*/
public static final String PROFILE_PROPERTY = "debug.hwui.profile";
抽象类GlRenderer中重写函数dumpGfxInfo:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code@Override
void dumpGfxInfo(PrintWriter pw) {
if (mProfileEnabled) {
pw.printf("\n\tDraw\tProcess\tExecute\n");
mProfileLock.lock();
try {
for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
if (mProfileData[i] < 0) {
break;
}
pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
mProfileData[i + 2]);④
mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
}
mProfileCurrentFrame = mProfileData.length;
} finally {
mProfileLock.unlock();
}
}
}
在“Profile GPU rendering”中勾选“In adb shell dumpsys gfxinfo”或者“adb shell setprop debug.hwui.profile true”之后,运行adb shell dumpsys gfxinfo package_name输出(ms):
com.android.deskclock/com.android.deskclock.DeskClock/android.view.ViewRootImpl@41770410
Draw Process Execute
31.73 10.66 11.73
29.34 6.74 8.19
74.70 31.81 26.01
5.96 4.51 5.67
59.04 3.42 6.61
19.34 5.00 7.14
33.70 18.27 138.73
2.97 14.80 14.26
4.28 3.27 15.31
2.25 3.83 4.03
3.18 1.87 3.89
3.09 1.92 5.38
14.55 3.01 16.52
2.66 3.74 3.83
5.69 1.97 72.48
4.27 1.93 12.90
0.73 4.84 4.49
36.66 6.62 7.53
12.80 3.22 17.49
6.31 4.86 4.73
4.55 3.42 4.46
1.95 1.91 13.81
15.05 23.02 5.10
21.86 1.86 4.61
4.87 8.40 5.24
4.49 6.78 4.21
7.15 30.36 3.70
12.38 24.17 4.89
2.77 5.27 22.00
3.26 5.66 4.02
1.74 3.98 22.07
2.23 3.30 4.34
0.86 2.89 13.56
1.00 13.85 13.94
0.84 1.34 13.86
3.45 2.42 3.81
0.69 1.48 14.75
0.64 1.32 13.88
0.46 1.41 14.48
其中:
Draw--Build display lists
Process—Process(Draw) display lists
Excute--Swap GL buffers
关于此数据分析详见Android Performance Case Study和Android application (performance and more) analysis tools - Tutorial。
重写了函数draw:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code@Override
void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
if (canDraw()) {
if (!hasDirtyRegions()) {
dirty = null;
}
attachInfo.mIgnoreDirtyState = true;
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
view.mPrivateFlags |= View.PFLAG_DRAWN;
final int surfaceState = checkCurrent();
if (surfaceState != SURFACE_STATE_ERROR) {
HardwareCanvas canvas = mCanvas;
attachInfo.mHardwareCanvas = canvas;
if (mProfileEnabled) {
mProfileLock.lock();
}
dirty = beginFrame(canvas, dirty, surfaceState);
DisplayList displayList = buildDisplayList(view, canvas);
int saveCount = 0;
int status = DisplayList.STATUS_DONE;
try {
status = prepareFrame(dirty);
saveCount = canvas.save();
callbacks.onHardwarePreDraw(canvas);
if (displayList != null) {
status |= drawDisplayList(attachInfo, canvas, displayList, status);
} else {
// Shouldn't reach here
view.draw(canvas);
}
} catch (Exception e) {
Log.e(LOG_TAG, "An error has occurred while drawing:", e);
} finally {
callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
view.mRecreateDisplayList = false;
mFrameCount++;
debugDirtyRegions(dirty, canvas);
drawProfileData(attachInfo);
}
onPostDraw();
swapBuffers(status);
if (mProfileEnabled) {
mProfileLock.unlock();
}
attachInfo.mIgnoreDirtyState = false;
}
}
}
1.buildDisplayList
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Codeprivate long startBuildDisplayListProfiling() {
if (mProfileEnabled) {
mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
if (mProfileCurrentFrame >= mProfileData.length) {
mProfileCurrentFrame = 0;
}
return System.nanoTime();
}
return 0;
}
private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - getDisplayListStartTime) * 0.000001f;
//noinspection PointlessArithmeticExpression
mProfileData[mProfileCurrentFrame] = total;①
}
}
private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
long buildDisplayListStartTime = startBuildDisplayListProfiling();
canvas.clearLayerUpdates();
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
DisplayList displayList = view.getDisplayList();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
endBuildDisplayListProfiling(buildDisplayListStartTime);
return displayList;
}
view.getDisplayList()来自View.java:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/**
* <p>Returns a display list that can be used to draw this view again
* without executing its draw method.</p>
*
* @return A DisplayList ready to replay, or null if caching is not enabled.
*
* @hide
*/
public DisplayList getDisplayList() {
mDisplayList = getDisplayList(mDisplayList, false);
return mDisplayList;
}
/**
* Returns a DisplayList. If the incoming displayList is null, one will be created.
* Otherwise, the same display list will be returned (after having been rendered into
* along the way, depending on the invalidation state of the view).
*
* @param displayList The previous version of this displayList, could be null.
* @param isLayer Whether the requester of the display list is a layer. If so,
* the view will avoid creating a layer inside the resulting display list.
* @return A new or reused DisplayList object.
*/
private DisplayList getDisplayList(DisplayList displayList, boolean isLayer) {
if (!canHaveDisplayList()) {
return null;
}
if (((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 ||
displayList == null || !displayList.isValid() ||
(!isLayer && mRecreateDisplayList))) {
// Don't need to recreate the display list, just need to tell our
// children to restore/recreate theirs
if (displayList != null && displayList.isValid() &&
!isLayer && !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
return displayList;
}
if (!isLayer) {
// If we got here, we're recreating it. Mark it as such to ensure that
// we copy in child display lists into ours in drawChild()
mRecreateDisplayList = true;
}
if (displayList == null) {
final String name = getClass().getSimpleName();
displayList = mAttachInfo.mHardwareRenderer.createDisplayList(name);
// If we're creating a new display list, make sure our parent gets invalidated
// since they will need to recreate their display list to account for this
// new child display list.
invalidateParentCaches();
}
boolean caching = false;
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType();
final HardwareCanvas canvas = displayList.start(width, height);
try {
if (!isLayer && layerType != LAYER_TYPE_NONE) {
if (layerType == LAYER_TYPE_HARDWARE) {
final HardwareLayer layer = getHardwareLayer();
if (layer != null && layer.isValid()) {
canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
} else {
canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
}
caching = true;
} else {
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
caching = true;
}
}
} else {
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
if (!isLayer) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
}
} finally {
displayList.end();
displayList.setCaching(caching);
if (isLayer) {
displayList.setLeftTopRightBottom(0, 0, width, height);
} else {
setDisplayListProperties(displayList);
}
}
} else if (!isLayer) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return displayList;
}
View Code
2.drawDisplayList
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Codeprivate int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
DisplayList displayList, int status) {
long drawDisplayListStartTime = 0;
if (mProfileEnabled) {
drawDisplayListStartTime = System.nanoTime();
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
try {
status |= canvas.drawDisplayList(displayList, mRedrawClip,
DisplayList.FLAG_CLIP_CHILDREN);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - drawDisplayListStartTime) * 0.000001f;
mProfileData[mProfileCurrentFrame + 1] = total;②
}
handleFunctorStatus(attachInfo, status);
return status;
}
canvas.drawDisplayList来自HardwareCanvas.java,这是一个抽象接口:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code/**
* Draws the specified display list onto this canvas. The display list can only
* be drawn if {@link android.view.DisplayList#isValid()} returns true.
*
* @param displayList The display list to replay.
*/
public void drawDisplayList(DisplayList displayList) {
drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
}
/**
* Draws the specified display list onto this canvas.
*
* @param displayList The display list to replay.
* @param dirty The dirty region to redraw in the next pass, matters only
* if this method returns {@link DisplayList#STATUS_DRAW}, can be null.
* @param flags Optional flags about drawing, see {@link DisplayList} for
* the possible flags.
*
* @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW}, or
* {@link DisplayList#STATUS_INVOKE}, or'd with {@link DisplayList#STATUS_DREW}
* if anything was drawn.
*
* @hide
*/
public abstract int drawDisplayList(DisplayList displayList, Rect dirty, int flags);
3.swapBuffers
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Codeprivate void swapBuffers(int status) {
if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
long eglSwapBuffersStartTime = 0;
if (mProfileEnabled) {
eglSwapBuffersStartTime = System.nanoTime();
}
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - eglSwapBuffersStartTime) * 0.000001f;
mProfileData[mProfileCurrentFrame + 2] = total;③
}
checkEglErrors();
}
}
EGL10.java中的eglSwapBuffers->EGLImpl.java->com_google_android_gles_jni_EGLImpl.cpp:
com_google_android_gles_jni_EGLImpl.cpp中:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Codestatic jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display, jobject surface) {
if (display == NULL || surface == NULL) {
jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
EGLSurface sur = getSurface(_env, surface);
return eglSwapBuffers(dpy, sur);
}