Загрузка процессора на 10% ~ 20% выше при рендеринге анимации в Android Oreo - PullRequest
2 голосов
/ 10 июня 2019

Когда я адаптировал свое приложение к Android Oreo, загрузка процессора увеличилась на 10% ~ 20% при рендеринге анимации. Тем не менее, до Android 8.0, таких как 7.1.1, 7.0 и 6.0, загрузка процессора приложения составляет около 0,3% при рендеринге анимации.

Я добавляю эту строку в тег приложения манифеста

<application android:hardwareAccelerated="true" ...>

Но это ничего не улучшает.

Пожалуйста, дайте мне несколько советов, как решить эту проблему.

public class AnimView extends FrameLayout {

    private static final String TAG = "AnimView";

    private TextView mCenterText;

    private TextView mCenterUnit;

    private Context mContext;

    private long totalSize = 0;

    private long totalCount = 0;

    private View mRoot;

    private OnAnimListener mOnAnimListener;

    public AnimView(Context context) {
        this(context, null);
    }

    public AnimView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public void setOnAnimListener(OnAnimListener listener) {
        mOnAnimListener = listener;
    }

    private void initView(Context context) {

        mContext = context;

        inflate(mContext, R.layout.anim, this);

        mRoot = findViewById(R.id.anim_layout);

        mCenterText = (TextView) findViewById(R.id.anim_text);
        mCenterText.setTypeface(UIUtil.getHelveticaNeueLTProFonts(mContext));
        mCenterUnit = (TextView) findViewById(R.id.anim_text_unit);

    }

    public void setTotalSize(long size, long count) {
        totalSize = size;
        totalCount = count;
    }

    public void setAnimBackgroundColor(int color) {
        mRoot.setBackgroundColor(color);
    }

    public void startAnim(final OuterSpaceView pointView) {
        long value;
        if (totalSize > 0) {
            value = totalSize;
        } else {
            value = totalCount;
        }
        final ValueAnimator animator = ValueAnimator.ofFloat(value, 0);
        animator.setDuration(3000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float v = (Float) animation.getAnimatedValue();
                setCenterSize(v.longValue());
            }
        });

        animator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                if(totalSize > 0) {
                    setCenterText("0.0");
                }else {
                    setCenterText("0");
                }
                mCenterText.requestLayout();

                pointView.setVisibility(View.GONE);

                if (mOnAnimListener != null) {
                    mOnAnimListener.onAnimFinish();
                }

                animator.cancel();
            }
        });
        mCenterText.post(new Runnable() {
            @Override
            public void run() {
                animator.start();
            }
        });
    }

    private void setCenterSize(long size) {
        if(totalSize > 0) {
            if (size == 0) {
                setCenterText("0.0");
                setCenterUnit("B");
            } else {
                String[] formatSizeSource = CommonUtils.getFormatSizeSource(size);
                setCenterText(formatSizeSource[0]);
                setCenterUnit(formatSizeSource[1]);
            }
        }else {
            setCenterText(String.valueOf(size));
            setCenterUnit(getResources().getString(R.string.sysclear_unit_kuan));
        }

        if (mOnAnimListener != null) {
            mOnAnimListener.onCenterSize(size);
        }
    }

    private void setCenterText(CharSequence text) {
        mCenterText.setText(text);
        mCenterText.setContentDescription(text);
    }

    private void setCenterUnit(CharSequence text) {
        mCenterUnit.setText(text);
        mCenterUnit.setContentDescription(text);
    }

    public static interface OnAnimListener {
        void onAnimFinish();

        void onCenterSize(long size);
    }
}

и код pointView:

interface Ball {
    int getQuadrant();

    Point getPosition();

    int getDistance();

    double getPercentage();

    int getRadius();

    int getAlpha();

    void next();

    boolean isFinished();
}


public class OuterSpaceView extends View {

    public static final String TAG = "OuterSpaceView";

    private static final int MIN_BALL_COUNT = 4;

    private static final int MAX_BALL_COUNT = 9;

    List<Ball> mBallList = new ArrayList<Ball>(); 

    private boolean mInit = false;

    private int mWidth;

    private int mHeight;

    private Point mCenter = new Point();

    private boolean mInOrOut = true;

    private int[] mBallDistribution = new int[5];

    private long mLastAddBallTime = 0;

