用文字札记描绘自己 android学习之路
转载请保留出处 by Qiao
http://blog.csdn.net/qiaoidea/article/details/72943797
之前在研究png 和svg在绘制过程中的内存占用和绘制效率对比问题的时候,使用了比较便捷的adb shell 命令 adb shell dumpsys gfxinfo来查看具体数据。由于使用需要,就稍微跟进了一下代码,这里记录下命令行调用输出的详细过程。
以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)方法.
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 service = sm->checkService(services[i]);
if (service != NULL) {
aout << " " << services[i] << endl;
}
}
}
for (size_t i=0; i 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, PrintWriter pw, 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, pw, args);
}
}
前面调用的dump最终会调用ActivityManagerService.dumpGraphicsHardwareUsage方法。
从代码中跟进这个方法:
final void dumpGraphicsHardwareUsage(FileDescriptor fd,
PrintWriter pw, 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 clazz, 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", i, size, 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, 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",
name, 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 输出数据到界面的小例子。