Android AdView leaking, probably due to some inter

2019-05-18 12:57发布

问题:

I display an AdMob ad banner within a window created by a fragment. This works, but I'm getting strange leaking problems I don't understand yet. When I open and close the app a lot of times, the ad fragment is properly destroyed every time, but the MainActivity which controls this fragment is leaking:

The guilty one is this line in code:

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

(You can see below that I already tried to nullify the adRequest, but no result.) Anyway, when I comment these two lines, the leaks don't occur. I tested it twice, because it was hard to believe that this invocation leaks, but it does. Here is the fragment code. As you can see, I even nullify everything that is possible in onDestroy(). I can assure that onDestroy() is called. loadAd starty any background thread. I guess that has to do with that problem.

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();
    }
}

回答1:

I fortunately found the solution. You have to manually kick the AdView out of the surrounding layout, before the fragment gets destroyed. No leak anymore after I launched and closed the app about 20 times. Here is the updated fragment:

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;
        }
    }
}