无限 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;
}
}
}
};
替换匿名内部 类 并处理线程中断解决了该问题。
我的代码正在以设定的速率创建显示在 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;
}
}
}
};
替换匿名内部 类 并处理线程中断解决了该问题。