【发布时间】:2022-11-09 02:41:33
【问题描述】:
我正在使用Timer() 对象在Android 中的活动中显示点加载器,并在scheduleAtFixedRate() 的Timer API 中传递TimerTask() 的对象。
这是我的代码 sn-p:-
private fun scheduleTimer() {
timer = Timer()
timer?.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
if (isSingleDir) {
selectedDotPos++
if (selectedDotPos > noOfDots) {
selectedDotPos = 1
}
} else {
if (isFwdDir) {
selectedDotPos++
if (selectedDotPos == noOfDots) {
isFwdDir = !isFwdDir
}
} else {
selectedDotPos--
if (selectedDotPos == 1) {
isFwdDir = !isFwdDir
}
}
}
(scanForActivity(context))?.runOnUiThread {
invalidate()
}
}
}, 0, animDur.toLong())
}
当视图的可见性发生变化时,我确保通过调用Timer::cancel() 来取消计时器。这是代码: -
override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
if (visibility != VISIBLE) {
timer?.cancel()
} else if (shouldAnimate) {
scheduleTimer()
}
}
但是TimerTask 正在泄漏一些内存。我正在使用 Leak Canary 来分析内存泄漏。这是报告:-
寻求帮助来解决这个问题。
我班级的完整代码:-
class LinearDotsLoader : DotsLoaderBaseView
{
private var timer: Timer? = null
var isSingleDir = true
private var diffRadius: Int = 0
private var isFwdDir = true
constructor(context: Context) : super(context) {
initCordinates()
initPaints()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initAttributes(attrs)
initCordinates()
initPaints()
initShadowPaints()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initAttributes(attrs)
initCordinates()
initPaints()
initShadowPaints()
}
override fun initAttributes(attrs: AttributeSet) {
super.initAttributes(attrs)
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.LinearDotsLoader, 0, 0)
this.noOfDots = typedArray.getInt(R.styleable.LinearDotsLoader_loader_noOfDots, 3)
this.selRadius = typedArray.getDimensionPixelSize(R.styleable.LinearDotsLoader_loader_selectedRadius, radius + 10)
this.dotsDistance = typedArray.getDimensionPixelSize(R.styleable.LinearDotsLoader_loader_dotsDist, 15)
this.isSingleDir = typedArray.getBoolean(R.styleable.LinearDotsLoader_loader_isSingleDir, false)
this.expandOnSelect = typedArray.getBoolean(R.styleable.LinearDotsLoader_loader_expandOnSelect, false)
typedArray.recycle()
}
override fun initCordinates() {
diffRadius = this.selRadius - radius
dotsXCorArr = FloatArray(this.noOfDots)
//init X cordinates for all dots
for (i in 0 until noOfDots) {
dotsXCorArr[i] = (i * dotsDistance + (i * 2 + 1) * radius).toFloat()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val calWidth: Int
val calHeight: Int
if (expandOnSelect) {
calWidth = (2 * this.noOfDots * radius + (this.noOfDots - 1) * dotsDistance + 2 * diffRadius)
calHeight = 2 * this.selRadius
} else {
calHeight = 2 * radius
calWidth = (2 * this.noOfDots * radius + (this.noOfDots - 1) * dotsDistance)
}
setMeasuredDimension(calWidth, calHeight)
}
override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
if (visibility != VISIBLE) {
timer?.cancel()
timer?.purge()
} else if (shouldAnimate) {
scheduleTimer()
}
}
private fun scheduleTimer() {
timer = Timer()
val dotsTimerTask = DotsTimerTask()
timer?.scheduleAtFixedRate(dotsTimerTask, 0, animDur.toLong())
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawCircle(canvas)
}
private fun drawCircle(canvas: Canvas) {
for (i in 0 until noOfDots) {
var xCor = dotsXCorArr[i]
if (expandOnSelect) {
if (i + 1 == selectedDotPos) {
xCor += diffRadius.toFloat()
} else if (i + 1 > selectedDotPos) {
xCor += (2 * diffRadius).toFloat()
}
}
var firstShadowPos: Int
var secondShadowPos: Int
if ((isFwdDir && selectedDotPos > 1) || selectedDotPos == noOfDots) {
firstShadowPos = selectedDotPos - 1
secondShadowPos = firstShadowPos - 1
} else {
firstShadowPos = selectedDotPos + 1
secondShadowPos = firstShadowPos + 1
}
if (i + 1 == selectedDotPos) {
selectedCirclePaint?.let {
canvas.drawCircle(
xCor,
(if (expandOnSelect) this.selRadius else radius).toFloat(),
(if (expandOnSelect) this.selRadius else radius).toFloat(),
it
)
}
} else if (showRunningShadow && i + 1 == firstShadowPos) {
canvas.drawCircle(
xCor,
(if (expandOnSelect) this.selRadius else radius).toFloat(),
radius.toFloat(),
firstShadowPaint)
} else if (showRunningShadow && i + 1 == secondShadowPos) {
canvas.drawCircle(
xCor,
(if (expandOnSelect) this.selRadius else radius).toFloat(),
radius.toFloat(),
secondShadowPaint)
} else {
defaultCirclePaint?.let {
canvas.drawCircle(
xCor,
(if (expandOnSelect) this.selRadius else radius).toFloat(),
radius.toFloat(),
it
)
}
}
}
}
var dotsDistance: Int = 15
set(value) {
field = value
initCordinates()
}
var noOfDots: Int = 3
set(noOfDots) {
field = noOfDots
initCordinates()
}
var selRadius: Int = 38
set(selRadius) {
field = selRadius
initCordinates()
}
var expandOnSelect: Boolean = false
set(expandOnSelect) {
field = expandOnSelect
initCordinates()
}
private fun scanForActivity(context: Context?): Activity? {
return when (context) {
null -> null
is Activity -> context
is ContextWrapper -> scanForActivity(context.baseContext)
else -> null
}
}
private fun updateSelectedDot()
{
if (isSingleDir) {
selectedDotPos++
if (selectedDotPos > noOfDots) {
selectedDotPos = 1
}
} else {
if (isFwdDir) {
selectedDotPos++
if (selectedDotPos == noOfDots) {
isFwdDir = !isFwdDir
}
} else {
selectedDotPos--
if (selectedDotPos == 1) {
isFwdDir = !isFwdDir
}
}
}
(scanForActivity(context))?.runOnUiThread {
invalidate()
}
}
private inner class DotsTimerTask: TimerTask(){
override fun run() {
updateSelectedDot()
}
}
}
【问题讨论】:
-
onVisibilityChanged是否在onDestroy()之前被调用?您还需要取消onDestroy内的计时器。您还需要在 cancel 之后使用timer.purge()。 -
@ADM 是的
onVisibilityChanged在onDestroy()之前被调用。我已经验证了这一点。调用timer.purge()是否确保不会有任何内存泄漏?因为我已经尝试过这样做。而且这种内存泄漏仍然存在。 -
不能肯定地说。你到底在哪里有这个方法
scheduleTimer你能添加那个类的完整代码吗?看起来它的 Runnable 导致泄漏,因为你在其中使用了上下文.. 删除context使用并使用主线程处理程序在 UI 线程上发布。 -
@ADM我已经添加了我班级的完整代码。你能看看吗?
标签: android memory memory-leaks timer timertask