    public OuterSpaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (!mInit) {
            mInit = true;

            mWidth = getWidth();
            mHeight = getHeight();
            mCenter.x = getWidth() / 2;
            mCenter.y = getHeight() / 2;

            mBallDistribution[0] = Integer.MAX_VALUE;
            Arrays.fill(mBallDistribution, 1, mBallDistribution.length, 0);

            int maxDistance = (int) Math.sqrt(Math.pow(mWidth, 2) + Math.pow(mHeight, 2));
            FlyBallBase.setMaxDistance(maxDistance);
        }

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);

        for (Ball ball : mBallList) {
            int x = ball.getPosition().x;
            int y = ball.getPosition().y;
            int r = ball.getRadius();
            int alpha = ball.getAlpha();

            paint.setAlpha(alpha);
            canvas.drawCircle(x, y, r, paint);
        }

        next();

        invalidate();
    }

    private void next() {

        int needNewBall = calculateNeedBall();
        for (int i = 0; i < needNewBall; i++) {
            addNewBall();
        }

        for (Ball ball : mBallList) {
            ball.next();
        }

        removeFinishedBall();
    }

    public void setFlyDirection(boolean inOrOut) {
        mInOrOut = inOrOut;
    }

    public void clear() {
        mBallDistribution[0] = Integer.MAX_VALUE;
        Arrays.fill(mBallDistribution, 1, mBallDistribution.length, 0);

        mBallList.clear();
    }

    boolean mUnTouchable;

    public void setUnTouchable(boolean value){
        mUnTouchable = value;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mUnTouchable;
    }

    private Map<String, Integer> generateRandomPos() {

        int minCount = Integer.MAX_VALUE;
        int distributionIndex = 0;
        for (int i = 0; i < mBallDistribution.length; i++) {
            if (mBallDistribution[i] < minCount) {
                minCount = mBallDistribution[i];
                distributionIndex = i;
            }
        }

        boolean horizonOrVertical = Math.random() > 0.5;   
        int x = 0;
        int y = 0;
        switch (distributionIndex) {
            case 1:
                if (horizonOrVertical) {

                    x = mWidth / 2 + (int) (mWidth * Math.random());
                    y = -mHeight / 2 + (int) (mHeight / 2 * Math.random());
                } else {

                    x = mWidth + (int) (mWidth / 2 * Math.random());
                    y = -mHeight / 2 + (int) (mHeight * Math.random());
                }
                break;

            case 2:
                if (horizonOrVertical) {

                    x = -mWidth / 2 + (int) (mWidth * Math.random());
                    y = -mHeight / 2 + (int) (mHeight / 2 * Math.random());
                } else {

                    x = -mWidth / 2 + (int) (mWidth / 2 * Math.random());
                    y = -mHeight / 2 + (int) (mHeight * Math.random());
                }
                break;

            case 3:
                if (horizonOrVertical) {

                    x = -mWidth / 2 + (int) (mWidth * Math.random());
                    y = mHeight + (int) (mHeight / 2 * Math.random());
                } else {

                    x = -mWidth / 2 + (int) (mWidth / 2 * Math.random());
                    y = mHeight / 2 + (int) (mHeight * Math.random());
                }
                break;

            case 4:
                if (horizonOrVertical) {

                    x = mWidth / 2 + (int) (mWidth * Math.random());
                    y = mHeight + (int) (mHeight / 2 * Math.random());
                } else {

                    x = mWidth + (int) (mWidth / 2 * Math.random());
                    y = mHeight / 2 + (int) (mHeight * Math.random());
                }
                break;

            default:
                break;
        }

        HashMap<String, Integer> xy = new HashMap<String, Integer>(2);
        xy.put("x", x);
        xy.put("y", y);
        return xy;
    }

    private void addNewBall() {
        Map<String, Integer> pos = generateRandomPos();
        int x = pos.get("x");
        int y = pos.get("y");

        Point newPoint = new Point(x, y);
        int quadrant = posToQuadrant((newPoint.x + mCenter.x) / 2, (newPoint.y + mCenter.y) / 2);

        Ball newBall = null;
        if (mInOrOut) {
            newBall = new FlyIn(newPoint, mCenter, quadrant);
        } else {
            newBall = new FlyOut(mCenter, newPoint, quadrant);
        }

        mBallList.add(newBall);


        mBallDistribution[newBall.getQuadrant()]++;
    }

    private void removeFinishedBall() {
        for (int i = 0; i < mBallList.size(); i++) {
            Ball ball = mBallList.get(i);
            if (ball.isFinished()) {

                int x = ball.getPosition().x;
                int y = ball.getPosition().y;
                mBallDistribution[ball.getQuadrant()]--;

                mBallList.remove(i);
            }
        }
    }


    private int calculateNeedBall() {

        if (mBallList.size() == 0) {

            return 1;
        }

        if (SystemClock.elapsedRealtime() - mLastAddBallTime > 150) {
            mLastAddBallTime = SystemClock.elapsedRealtime();
            return 1;
        }

        return 0;
    }

    /**
     * .                                 mWidth
     * .                                /      \
     * .            |------------2--------------------1-----------|
     * .            |                      |                      |
     * .         /  2            2         2          1           1
     * .            |                      |                      |
     * .mHeight     |------------3--------------------1-----------|
     * .            |                      |                      |
     * .         \  3            3         4          4           4
     * .            |                      |                      |
     * .            |------------3--------------------4-----------|
     */
    private int posToQuadrant(int x, int y) {
        if (x > mCenter.x && y <= mCenter.y) {
            return 1;
        } else if (x <= mCenter.x && y < mCenter.y) {
            return 2;
        } else if (x < mCenter.x && y >= mCenter.y) {
            return 3;
        } else if (x >= mCenter.x && y > mCenter.y) {
            return 4;
        } else {
            return 0;
        }
    }

    public void setBallRadius(int min, int max) {
        FlyBallBase.setBallRadius(min, max);
    }
}

