线程随着时间的推移变慢
Thread slows down over time
我正在尝试制作彩虹色标题(随时间变化),这是 onCreate:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
new Thread(TitleColorRunnable).start();
}
对应的runnable:
Runnable TitleColorRunnable = new Runnable()
{
TextView title;
int titleLength;
Spannable spannable;
int i;
float mainHue = 15;
float hue;
int color;
@Override
public void run()
{
title = findViewById(R.id.titleTextView);
title.setText("TITLE EXAMPLE", TextView.BufferType.SPANNABLE);
titleLength = title.length();
spannable = (Spannable) title.getText();
while (true)
{
for (i = 0; i < titleLength - 1; i++)
{
hue = (mainHue - i) % 360;
color = Color.HSVToColor(new float[]{hue, 1, 1});
title.post(new Runnable()
{
@Override
public void run()
{
spannable.setSpan(new ForegroundColorSpan(color), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
});
}
mainHue++;
if (mainHue == 360)
{
mainHue = 0;
}
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
};
这个线程随着时间的推移变慢,并开始慢慢地破坏整个 UI,直到一切都完全停止。
是否有可能
spannable.setSpan(new ForegroundColorSpan(color), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
继续将新的 ForegroundColorSpan 变量保存到内存中?
请帮忙,谢谢!
参考。该行:
title.post(new Runnable()...
无法保证在您下次调用 title.post()
时 Runnable 已完成并已出队。您应用程序 Looper 中的 Runnables 队列可能积压得很厉害。 IOW,你排队 Runnables 的速度快于它们可以执行的速度,最终你的 UI 线程必须花费所有时间来处理这些,而不是做你想让它做的其他事情,比如响应用户输入等等。
一个影响因素是您的 10 毫秒延迟:Thread.sleep(10)
。 100Hz 对于这种更新来说有点快; 30Hz 应该足够了(对于人类感知),甚至更慢,因为你只是 几乎 改变颜色。
建议修复
- 去掉线程。把你的
TitleColorRunnable
逻辑移到 onCreate()
.
- 将你的 Runnable——你现在作为闭包的那个——声明为最终局部变量。
- 在你的
myRunnable.run()
中,执行 title.postDelayed( myRunnable, 33 )
。这就是诀窍;这就是让迭代继续进行的原因,也是阻止 Looper 队列填满的原因。由于 myRunnable
重新排队 本身 ,队列中永远不会超过一个 myRunnable
。 (这不是递归;您只是利用 Android 消息队列机制来安排您的 Runnable。)33 毫秒用于 30Hz 更新率。
- 要开始整个事情,请将
title.post( myRunnable )
添加到 onCreate()
。
- 改为
mainHue = mainHue + 3;
,以保持颜色以大致相同的速度变化。
显然,使用此解决方案,您必须更改 myRunnable.run()
.
中 mainHue
的状态
我正在尝试制作彩虹色标题(随时间变化),这是 onCreate:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
new Thread(TitleColorRunnable).start();
}
对应的runnable:
Runnable TitleColorRunnable = new Runnable()
{
TextView title;
int titleLength;
Spannable spannable;
int i;
float mainHue = 15;
float hue;
int color;
@Override
public void run()
{
title = findViewById(R.id.titleTextView);
title.setText("TITLE EXAMPLE", TextView.BufferType.SPANNABLE);
titleLength = title.length();
spannable = (Spannable) title.getText();
while (true)
{
for (i = 0; i < titleLength - 1; i++)
{
hue = (mainHue - i) % 360;
color = Color.HSVToColor(new float[]{hue, 1, 1});
title.post(new Runnable()
{
@Override
public void run()
{
spannable.setSpan(new ForegroundColorSpan(color), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
});
}
mainHue++;
if (mainHue == 360)
{
mainHue = 0;
}
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
};
这个线程随着时间的推移变慢,并开始慢慢地破坏整个 UI,直到一切都完全停止。
是否有可能
spannable.setSpan(new ForegroundColorSpan(color), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
继续将新的 ForegroundColorSpan 变量保存到内存中?
请帮忙,谢谢!
参考。该行:
title.post(new Runnable()...
无法保证在您下次调用 title.post()
时 Runnable 已完成并已出队。您应用程序 Looper 中的 Runnables 队列可能积压得很厉害。 IOW,你排队 Runnables 的速度快于它们可以执行的速度,最终你的 UI 线程必须花费所有时间来处理这些,而不是做你想让它做的其他事情,比如响应用户输入等等。
一个影响因素是您的 10 毫秒延迟:Thread.sleep(10)
。 100Hz 对于这种更新来说有点快; 30Hz 应该足够了(对于人类感知),甚至更慢,因为你只是 几乎 改变颜色。
建议修复
- 去掉线程。把你的
TitleColorRunnable
逻辑移到onCreate()
. - 将你的 Runnable——你现在作为闭包的那个——声明为最终局部变量。
- 在你的
myRunnable.run()
中,执行title.postDelayed( myRunnable, 33 )
。这就是诀窍;这就是让迭代继续进行的原因,也是阻止 Looper 队列填满的原因。由于myRunnable
重新排队 本身 ,队列中永远不会超过一个myRunnable
。 (这不是递归;您只是利用 Android 消息队列机制来安排您的 Runnable。)33 毫秒用于 30Hz 更新率。 - 要开始整个事情,请将
title.post( myRunnable )
添加到onCreate()
。 - 改为
mainHue = mainHue + 3;
,以保持颜色以大致相同的速度变化。
显然,使用此解决方案,您必须更改 myRunnable.run()
.
mainHue
的状态