Android AdView 泄漏,可能是由于某些内部后台线程

Android AdView leaking, probably due to some internal background thread

我在片段创建的 window 中显示 AdMob 广告横幅。这行得通,但我遇到了一些我还不明白的奇怪的泄漏问题。当我多次打开和关闭应用程序时,广告片段每次都被正确销毁,但是控制这个片段的 MainActivity 正在泄漏:

有罪的是代码中的这一行:

adRequest = new AdRequest.Builder().addTestDevice(DEVICE_ID_MOTO_G).addTestDevice(DEVICE_ID_ZTE).build();
mAdView.loadAd(adRequest);

(您可以在下面看到我已经尝试使 adRequest 无效,但没有结果。)无论如何,当我评论这两行时,不会发生泄漏。我测试了两次,因为很难相信这个调用会泄漏,但确实如此。这是片段代码。如您所见,我什至取消了 onDestroy() 中所有可能的内容。我可以确保 onDestroy() 被调用。 loadAd 启动任何后台线程。我想这与那个问题有关。

public class SnippetFragment extends Fragment
{
    private AdView mAdView;
    private OnAdFinishedLoadingListener onAdFinishedLoadingListener;
    private Context context;
    private AdRequest adRequest;

    private final String DEVICE_ID_ZTE = "1CA20334345BB1479C43692AFA576456487A48";
    private final String DEVICE_ID_MOTO_G = "131465469A7BE11543543065404B168908CB13C8D1";

    public SnippetFragment(Context context)
    {
        this.context = context;
    }

    @Override
    public void onActivityCreated(Bundle bundle)
    {
        super.onActivityCreated(bundle);

        mAdView = (AdView)getView().findViewById(R.id.adView);
        mAdView.setAdListener(new AdListener()
        {
            @Override
            public void onAdLoaded()
            {
                super.onAdLoaded();

                if (context != null)
                {
                    onAdFinishedLoadingListener = (OnAdFinishedLoadingListener)context;
                    onAdFinishedLoadingListener.onAdLoaded();
                }
            }
        });

        //THIS SOMEHOW LEAKS!
        adRequest = new AdRequest.Builder().addTestDevice(DEVICE_ID_MOTO_G).addTestDevice(DEVICE_ID_ZTE).build();
        mAdView.loadAd(adRequest);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.fragment_snippet, container, false);
    }

    /** Called when leaving the activity */
    @Override
    public void onPause()
    {
        if (mAdView != null)
        {
            mAdView.pause();
        }
        super.onPause();
    }

    /** Called when returning to the activity */
    @Override
    public void onResume()
    {
        super.onResume();
        if (mAdView != null)
        {
            mAdView.resume();
        }
    }

    /** Called before the activity is destroyed */
    @Override
    public void onDestroy()
    {
        adRequest = null;

        if (mAdView != null)
        {
            mAdView.setAdListener(null);
            mAdView.destroy();
            mAdView = null;
        }

        context = null;

        super.onDestroy();
    }
}

我幸运地找到了解决方案。在片段被销毁之前,您必须手动将 AdView 踢出周围的布局。在我启动和关闭应用程序大约 20 次后,不再泄漏。这是更新后的片段:

public class SnippetFragment extends Fragment
{
    private AdView adView;
    private OnAdFinishedLoadingListener onAdFinishedLoadingListener;
    private Context context;
    private LatLng routeDestination;
    private AdRequest adRequest;
    private LinearLayout snippetContent;

    private final String DEVICE_ID_ZTE = "2353496z35752t3524267854";
    private final String DEVICE_ID_MOTO_G = "4568735862378523675427897";

    @Override
    public void onActivityCreated(Bundle bundle)
    {
        super.onActivityCreated(bundle);

        snippetContent = (LinearLayout)getView().findViewById(R.id.snippet_content);
        adView = (AdView)getView().findViewById(R.id.adView);
        adView.setAdListener(new AdListener()
        {
            @Override
            public void onAdLoaded()
            {
                super.onAdLoaded();

                if (context != null)
                {
                    onAdFinishedLoadingListener = (OnAdFinishedLoadingListener)getActivity();
                    onAdFinishedLoadingListener.onAdLoaded();
                }
            }
        });

        adRequest = new AdRequest.Builder().addTestDevice(DEVICE_ID_MOTO_G).addTestDevice(DEVICE_ID_ZTE).build();
        adView.loadAd(adRequest);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.fragment_snippet, container, false);
    }

    @Override
    public void onPause()
    {
        if (adView != null)
        {
            adView.pause();
        }

        super.onPause();
    }

    @Override
    public void onResume()
    {
        super.onResume();

        if (adView != null)
        {
            adView.resume();
        }

    }

    @Override
    public void onDestroy()
    {
        destroyAdView();

        super.onDestroy();
    }

    private void destroyAdView()
    {
        if (adView != null)
        {
            adRequest = null;
            adView.removeAllViews();
            adView.setAdListener(null);
            adView.destroy();
            snippetContent.removeView(adView);

            adView = null;
            snippetContent = null;
        }
    }
}