abstract class FlyBallBase implements Ball {

    protected static final int MIN_OPACITY = 30;

    protected static final int MAX_OPACITY = 130;

    protected static int sMinRadius;

    protected static int sMaxRadius;

    protected static int sMaxDistance; 

    protected Point from, to;

    protected Point pos;

    protected int quadrant;

    protected int distance;

    protected int distanceX, distanceY;

    protected int totalDistance;

    protected int totalDistanceX, totalDistanceY;

    protected double totalDistanceProportion; // `distance` / `totalDistance`

    protected double maxDistanceProportion; // `distance` / `sMaxDistance`

    protected int frameCount = 0;

    public FlyBallBase(Point from, Point to, int quadrant) {
        this.from = new Point(from);
        this.to = new Point(to);
        pos = new Point(from);

        this.quadrant = quadrant;

        distance = distanceX = distanceY = 0;
        totalDistanceX = to.x - from.x;
        totalDistanceY = to.y - from.y;
        totalDistance = (int) Math.sqrt(Math.pow(totalDistanceX, 2) + Math.pow(totalDistanceY, 2));

        totalDistanceProportion = maxDistanceProportion = 0.0;
    }

    public static void setBallRadius(int min, int max) {
        sMinRadius = min;
        sMaxRadius = max;
    }

    public static void setMaxDistance(int maxDistance) {
        sMaxDistance = maxDistance;
    }

    @Override
    public int getQuadrant() {
        return quadrant;
    }

    @Override
    public Point getPosition() {
        return pos;
    }

    @Override
    public int getDistance() {
        return distance;
    }

    @Override
    public double getPercentage() {
        return totalDistanceProportion;
    }

    @Override
    public void next() {
        distanceX = pos.x - from.x;
        distanceY = pos.y - from.y;
        distance = (int) Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));
        totalDistanceProportion = (double) distance / totalDistance;
        maxDistanceProportion = (double) distance / sMaxDistance;
        frameCount++;
    }

    @Override
    public boolean isFinished() {
        return pos.x == to.x && pos.y == to.y;
    }
}

class FlyIn extends FlyBallBase {
    public FlyIn(Point from, Point to, int quadrant) {
        super(from, to, quadrant);
    }

    @Override
    public int getRadius() {
        return sMinRadius + (int) ((sMaxRadius - sMinRadius) * (1.0 - totalDistanceProportion));
    }

    @Override
    public int getAlpha() {
        return (int) Math.max(MIN_OPACITY, (MAX_OPACITY * (1.0 - totalDistanceProportion)));
    }

    @Override
    public void next() {
        super.next();

        if (Math.abs(to.x - pos.x) <= 80 && Math.abs(to.y - pos.y) <= 50) {
            pos.x = to.x;
            pos.y = to.y;
        } else {
            double incPercentage = 1.0 - Math.pow(0.95, frameCount);
            int incX = (int) (totalDistanceX * incPercentage);
            int incY = (int) (totalDistanceY * incPercentage);

            pos.x = from.x + incX;
            pos.y = from.y + incY;
        }
    }
}

class FlyOut extends FlyBallBase {

    public FlyOut(Point from, Point to, int quadrant) {
        super(from, to, quadrant);
    }

    @Override
    public int getRadius() {
       // return (int) (sMaxRadius * totalDistanceProportion);
        return sMinRadius + (int) ((sMaxRadius - sMinRadius) * totalDistanceProportion);
    }

    @Override
    public int getAlpha() {
        return (int) Math.max(MIN_OPACITY, (MAX_OPACITY * (1.0 - totalDistanceProportion)));
        // return (int) (MAX_OPACITY * totalDistanceProportion);
    }

    @Override
    public void next() {
        super.next();

        if (Math.abs(to.x - pos.x) <= 80 && Math.abs(to.y - pos.y) <= 50) {
            pos.x = to.x;
            pos.y = to.y;
        } else {
            double incPercentage = 1.0 - Math.pow(0.98, frameCount);
            int incX = (int) (totalDistanceX * incPercentage);
            int incY = (int) (totalDistanceY * incPercentage);

            pos.x = from.x + incX;
            pos.y = from.y + incY;
        }
    }
}

Затем я попытаюсь уменьшить это высокое использование процессора, когда я настроил частоту обновления анимации до 1 кадра / 2 секунды, а загрузка процессора снизилась с 5% ~ 15% до 0,12% ~ 4% ... Итак, действительно ли Google использовал больше ресурсов процессора для продвижения анимации рендеринга в Android Oreo?

...