无限 ImageView 的垃圾收集问题

Garbage Collection issue with infinite ImageViews

我的代码正在以设定的速率创建显示在 UI 上的 ImageView(目标图像),并将在 3 秒后或单击后从主布局中删除。问题是当我创建这些 ImageViews 时,垃圾收集会导致延迟,这会延迟创建更多 ImageViews

在这里,我产生了在 while 循环中创建 ImageView 的调用:

//for each multiple of the spawn rate value, a target will be spawned
                    while (currentTimeOfGame % spawnRate == 0) {
                        createTarget(context);
                        //update the time of game relative to the game's start time
                        currentTimeOfGame = ((new Date().getTime()) - startTime);
                    }

然后在 createTarget() 中创建一个扩展 ImageView 的 Target class 实例。

    //method for creating the targets
    public void createTarget(Context context) {
        //dimensions of the main relative layout
        int width = getWidth();
        int height = getHeight();

        //instantiate a target to spawn
        final Target target = new Target(context);
        target.setParams(width, height);


这里一旦点击它就会从布局中移除。这可以很好地移除目标,但在其中一些之后,它会导致垃圾收集器造成的延迟,这只会延迟下一次生成。

        Runnable thread = () -> target.setOnClickListener(v -> {
            Handler subMainHandler = new Handler(context.getMainLooper());
            Runnable subThread = new Runnable() {
                @Override
                public void run() {
                    target.setVisibility(View.INVISIBLE);
                    removeView(target);
                    target.setImageDrawable(null);
//            Log.d("target clicked", String.valueOf(currentTimeOfGame));
//            Interrupting the thread that tracks the target's timer
                    spawnTarget.interrupt();
                }
            };
            subMainHandler.post(subThread);

        });

这是目标的 class。

public class Target extends androidx.appcompat.widget.AppCompatImageView {

    public long spawnTime = new Date().getTime();

    public Target(Context context) {
        super(context);
        this.setBackgroundResource(R.drawable.target_shape);
        this.setVisibility(View.VISIBLE);
    }

    public void setParams(int width, int height) {
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        Random r = new Random();

        this.setLayoutParams(params);
        int randomLeftMargin = r.nextInt(width);
        int randomTopMargin = r.nextInt(height);
        params.leftMargin = randomLeftMargin;
        params.topMargin = randomTopMargin;
        this.setLayoutParams(params);
    }

}

这是目标的XML文件

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/PrimaryBlue" />
    <stroke
        android:width="1dp"
        android:color="@color/LightGrey" />
    <size
        android:width="30dp"
        android:height="30dp" />
</shape>

如何防止垃圾收集器造成长达 300 毫秒的延迟?这与每次创建新 Target class 时创建 Target 的新实例有关吗?

编辑:

用于处理每个目标生命周期的 3 秒计时器的代码。只要目标在 3 秒内未被点击,3 秒后,目标就会被移除。每次扣除一条生命,一旦生命为 0,则不再生成目标。延迟的问题是由这段代码引起的,其中玩家仍然有生命并且目标被点击移除,所以这个线程被 onClickListener 中断。

       Thread handleTarget = new Thread() {
            public void run() {

                //updating the location of the target to ensure it doesn't overlap with the edge
                Runnable setTargetParamsThread = () -> {
                    int targetWidth = target.getMeasuredWidth();
                    int targetHeight = target.getMeasuredHeight();
                    if (targetHeight != 0 && targetWidth != 0) {
                        target.setParams(width, height, targetWidth, targetHeight);
                        target.setVisibility(View.VISIBLE);
                    }
                };
                mainHandler.post(setTargetParamsThread);


                while (true) {
                    //check if the target has reached 3 seconds without being tapped
                    if (new Date().getTime() > target.spawnTime + 3000) {

                        if (lives == 0) {
                            gameOver = true;
                            //discontinue of the number of lives = 0 as the game is over
                            target.setClickable(false);

                            //fade out animation for target on end of game for remaining targets
                            Animation fadeOut = new AlphaAnimation(1, 0);
                            fadeOut.setDuration(250);
                            AnimationSet animFadeOut = new AnimationSet(true);
                            animFadeOut.addAnimation(fadeOut);
                            target.setAnimation(animFadeOut);

                            Handler mainHandler = new Handler(context.getMainLooper());
                            Runnable thread = () -> removeView(target);
                            mainHandler.post(thread);

                            Thread.currentThread().interrupt();
                        }

                        //if the target is visible remove it and deduct a life.
                        else if (target.getVisibility() == View.VISIBLE) {
                            //remove the target and deduct a life
                            target.setImageDrawable(null);
                            Handler mainHandler2 = new Handler(context.getMainLooper());
                            Runnable thread = () -> removeView(target);
                            mainHandler2.post(thread);

//                            Log.d("target removed", String.valueOf(currentTimeOfGame));
                            lives--;
//                            Log.v("lives", String.valueOf(gameOver));
                            //discontinue the thread for this target once it has been removed as it no longer needs to be referenced
                            Thread.currentThread().interrupt();
                        }
                        break;
                    }
                }
            }
        };

替换匿名内部 类 并处理线程中断解决了该问题。