Android: how to solve adview CPU consuming

adview can consume CPU when the application is in the background… This is a very common problem. Even if the application in the background or has been stopped, it can consume the CPU continuously.

Other symptom, if you disable ads, the CPU usage is minimal or zero.

This is caused by several reasons including possible adview bug too. Here, you can see how to solve the high CPU usage problem:

The first one is to destroy the adview in onDestroy() and onPause() (don’t forget to recreate it in onResume() and onCreate()):

private void destroyAdView() {
    if (adview != null) {
        adview.destroy();               
    }   
}

@Override
public void onDestroy() {
    destroyAdView();
    super.onDestroy();
}

@Override
public void onPause() {
    destroyAdView();
    super.onDestroy();
}

In newer admob sdk you can call adview.onPause() and adview.onResume() functions, but my experience that sometimes they don’t do what they promise.

If adview still consumes CPU, there is a second solution: removing the full adview from the layout, not only destroying it:

private void destroyAdView() {
    if (adview != null) {
        // In my case the adview has been added to a relativelayout
        RelativeLayout layout = (RelativeLayout) mainView.findViewById(R.id.adRelativeLayout);
        layout.removeView(adview);
        adview.destroy();               
    }   
    }

The two solutions above should solve the high CPU usage problem. Excepting only one case:
If you are using the new Google Play services library for adview. Google will only support this admob library after august, 2014.
It seems there is a bug in the library:
https://groups.google.com/forum/#!topic/google-admob-ads-sdk/1aidjtsin8A
https://groups.google.com/forum/#!topic/google-admob-ads-sdk/Qu4G19NFAuI

So if you experience high CPU usage after switching to the new library, you can use a workaround (pausing webview can have other effects too, so use it for your own risk):

@Override
    public void onDestroy() {
        destroyAdView();
        super.onDestroy();
    }
private void destroyAdView() {
    if (adview != null) {
        // In my case the adview has been added to a relativelayout
        RelativeLayout layout = (RelativeLayout) mainView.findViewById(R.id.adRelativeLayout);
        layout.removeView(adview);
        pauseWebView(adview);
        adview.destroy();               
    }   
    }

private void pauseWebView(ViewGroup v) {
    for (int i = 0; i < v.getChildCount(); i++) {
        View child = v.getChildAt(i);
        if (child instanceof WebView) {
            ((WebView) child).pauseTimers();
        }   
        pauseWebView((ViewGroup) child);
    }   
}

And finally, here is how I am creating the adview and a full example:

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
createAdView();
...
}
private void createAdView() {
    if (adview == null) {
        RelativeLayout layout = (RelativeLayout) mainView.findViewById(R.id.adRelativeLayout);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);

        layout.setLayoutParams(lp);
        RelativeLayout.LayoutParams adsParams = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        adsParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        adsParams.addRule(RelativeLayout.CENTER_IN_PARENT);

        adview = new AdView(fragmentActivity);
        adview.setAdSize(AdSize.BANNER);
        adview.setAdUnitId("ca-xxxx");
        final AdListener listener = new AdListener() {
            @Override
            public void onAdLoaded() {
                //Here we will display the adview if there is ad
                adview.setVisibility(View.VISIBLE);
                super.onAdLoaded();
            }
        };

        adview.setAdListener(listener);

        //This needs to remove empty space when ad is not loaded, for example no internet conn.
        adview.setVisibility(View.GONE);

        layout.addView(adview, adsParams);

        AdRequest adRequest = new AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
        .build();
        adview.loadAd(adRequest);
    }   
}

void resumeWebView(ViewGroup v) {
        for (int i = 0; i < v.getChildCount(); i++) {
            View child = v.getChildAt(i);
            if (child instanceof WebView) {
                ((WebView) child).resumeTimers();
            }
            try {                
                resumeWebView((ViewGroup) child);
            } catch (Exception e) {
                e.printStackTrace();
            }            
        }
    }

