Xamarin Android:当离开当前Activity转到另一个Activity时,Finalizer没有被调用

8

在离开活动后,Finalizer 永远不会被调用。这是否意味着即使我转到下一个活动,该活动仍然存在?

namespace XamarinTest {
[Activity(Label = "XamarinTest", Icon = "@drawable/icon")]
public class MainActivity : Activity {
    private int count = 1;

    private TextView density;

    protected override void OnCreate(Bundle bundle) {
        base.OnCreate(bundle);
        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.ScreenData);
        density = FindViewById<TextView>(Resource.Id.Density);

        var pendingInent = new Intent();
        pendingInent.SetFlags(ActivityFlags.ClearTop);
        pendingInent.SetClass(this, typeof(TestActivity));
        StartActivity(pendingInent);
        Finish();
    }


    ~MainActivity() {

        Console.WriteLine("Finalizer called");
    }

  protected override void Dispose(bool disposing){
        if (disposing) {
            density.Dispose();
            density = null;
        }
        base.Dispose(disposing);
    }

  }
}
1个回答

29

实际上,这非常复杂;关于该活动是否仍然存在的简短回答是肯定和否定。只要您正确清理了Activity的资源,垃圾收集器最终会清理它。

关于清理,重要的是要知道,Xamarin 不鼓励使用finalizer(从幻灯片44开始)。原因如下:

  • 不能保证它们在任何期限内运行。
  • 它们不按特定顺序运行。
  • 它们使对象存活时间更长。
  • GC 不知道未托管的资源。

因此,使用 finalizer 执行清理是错误的做法... 如果您想确保MainActivity被销毁,请在其OnDestroy回调中手动释放Activity

protected override void OnDestroy ()
{
    base.OnDestroy ();
    this.Dispose (); // Sever java binding.
}

这将导致Mono中断对等对象连接,并在下一次垃圾回收周期(GC.Collect(GC.MaxGeneration))期间销毁活动。根据文档:

为了缩短对象的生命周期,应调用Java.Lang.Object.Dispose()。这将手动“切断”对象在两个虚拟机之间的连接,通过释放全局引用,从而使对象更快地被收集。

请注意调用顺序,必须在任何调用返回到Android领域的代码(Fragment, Activity, Adapter)之后调用this.Dispose(),为什么呢?因为现在Java和.NET之间所有连接都已经断开,以便让Android回收资源,所以使用Android领域对象的任何代码都将失败。

现在,来看一些用于检测活动泄漏的调试技术。要验证您的Activity是否被清理,请将以下代码添加到应用程序入口Activity的OnCreate方法中:

var vmPolicy = new StrictMode.VmPolicy.Builder ();
StrictMode.SetVmPolicy (vmPolicy.DetectActivityLeaks().PenaltyLog().Build ());

这使得 StrictMode 成为一个有用的调试工具,它可以高兴地通知你何时泄漏了资源。当你的应用程序活动没有正确释放时,它会将类似于以下内容转储到输出流中:


[StrictMode] class activitydispose.LeakyActivity; instances=2; limit=1
[StrictMode] android.os.StrictMode$InstanceCountViolation: class activitydispose.LeakyActivity; instances=2; limit=1
[StrictMode]    at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)

结合 Dispose() 调用,您可以检查活动是否被释放。以下是在 Xamarin.Android 中通常如何处理 Activity 及其资源:

结合 Dispose() 调用,您可以检查活动是否被释放。以下是在 Xamarin.Android 中通常如何处理 Activity 及其资源:

protected override void Dispose (bool disposing)
{
    // TODO: Dispose logic here.
    base.Dispose (disposing);
    GC.Collect(GC.MaxGeneration); // Will force cleanup but not recommended.
}

protected override void OnDestroy ()
{
    if (density != null) { // Release Java objects (buttons, adapters etc) here
        density.Dispose ();
        density = null;
    }
    base.OnDestroy ();
    this.Dispose (); // Sever java binding.
}

5
这其实非常复杂。 - rarrarrarrr
1
这些信息非常有用。StrictMode很棒。 - xleon
StrictMode似乎只能在Activities中工作,有没有办法让它也适用于Fragments呢?因为StrictMode似乎不会显示关于Fragments的任何泄漏信息。 - Huy.Vu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接