这次遇到的问题发生在了listview的绘制上,也是研究systrace的时候偶然发现的。
命令:
python systrace.py -b 32768 -o mytrace.html sched gfx view am dalvik binder_driver core_services -a <包名>
解释一下命令
systrace是android提供的一个性能优化工具。
路径在android sdk/platform-tools/,是一个Python脚本,所以需要Python环境,我用的Python2.7。
说一下参数,这里只用了一部分:
-b 即buffer大小
-o output,结果输出到哪里
sched cpu的调度信息
gfx 图像图形相关的
view view相关
am ActivityManager
dalvik java虚拟机,gc啥的
binder_driver binder相关的,对这块我还是小白
core_services 核心服务的情况
-a 你的包名
掉帧
首先,我们先找到自己的进程,可以看到一堆F,那种红色的F就是有问题,准确的说,不是绿色的都有问题。
上边这个就是有问题的,我们放大一些看一下。明显layout过程太长了,超过了一帧,即16ms。
点击layout,发现耗时24ms, layout里边有一些inflate,通过查看listview的代码,我们可以发现这么几个地方:
ref: android.widget.AbsListView#obtainView
View obtainView(int position, boolean[] outMetadata) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); // 搜集trace
outMetadata[0] = false;
// Check whether we have a transient state view. Attempt to re-bind the
// data and discard the view if we fail.
final View transientView = mRecycler.getTransientStateView(position);
if (transientView != null) {
final LayoutParams params = (LayoutParams) transientView.getLayoutParams();
// If the view type hasn't changed, attempt to re-bind the data.
if (params.viewType == mAdapter.getItemViewType(position)) {
final View updatedView = mAdapter.getView(position, transientView, this); // adapter#getView 这是我们的方法,我们会在这里inflate
// If we failed to re-bind the data, scrap the obtained view.
if (updatedView != transientView) {
setItemViewLayoutParams(updatedView, position);
mRecycler.addScrapView(updatedView, position);
}
}
outMetadata[0] = true;
// Finish the temporary detach started in addScrapView().
transientView.dispatchFinishTemporaryDetach();
return transientView;
}
final View scrapView = mRecycler.getScrapView(position);
final View child = mAdapter.getView(position, scrapView, this); // adapter#getView 这是我们的方法,我们会在这里inflate
if (scrapView != null) {
if (child != scrapView) {
// Failed to re-bind the data, return scrap to the heap.
mRecycler.addScrapView(scrapView, position);
} else if (child.isTemporarilyDetached()) {
outMetadata[0] = true;
// Finish the temporary detach started in addScrapView().
child.dispatchFinishTemporaryDetach();
}
}
if (mCacheColorHint != 0) {
child.setDrawingCacheBackgroundColor(mCacheColorHint);
}
if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
setItemViewLayoutParams(child, position);
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mAccessibilityDelegate == null) {
mAccessibilityDelegate = new ListItemAccessibilityDelegate();
}
if (child.getAccessibilityDelegate() == null) {
child.setAccessibilityDelegate(mAccessibilityDelegate);
}
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return child;
}
所以是我们在adapter的getView方法里inflate过多导致的,应该复用view,避免多次inflate。同时,可以看出来inflate是一个比较耗时的方法。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!