垃圾收集问题与无限的ImageViews

huangapple go评论72阅读模式
英文:

Garbage Collection issue with infinite ImageViews

问题

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

这是一个创建ImageView的调用示例,在while循环中进行:

//对于每个出现率值的倍数,将生成一个目标
while (currentTimeOfGame % spawnRate == 0) {
    createTarget(context);
    //相对于游戏开始时间更新游戏的当前时间
    currentTimeOfGame = ((new Date().getTime()) - startTime);
}

然后在createTarget()中,我创建了一个扩展了ImageView的Target类的实例:

//用于创建目标的方法
public void createTarget(Context context) {
    //主相对布局的尺寸
    int width = getWidth();
    int height = getHeight();

    //实例化要生成的目标
    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);
            spawnTarget.interrupt();
        }
    };
    subMainHandler.post(subThread);
});

这是Target类的定义:

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类的新实例有关?

编辑:

处理每个目标生命周期的3秒计时器的代码。如果目标在3秒内未被点击,则会将其移除;每次都会减少一个生命值,一旦生命值为零,则不再生成目标。延迟的问题是由以下代码块引起的,其中玩家仍然有生命,但目标在被点击后被移除,因此这个线程会被点击事件中的onClickLister中断。

Thread handleTarget = new Thread() {
    public void run() {
        // 更新目标的位置以确保它不会与边缘重叠
        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) {
            // 检查目标是否在3秒内未被点击
            if (new Date().getTime() > target.spawnTime + 3000) {
                if (lives == 0) {
                    // 游戏结束
                    // ...
                    Thread.currentThread().interrupt();
                } else if (target.getVisibility() == View.VISIBLE) {
                    // 移除目标并减少生命值
                    // ...
                    Thread.currentThread().interrupt();
                }
                break;
            }
        }
    }
};
英文:

My code is creating ImageViews (image of a target) at a set rate that are displayed on the UI and will be removed from the main layout after 3 seconds or once it has been clicked on. The problem is the garbage collection causes delay when I create these ImageViews, which delays the creation of further ImageViews

Here I spawn call for the ImageView to be created in a while loop:

//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&#39;s start time
                        currentTimeOfGame = ((new Date().getTime()) - startTime);
                    }

And then in createTarget() I create an instance of the Target class which extends ImageView.

    //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);


Here it is removed from the layout once it is clicked. This removes the targets fine but after a few of them, it causes delays caused by the garbage collector, which only delays the next spawn.

        Runnable thread = () -&gt; target.setOnClickListener(v -&gt; {
            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(&quot;target clicked&quot;, String.valueOf(currentTimeOfGame));
//            Interrupting the thread that tracks the target&#39;s timer
                    spawnTarget.interrupt();
                }
            };
            subMainHandler.post(subThread);

        });

This is the class for the Target.

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);
    }

}

This is the XML file of the target

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

How do I prevent the garbage collector from causing delays up to 300 milliseconds? Is this to do with creating a new instance of the Target class every time I create a new Target?

EDIT:

Code for handling the 3 second timer for each targets life span. a target is removed whenever a target isn't clicked within 3 seconds, after 3 seconds. a life is deducted each time, and once there are 0 lives, no more targets are spawned. The issue with the delay is caused by this block of code where the player still has lives and the target is removed with a click, so this thread is interrupted by the onClickListener.

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

                //updating the location of the target to ensure it doesn&#39;t overlap with the edge
                Runnable setTargetParamsThread = () -&gt; {
                    int targetWidth = target.getMeasuredWidth();
                    int targetHeight = target.getMeasuredHeight();
                    if (targetHeight != 0 &amp;&amp; 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() &gt; 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 = () -&gt; 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 = () -&gt; removeView(target);
                            mainHandler2.post(thread);

//                            Log.d(&quot;target removed&quot;, String.valueOf(currentTimeOfGame));
                            lives--;
//                            Log.v(&quot;lives&quot;, 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;
                    }
                }
            }
        };

答案1

得分: 0

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

英文:

Replacing anonymous inner classes and handling Thread interruptions resolved the issue.

huangapple
  • 本文由 发表于 2020年9月14日 05:28:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/63875762.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定