handler1是一个泄漏。
我想将handler1代码转换为handler2代码。可以吗?
两个代码有什么区别?
公共类MainActivity扩展了AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// leaks!
Handler handler1 = new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("LOG", "Hello~1");
}
};
Handler handler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.e("LOG", "Hello~2");
return false;
}
});
handler1.postDelayed(new Runnable() {
@Override
public void run() { }
}, 60000);
handler2.postDelayed(new Runnable() {
@Override
public void run() { }
}, 60000);
finish();
}
}
答案 0 :(得分:1)
我在模拟器(android 28)上尝试此代码并转储内存,探查器显示没有泄漏,我也尝试了Leakcanary并显示了相同的结果。这使我怀疑网上文章的准确性。然后我注意到了区别,如果我使用 Kotlin 编写此逻辑,内存不会泄漏,但是使用 Java 代码会泄漏。
后来我发现了一些有趣的东西。在Java中,无论是否使用外部类方法,匿名内部类都会通过构造函数保存外部对象引用。在Kotlin中,如果内部逻辑未使用外部类方法,则匿名内部类将不保留外部对象引用。
答案 1 :(得分:0)
出于泄漏警告的原因,this article解释得很好。
文章引用
在Java中,非静态内部和匿名类持有对其外部类的隐式引用。另一方面,静态内部类则没有。
因此,当您通过匿名类创建handler1
时,它将保存对MainActivity
实例的引用,并且MainActiviy
无法被垃圾回收。
再次引用本文
要解决此问题,请将处理程序子类化到新文件中,或改为使用静态内部类。静态内部类不持有对其外部类的隐式引用,因此不会泄漏活动。如果您需要在Handler中调用外部活动的方法,请让Handler对该活动持有WeakReference,以免意外泄漏上下文。为了解决在实例化匿名Runnable类时发生的内存泄漏,我们将变量设置为该类的静态字段(因为匿名类的静态实例不包含对其外部类的隐式引用):
在本文之后,按如下所示更新代码:
public class MainActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("LOG", "Hello~1");
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler handler1 = new MyHandler();
handler1.postDelayed(new Runnable() {
@Override
public void run() { }
}, 60000);
finish();
}
}
@Michael This Handler class should be static or leaks might occur: IncomingHandler的回答 提供解决方案。
引用@Michael的答案
据我了解,这无法避免潜在的内存泄漏。消息对象包含对mIncomingHandler对象的引用,该对象包含对Handler.Callback对象的引用,该对象包含对Service对象的引用。只要Looper消息队列中有消息,该服务就不会是GC。但是,除非您在消息队列中有很长的延迟消息,否则这不是一个严重的问题。
在您的情况下,handler2
将保留对Handler.Callback
对象的引用。由于Handler.Callback
是由匿名类创建的,因此它将保留对MainActiviy
实例的引用太。因此MainActiviy
实例也不能被垃圾收集。
答案 2 :(得分:0)
我在Kotlin代码中使用了Handler来捕获传入的蓝牙传入消息。该代码没有棉绒泄漏警告:
private val incomingMsgHandler: Handler = Handler { msg ->
msg.obj?.let {
if (it is ByteArray) {
val msgStr = String(it)
setIncomingMessage(msgStr)
}
}
true
}
简而言之,它使用带有lambda回调的Handler构造函数。