参见英文答案 >
Kotlin : safe lambdas (no memory leak)?2个
我在活动中有一段简单的代码……
我在活动中有一段简单的代码……
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f,1.0f); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { } }); valueAnimator.start(); } }
如果活动终止,将会有内存泄漏(Leak Canary证明).
但是,当我将此代码转换为相同的Kotlin代码(使用shift-alt-command-k)时,如下所示
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val valueAnimator = ValueAnimator.ofFloat(0.0f,1.0f) valueAnimator.repeatCount = ValueAnimator.INFINITE valueAnimator.addUpdateListener { } valueAnimator.start() } }
内存泄漏不再发生.为什么?是因为匿名类对象被转换为Lambda?
解决方法
这两个版本之间的区别非常简单.
AnimatorUpdateListener
的Java版本包含对外部类的隐式引用(在您的情况下为MainActivity).因此,如果动画在不再需要活动时继续运行,则侦听器会持续保持对活动的引用,从而防止对其进行垃圾回收.
科特林试图在这里变得更聪明.它看到传递给ValueAnimator
的lambda不引用外部作用域中的任何对象(即MainActivity),因此它创建了一个单独的AnimatorUpdateListener
实例,只要你[重新]启动动画,它就会被重用.并且此实例没有对外部作用域的任何隐式引用.
旁注:如果将对外部作用域中某个对象的引用添加到lambda,Kotlin将生成每次动画重新启动时创建更新侦听器的新实例的代码,这些实例将保持隐式对MainActivity的引用(为了访问您决定在lambda中使用的对象所必需的).
另一方面注意:我强烈建议阅读名为“Kotlin in Action”的书,因为它包含了很多有关Kotlin的有用信息,以及我对Kotlin编译器如何选择是否将隐式引用放入外部范围的解释SAM转换后创建的对象是否来自本书.