《代码里的世界》 —原理篇
用文字札记描绘自己 android学习之路
转载请保留出处 by Qiao
http://www.jb51.cc/article/p-gtxxakbe-bps.html
之前在研究png 和svg在绘制过程中的内存占用和绘制效率对比问题的时候,使用了比较便捷的adb shell 命令 adb shell dumpsys gfxinfo来查看具体数据。由于使用需要,就稍微跟进了一下代码,这里记录下命令行调用输出的详细过程。
1.场景举例
以com.xxx.demo举例,在terminal终端调用 adb shell dumpsys gfxinfo com.xxx.demo,得到的信息大概有两部分:
- Recent DisplayList operations
- Caches
详细日志如下:
• >adb shell dumpsys com.xxx.demo
• Applications Graphics Acceleration Info:
• Uptime: 24363222 Realtime: 26646172
•
• ** Graphics info for pid 30503 [com.xxx.demo] **
•
• Recent DisplayList operations
• Save
• DrawRenderNode
• DrawRenderNode
• DrawRect
• DrawRenderNode
• Save
• ClipRect
• DrawRenderNode
• DrawRenderNode
• Save
• ClipRect
• DrawPath
• DrawPath
• RestoreToCount
• DrawBitmapRect
• RestoreToCount
• DrawRenderNode
• DrawRenderNode
• DrawRect
• RestoreToCount
•
• Caches:
• Current memory usage / total memory usage (bytes):
• TextureCache 65536 / 50331648
• LayerCache 0 / 33554432 (numLayers = 0)
• Layers total 0 (numLayers = 0)
• RenderBufferCache 0 / 8388608
• GradientCache 0 / 3145728
• PathCache 14850 / 33554432
• TessellationCache 0 / 1048576
• TextDropShadowCache 0 / 6291456
• PatchCache 0 / 131072
• FontRenderer 0 A8 0 / 0
• FontRenderer 0 RGBA 0 / 0
• FontRenderer 0 total 0 / 0
• Other:
• FboCache 0 / 25
• Total memory usage:
• 80386 bytes,0.08 MB
•
• Profile data in ms:
•
• com.xxx.demo/com.xxx.demo.MainActivity/android.view.ViewRootImpl@3719de6c (visibility=0)
View hierarchy:
这里从android-4.0.1_r1源码跟进一下dumpsys gfxinfo 的调用过程。
源码探究
首先从入口 \cmds\dumpsys\dumpsys.cpp文件入手。
通过遍历ServiceManager来获取对应参数的Service,并且调用其dump(FileDescriptor fd,PrintWriter pw,String[] args)方法.
获取service
if (service != NULL) {
if (N > 1) {
aout << "------------------------------------------------------------"
"-------------------" << endl;
aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
}
int err = service->dump(STDOUT_FILENO,args); //调用dump
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err)
<< ") " << services[i] << endl;
}
} else {
aerr << "Can't find service: " << services[i] << endl;
}
}
return 0;
}
" data-snippet-id="ext.d9bdcfec81bcd71197b9bc0c6c601b29" data-snippet-saved="false" data-codota-status="done">int main(int argc,char* const argv[])
{
//...
const size_t N = services.size();
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
aout << " " << services[i] << endl;
}
}
}
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]); //获取service
if (service != NULL) {
if (N > 1) {
aout << "------------------------------------------------------------"
"-------------------" << endl;
aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
}
int err = service->dump(STDOUT_FILENO,args); //调用dump
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err)
<< ") " << services[i] << endl;
}
} else {
aerr << "Can't find service: " << services[i] << endl;
}
}
return 0;
}
而这些service是什么时候注册的呢,可以看一下services\java\com\android\server\am\ActivityManagerService.java文件。
有个static setSystemProcess()方法:
public static void setSystemProcess() {
try {
ActivityManagerService m = mSelf;
ServiceManager.addService("activity",m);
ServiceManager.addService("meminfo",new MemBinder(m));
ServiceManager.addService("gfxinfo",new GraphicsBinder(m));
if (MONITOR_cpu_USAGE) {
ServiceManager.addService("cpuinfo",new cpuBinder(m));
}
ServiceManager.addService("permission",new PermissionController(m));
//...
} catch (PackageManager.NameNotFoundException e) {
//...
}
}
可以看到对应 gfxinfo所对应的service(Binder)是 GraphicsBinder,它是ActivityManagerService 的一个内部类。
其实现:
static class GraphicsBinder extends Binder {
ActivityManagerService mActivityManagerService;
GraphicsBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd,String[] args) {
if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump gfxinfo from from pid="
+ Binder.getCallingPid() + ",uid=" + Binder.getCallingUid()
+ " without permission " + android.Manifest.permission.DUMP);
return;
}
mActivityManagerService.dumpGraphicsHardwareUsage(fd,args);
}
}
前面调用的dump最终会调用ActivityManagerService.dumpGraphicsHardwareUsage方法。
从代码中跟进这个方法:
调用ApplicationThread的dumpGfxInfo方法
r.thread.dumpGfxInfo(tp.getWriteFd().getFileDescriptor(),args);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
//..
}
}
}
}
" data-snippet-id="ext.5efc611ebe1ccf895e31f1f8e7de1b0e" data-snippet-saved="false" data-codota-status="done">final void dumpGraphicsHardwareUsage(FileDescriptor fd,String[] args) {
//...
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
if (r.thread != null) {
//..
try {
TransferPipe tp = new TransferPipe();
try {
//这里r是processRecord,即最后调用ApplicationThread的dumpGfxInfo方法
r.thread.dumpGfxInfo(tp.getWriteFd().getFileDescriptor(),args);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
//..
}
}
}
}
继续跟进,查看ApplicationThread实现。该类是ActivtyThread的一个内部类,该ActivtyThread.java文件路径core\java\android\app\ActivityThread.java。
ApplicationThread集成自ApplicationThreadNative,这里主要看dumpGfxInfo方法。
private class ApplicationThread extends ApplicationThreadNative {
//...
@Override
public void dumpGfxInfo(FileDescriptor fd,String[] args) {
dumpGraphicsInfo(fd);
WindowManagerImpl.getDefault().dumpGfxInfo(fd);
}
}
它先调用了ApplicationThread的dumpGraphicsInfo()方法,然后又调用了默认WindowManagerImpl实例的dumpGfxInfo()。
先看ApplicationThread.dumpGraphicsInfo方法:
private native void dumpGraphicsInfo(FileDescriptor fd);
就一行代码,调用了native方法,其实现实在core\jni\android_view_GLES20Canvas.cpp
文件中。(注:最新的源码版本在core/jni/android_view_DisplayListCanvas.cpp)
static void
android_app_ActivityThread_dumpGraphics(JNIEnv* env,jobject javaFileDescriptor) {
#ifdef USE_OPENGL_RENDERER
int fd = jniGetFDFromFileDescriptor(env,javaFileDescriptor);
android::uirenderer::DisplayList::outputLogBuffer(fd);
#endif // USE_OPENGL_RENDERER
}
执行的是DisplayList 的outputLogBuffer()。代码在libs\hwui\DisplayListRenderer.cpp
void DisplayList::outputLogBuffer(int fd) {
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
if (logBuffer.isEmpty()) {
return;
}
FILE *file = fdopen(fd,"a");
fprintf(file,"\nRecent DisplayList operations\n");
logBuffer.outputCommands(file,OP_NAMES); //输出DisplayList
String8 cachesLog;
Caches::getInstance().dumpMemoryUsage(cachesLog); //dumpCache
fprintf(file,"\nCaches:\n%s",cachesLog.string());
fprintf(file,"\n");
fflush(file);
}
可以看到如图两个关键部分,就是输出DisplayList 和 Caches 的两个调用。
OP_NAMES是对于不同的DrawRecderNode所做相应的操作。
这里跟进Cache.dumpMemoryUsage()方法。位于libs\hwui\Caches.cpp
void Caches::dumpMemoryUsage(String8 &log) {
log.appendFormat("Current memory usage / total memory usage (bytes):\n");
log.appendFormat(" TextureCache %8d / %8d\n",textureCache.getSize(),textureCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8d\n",layerCache.getSize(),layerCache.getMaxSize());
log.appendFormat(" GradientCache %8d / %8d\n",gradientCache.getSize(),gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",pathCache.getSize(),pathCache.getMaxSize());
log.appendFormat(" CircleShapeCache %8d / %8d\n",circleShapeCache.getSize(),circleShapeCache.getMaxSize());
log.appendFormat(" OvalShapeCache %8d / %8d\n",ovalShapeCache.getSize(),ovalShapeCache.getMaxSize());
log.appendFormat(" RoundRectShapeCache %8d / %8d\n",roundRectShapeCache.getSize(),roundRectShapeCache.getMaxSize());
log.appendFormat(" RectShapeCache %8d / %8d\n",rectShapeCache.getSize(),rectShapeCache.getMaxSize());
log.appendFormat(" ArcShapeCache %8d / %8d\n",arcShapeCache.getSize(),arcShapeCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n",dropShadowCache.getSize(),dropShadowCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
const uint32_t size = fontRenderer.getFontRendererSize(i);
log.appendFormat(" FontRenderer %d %8d / %8d\n",size);
}
log.appendFormat("Other:\n");
log.appendFormat(" FboCache %8d / %8d\n",fboCache.getSize(),fboCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8d\n",patchCache.getSize(),patchCache.getMaxSize());
uint32_t total = 0;
total += textureCache.getSize();
total += layerCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
total += roundRectShapeCache.getSize();
total += circleShapeCache.getSize();
total += ovalShapeCache.getSize();
total += rectShapeCache.getSize();
total += arcShapeCache.getSize();
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
total += fontRenderer.getFontRendererSize(i);
}
log.appendFormat("Total memory usage:\n");
log.appendFormat(" %d bytes,%.2f MB\n",total / 1024.0f / 1024.0f);
}
及相关的打印信息。
回到前面 ApplicationThread的第二个调用WindowManagerImpl.getDefault().dumpGfxInfo(fd);。在拿到WindowManagerImpl默认实例后执行的动作,参见core\java\android\view\WindowManagerImpl.java
public void dumpGfxInfo(FileDescriptor fd) {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new PrintWriter(fout);
try {
synchronized (this) {
if (mViews != null) {
pw.println("View hierarchy:");
final int count = mViews.length;
int viewsCount = 0;
int displayListsSize = 0;
int[] info = new int[2];
for (int i = 0; i < count; i++) {
ViewRootImpl root = mRoots[i];
root.dumpGfxInfo(pw,info);
String name = root.getClass().getName() + '@' +
Integer.toHexString(hashCode());
pw.printf(" %s: %d views,%.2f kB (display lists)\n",info[0],info[1] / 1024.0f);
viewsCount += info[0];
displayListsSize += info[1];
}
pw.printf("\nTotal ViewRootImpl: %d\n",count);
pw.printf("Total Views: %d\n",viewsCount);
pw.printf("Total DisplayList: %.2f kB\n\n",displayListsSize / 1024.0f);
}
}
} finally {
pw.flush();
}
}
通过ViewRootImpl的dumpGfxInfo方法,将View数量和占用大小记录并输出。
其中,core\java\android\view\ViewRootImpl.java的实现,加入了递归调用
public void dumpGfxInfo(PrintWriter pw,int[] info) {
if (mView != null) {
getGfxInfo(mView,info);
} else {
info[0] = info[1] = 0;
}
}
private void getGfxInfo(View view,int[] info) {
DisplayList displayList = view.mDisplayList;
info[0]++;
if (displayList != null) {
info[1] += displayList.getSize();
}
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
getGfxInfo(group.getChildAt(i),info);
}
}
}
以上,就是关于 gfxinfo 的dumpsys 命令实现。其他命令也是相同方式,不再赘述。感兴趣的可以自行查看相关内容。
最后,附上一个 利用 dumpsys 输出数据到界面的小例子。