这次遇到的问题发生在了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 协议 ,转载请注明出处!

WDYDT-20-SharedPreferences探索 上一篇
决定要改改我的简历 下一篇