void pauseWebView(ViewGroup v) {     
        for (int i = 0; i < v.getChildCount(); i++) {
            View child = v.getChildAt(i);
            if (child instanceof WebView) {
                ((WebView) child).pauseTimers();
            }
            try {                
                pauseWebView((ViewGroup) child);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

private void destroyAdView() {
        if (adview != null) {
            RelativeLayout layout = (RelativeLayout) mainView.findViewById(R.id.relativeLayout);
            layout.removeView(adview);            
            adview.destroy();            
            adview = null;
        }

    }

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

    @Override
    public void onResume() {        
        if (adview != null) {
            adview.resume();
            resumeWebView(adview);
        }
        super.onResume();
    }

@Override
    public void onDestroy() {     
        destroyAdView();
        super.onDestroy();
    }

Advertisements

7 Responses to Android: how to solve adview CPU consuming

  1. gregko says:

    Thank you for posting this, Any idea if the pause/resumeWebView calls are still necessary with the latest versions of Google Services, as of beginning of Jan. 2015? My app seems to run into strange problems and lockups if I use them, however if I only use adview.pause() and adview.resume(), the high CPU usage when my activity is in background goes away.

    • PZolee says:

      Could you more specify this strange problem?

      • gregko says:

        OK, I finally went back to re-examine what was wrong. The trouble was that I had two activities in my app, that display banner ads. When I called WebView pauseTimers() in one of them, then went to create the other activity and its AdView, the ads never loaded. Calling resumeWebView() was not helping, because my newly created AdView for the new activity did not even have any children yet. I had to access a stored object of my main activity, find its AdView and resumeWebView() there, then everything started working in the 2nd activity as well.

  2. gregko says:

    Here is more info, the source of my troubles: Quoting from Android SDK WebView docs: pauseTimers() “Pauses all layout, parsing, and JavaScript timers for all WebViews.” So if you have an app which uses WebView elsewhere and you pause the timers for AdView, you’re in trouble. It seems that the correct action would be to use WebView methods onPause () and onResume() instead. Quoting again from http://developer.android.com/reference/android/webkit/WebView.html:

    public void onPause ()
    Added in API level 11
    Pauses any extra processing associated with this WebView and its associated DOM, plugins, JavaScript etc. For example, if this WebView is taken offscreen, this could be called to reduce unnecessary CPU or network traffic. When this WebView is again “active”, call onResume(). Note that this differs from pauseTimers(), which affects all WebViews.

  3. gregko says:

    Since nobody else comments on my investigation here, my final conclusions:

    1. Indeed, calling just adView.pause(); does not stop Google Ads component from consuming CPU even if the app is in background and ads are not visible.
    2. Finding all the WebViews inside a our adView and calling onPause() and onResume() WevView methods on them does not solve the problem of the unnecessary CPU consumption.
    3. Only the call to WebView pauseTimers() and resumeTimers() methods as the author of the above post proposes does stop the unnecessary CPU consumption.
    4. Finding recursively all the WebViews and calling pauseTimers() and resumeTimers() on all of them is unnecessary, since one such call “Pauses (or resumes – g.) all layout, parsing, and JavaScript timers for all WebViews. (within a process – g.)” – see WebView component docs.
    5. If you use a WebView anywhere else in your app – maybe in other activities, you must resumeTimers() for it, or it won’t work right. Also, be aware that a WevView may be constructed temporarily and used by some library functions you use in your project, without you explicitly knowing about it. It could be e.g. a prompt to login to some web site, social network etc. Such WebView may again not work right, if pauseTimers() was called in your process somewhere and you did not resume them. USE CAUTION AND TEST all you can.

    It’s really a shame, that Google and AdMob handled us such a nasty surprise with their ad component (constant CPU consumption even if you background the app, hide their component, even pause() it with their own API call…

    • PZolee says:

      Thank you for your detailed comment! About google: I agree with you sometimes they have terrible bugs in google play services, and they fix them unbelievable slow… and the biggest problem: users will blame you and not google if your app consumes the resources or crashes due to google fault…

  4. max says:

    Hi, it doesn’t seem to work on
    Nexus 7 / Android 5.0.2
    Samsung S5 / Android 4.4
    😦
    It seems to work on
    Samsung S3mini / Android 4.1.2
    Thank you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: