极其耗费CPU的任务每50ms更换一次ImageView

时间:2013-01-28 12:58:33

标签: android

编辑:添加了赏金。希望这会引起一些关注手头的问题。

我目前正在尝试解决一个问题,即在动画婴儿睡觉的应用程序的主屏幕上连续运行动画。这是通过使用imageView并用下一个替换动画中的当前图像来完成的。因为有超过500个图像,所以这变得非常占用CPU,因为每次更换图像时,都会重新计算整个视图层次。有没有办法在这个动画中不重新计算整个视图层次?

run方法如下所示:

        @Override
    public void run() {
        if (bitmap != null) {
            bitmap.recycle();
        }
        final String name = String.format(Locale.ENGLISH, pattern, current);
        final int id = getResources().getIdentifier(name, "drawable", getPackageName());
        if (id != 0) {
            bitmap = BitmapFactory.decodeResource(getResources(), id);
            view.setImageBitmap(bitmap);
        }
        current++;
        if (runAgain()) {
            frameCounter++;
            long nextAt = firstFrameAt + (frameCounter * interval);
            long delay = nextAt - SystemClock.elapsedRealtime() - 1;
            if (delay > 0) {
                handler.postDelayed(this, delay);
            } else {
                this.run();
            }
        }
    }

编辑:我一直在考虑创建自己的自定义ImageView并覆盖导致问题的任何方法。这是一个可行的解决方案吗?

4 个答案:

答案 0 :(得分:0)

覆盖ImageView和(至少)它的onDraw()方法,使用Canvas.drawBitmap绘制位图。

使用后台线程,您可以预取图像并创建一个小的FIFO位图队列,然后使用计时器机制触发队列中下一个位图的下一次绘制。

答案 1 :(得分:0)

是的,只是阻止布局请求。覆盖onRequestLayout并且不要调用super。您可能需要在父级中执行一些手动onLayout来设置正确的大小(或者如果您是ICS则覆盖布局方法...不能在2.x中标记为final)。合理?

我的容器视图示例:

@Override
public void requestLayout() {

}

// i'm ignoring the widths/heights here an manually setting it to what i know it should be. The children DO need a layout initially or their widths will be 0 and not show. 
 // 

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    int count = getChildCount();
    for (int i=0; i<count; i++) {
        ImageView iv = (ImageView)getChildAt(i);
        iv.layout(0, 0, width, height);
    }
}

所以现在每当孩子发生变化时,我都不必重新验证整个视图层次结构,因为我知道尺寸没有变化。这样可以节省大量CPU。您可能还想考虑这样的事情:Optimized Image View

答案 2 :(得分:0)

根据ImageView的源代码:

/**
 * Sets a drawable as the content of this ImageView.
 * 
 * @param drawable The drawable to set
 */
public void setImageDrawable(Drawable drawable) {
    if (mDrawable != drawable) {
        mResource = 0;
        mUri = null;

        int oldWidth = mDrawableWidth;
        int oldHeight = mDrawableHeight;

        updateDrawable(drawable);

        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }
        invalidate();
    }
}

/**
 * Sets a Bitmap as the content of this ImageView.
 * 
 * @param bm The bitmap to set
 */
@android.view.RemotableViewMethod
public void setImageBitmap(Bitmap bm) {
    // if this is used frequently, may handle bitmaps explicitly
    // to reduce the intermediate drawable object
    setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}

很明显,我们会检查您提供的新Bitmap的界限,以决定是否请求布局。

        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }

根据您的描述,我相信您的图像应该具有完全相同的尺寸,因为您几乎创建了电影效果。所以,检查他们的尺寸。如果它们已经具有相同的尺寸,您应该检查是否真的the entire View Hierachy is re-calculate

附注:

  • 您应该检查图像的大小:大图像的解码也是CPU密集型的。
  • 我建议您在单独的线程上解码下一个图像,然后在50 ms过去时设置它或等到它们通过。换句话说,不要等待处理程序,解码下一个图像并在解码完成时通知。
  • 您在位图上调用recycle!请查看文档: This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap. 我不确定回收是否非常密集,但请遵循本案例中的文档而不要调用它。

答案 3 :(得分:0)

我通过实现自己的自定义ImageView来解决了这个问题,重写了以下两种方法:

@Override
public void setImageBitmap(final Bitmap bitmap) {
    if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {

        // The currently set Drawable
        Bitmap oldBitmap = null;
        try
        {
            oldBitmap = ((BitmapDrawable)this.getDrawable()).getBitmap();   
        }catch(NullPointerException e)
        {
            Log.d("setImageBitmap", "oldBitmap is null");
        }

        if (null != oldBitmap && oldBitmap != bitmap) {
            final int oldWidth = oldBitmap.getWidth();
            final int oldHeight = oldBitmap.getHeight();

            /**
             * Ignore the next requestLayout call if the new Bitmap is the
             * same size as the currently displayed one.
             * */
            mIgnoreNextRequestLayout = oldHeight == bitmap.getHeight()
                    && oldWidth == bitmap.getWidth();
        }
    }

    super.setImageBitmap(bitmap);
}

@Override
public void requestLayout() {
    if (!mIgnoreNextRequestLayout) {
        super.requestLayout();
        Log.d("RequestLayout", "Requesting new layout");
    }
    else
        Log.d("RequestLayout", "Ignoring next requestLayout");

    // Reset Flag so that the requestLayout() will work again
    mIgnoreNextRequestLayout = false;
}