Showing AdMob's ads using native activity with Android's NDK

Android's AdMob and native activity | 2/ 1/2013 | Comments: 26

 

AdMob and Native Activity

So, you ported your shining free game/app to Android by using the NDK and now you are stuck: AdMob do not work in your pure native activity application. Panic.

I was facing this issue as well when I decided to put uFall in the ads realm (it was a paid game). Tragic but not fatal. This is what I did to show AdMob in a pure (mixed to the end) native activity.

The mix

Yes, the mix. If you want to use Java things in your native code put away the JNI callback interface for a moment. There is a more clean solution. I want to call to JNI directly from C/C++, not by using callbacks.

For the entire thing I used the command line tools (ndk-build, ant) but I think that making all this working with Eclipse or Android Studio will be easy enough.

This little tutorial assume that you copied the AdMob's jar file to your libs project directory. (See the AdMob SDK for more info).

Step 1

You'll need to Add a Java native activity subclass that will instantiate your native one

Create a new .java (text file) file to your project directory (Change youractivityname with a name of your choice, it will be used in the manifest too):

android

    src

       com

           yourcompany

                 yourapp

                      youractivityname.java

Now open youractivityname.java with your favorite text editor and edit it:

package com.yourcompany. youractivityname;

import android.app.NativeActivity;

import android.widget.PopupWindow;

… (Other imports here)

import com.google.ads.*;

public class youractivityname extends NativeActivity

{

AdView adView;

PopupWindow popUp;

youractivityname _activity;

LinearLayout layout;

LinearLayout mainLayout;

boolean adsinited = false;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Make your custom init here

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

_activity = this;

// Create our ad view here

adView = new AdView(_activity, AdSize.BANNER, "changethiswithyouradmobid");

}

// Our popup window, you will call it from your C/C++ code later

 public void showAdPopup()

 {

if(adsinited)

{

return;

}

if(adView!=null)  {

_activity.runOnUiThread(new Runnable()  {

@Override

public void run()  {

adsinited = true;

// Out popup window

popUp = new PopupWindow(_activity);

// This is the minimum size for AdMob, we need to set this in case our target device run at 320x480 resolution (Otherwise no ad will be shown, see the padding kill below)

popUp.setWidth(320);

popUp.setHeight(50);

popUp.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

popUp.setClippingEnabled(false);

layout = new LinearLayout(_activity);

mainLayout = new LinearLayout(_activity);

// The layout system for the PopupWindow will kill some pixels due to margins/paddings etc… (No way to remove it), so padd it to adjust

layout.setPadding(-5, -5, -5, -5);

       MarginLayoutParams params = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

params.setMargins(0, 0, 0, 0);

layout.setOrientation(LinearLayout.VERTICAL);

layout.addView(adView, params);

popUp.setContentView(layout);

_activity.setContentView(mainLayout, params);

AdRequest adRequest = new AdRequest();

// Enable this if your are testing AdMob, otherwise you'll risk to be banned!

//adRequest.addTestDevice(AdRequest.TEST_EMULATOR);

_activity.adView.loadAd(adRequest);

// Show our popup window

popUp.showAtLocation(mainLayout, Gravity.BOTTOM, 0, 0);

popUp.update();

}});

}

}

// Do some cleanup

  @Override

  public void onDestroy() {

    if (adView != null) {

      adView.destroy();

    }

    super.onDestroy();

  }

}

Step 2

Modify your manifest to override your NativeActivity

Open your AndroidManifest.xml and edit it  (Again, change youractivityname with a name of your choice that match the one in the class we made above):

// Original native activity 

<activity android:name="android.app.NativeActivity"

// Your new activity

<activity android:name=".youractivityname"

This is to tell to the build system that we want to use a custom native activity class instead of the app.NativeActivity one.

Step 3

Calling setupAds from your C/C++ code

This is the clue. It is time to call our ads show. Before we proceed keep in mind this: Never call this function in an onCreate callback. You have to call this code 'after' your whole native window is created. Personally I call it when my game (native, C) window is ready to be shown:

#include <jni.h>

#include <android_native_app_glue.h>

// Somewhere you defined this...

extern struct android_app *__gandroidapp__;

#endif

void showAds()

{

   // Get the android application's activity.

    ANativeActivity* activity = __gandroidapp__->activity;

    JavaVM* jvm = __gandroidapp__->activity->vm;

    JNIEnv* env = NULL;

    (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6);

    jint res = (*jvm)->AttachCurrentThread(jvm, &env, NULL);

    if (res == JNI_ERR)

    {

      // Failed to retrieve JVM environment

        return; 

    }

    jclass clazz = (*env)->GetObjectClass(env, activity->clazz);

    jmethodID methodID = (*env)->GetMethodID(env, clazz, "showAdPopup", "()V");

    (*env)->CallVoidMethod(env, activity->clazz, methodID);

    (*jvm)->DetachCurrentThread(jvm);

}

That's all. Your AdView will show up, otherwise feel free to leave some comments below. I will be glad to help.

If you are curious to see how the ADS will perform in your app/game just download uFall, it is free, of course:

uFall for Android

Best Regards


Comments

NDK   2/15/2013

Great!
Is there way hide Admob_Id ("changethiswithyouradmobid") in NDK code?

East   2/18/2013

Nice example!
However, I downloaded uFall from google play but it doesn't show any ads, just a greenish box at the bottom of the screen. I wonder if it's because admob doesn't have any ads to show? My device is Galaxy Note with OS 2.3.5 btw..

Josh   2/26/2013

Excellent tutorial. Our company is having the same problem. We have a proprietary ad system that displays ads over games. Unfortunately, most of our third party developers use NativeActivity, so the ads don't show up.

When is the earliest that you can attach a popup window? In other words, how can you tell that the Native window is created? Is there a callback in the native code or in NativeActivity?

Thanks

Carlo Lanzotti   3/18/2013

Hi all,

Sorry for the late reply:

NDK:

Yes, there is, give me some times and I'll show how to do it.

East:

The greenish box can be due to no internet connection.

Josh:

You can check the APP_CMD_INIT_WINDOW call in your native code.

Thanks for your kind words.

David   10/25/2013

Thanks for the great tutorial. However, I am having troubles compiling my C++ code. I get:

In function 'void showAds()':
error: base operand of '->' has non-pointer type 'JavaVM {aka _JavaVM}'

error: base operand of '->' has non-pointer type 'JNIEnv {aka _JNIEnv}'

Any suggestions? Thanks again.

Carlo Lanzotti   11/20/2013

David,

can you post your 'struct android_app' contents?

chue   3/ 9/2014

Thank you so much for putting this together! I was very deep into development when I realized that there is no native support for ads. Luckily I found your post - it saved me from having to rewrite my app in Java!

AngryMailer   6/12/2014

Thank you very much !

Carlo Lanzotti   6/12/2014

Thank you all for the kind words!

AlexAUT   7/ 5/2014

Is there a way to hide the ads again?

Carlo Lanzotti   7/ 7/2014

Hi AlexAUT,

I haven't tried it, but maybe PopupWindow::dismiss() should work (?)

Grimmreefer   7/14/2014

Hey, my ad is are just a gray field, could it be that it is a problem that i use a custom rom?

Carlo Lanzotti   7/15/2014

Hi Grimmreefer,

generally a gray field indicates that there is no connection to the AdMob server. Be sure you internet connection is up.

Regards

Grimmreefer   7/15/2014

HEy, thank you for the quick answer, i already found the problem. If i want to show the ads on my phone(LG2) logcat says "Not enought space to show ad. Needs 320x50, but only has 315x381dp."

Any solution for this case?

Carlo Lanzotti   7/15/2014

Hi,

unfortunately there is no way as it seems to be the minimum size supported by AdMob. I will check it out.

Regards

Grimmreefer   7/15/2014

o damn

anyway, a great tutorial. Maybe we can fix this.

Rob Gold   7/17/2014

I'm running into the same issue. yay!
Does this method work with InterstitialAds?

I'm having issues with showing an InterstitialAd. I got it working in a non-NDK build, but as soon as I try on my NativeActivity extended activity, it doesn't work. Creating the AdRequest and then loading the ad just silently fail. No output in the logs.

Carlo Lanzotti   7/17/2014

Hi Rob,

it should work with some modifications. I'm going to setup something like it in the next week. I'll post here the result, but feel free to post your solution if you got it first ;-)

Regards

Rob Gold   7/18/2014

I solved this last night (as far as I know right now.) I initially tried starting a new activity via an Intent- then loading the InterstitialAd. It worked, but the new activity was pushing off my native activity before the ad was even ready, so it was an unacceptable solution.
I ended up finding I could extend a new class from a Service, start that, and then load the InterstitialAd there and it worked! I still need more time to clean up the implementation (It's spare time project at the moment), but it seems like I have my solution.

btw, I downloaded uFall on my HTC One X last night, and the game wasn't working- it just started up to a black screen :(

stephane kyles   7/20/2014

well done, very useful article !

Mike   9/ 9/2015

Is there any way to implement this in such a way that does not depend on the package name (as used in the directory hierarchy and Java file itself)? When the package name is forcibly changed in the APK after it has been built, this solution does not work of course. Java noob here.

Carlo Lanzotti   9/ 9/2015

Hi Mike,

I'm not an advanced Java/packaging expert but I think changing the APK name after the build will affect even others module you include in the app (E.g. if you have another custom activity that does something else).

Maybe there is a workaround, like moving the activity src in another directory and not in the 'yourapp' one.

Didn't tried it so I can't say it will work.

Regards

Camus   9/26/2015

To the guy asking about the error:

""Not enought space to show ad"

Change the pop up initialization to:

popUp = new PopupWindow(_activity);
popUp.setWidth(adsize.getWidth());
popUp.setHeight(adsize.getHeight());
popUp.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
popUp.setClippingEnabled(false);

Thanks Carlo for the tip, now I'm looking for ways to catch the events like orientation changes.

Timmy   1/22/2016

It won't compile.


Description Resource Path Location Type
'res' was not declared in this scope ads.cpp /SDLActivity/jni/src line 31 C/C++ Problem
expected ';' before 'res' ads.cpp /SDLActivity/jni/src line 29 C/C++ Problem
'jint' was not declared in this scope ads.cpp /SDLActivity/jni/src line 29 C/C++ Problem
'JNI_VERSION_1_6' was not declared in this scope ads.cpp /SDLActivity/jni/src line 27 C/C++ Problem
'NULL' was not declared in this scope ads.cpp /SDLActivity/jni/src line 25 C/C++ Problem
'env' was not declared in this scope ads.cpp /SDLActivity/jni/src line 25 C/C++ Problem
'JNIEnv' was not declared in this scope ads.cpp /SDLActivity/jni/src line 25 C/C++ Problem
'jvm' was not declared in this scope ads.cpp /SDLActivity/jni/src line 23 C/C++ Problem
'JavaVM' was not declared in this scope ads.cpp /SDLActivity/jni/src line 23 C/C++ Problem
'__gandroidapp__' was not declared in this scope ads.cpp /SDLActivity/jni/src line 21 C/C++ Problem
'activity' was not declared in this scope ads.cpp /SDLActivity/jni/src line 21 C/C++ Problem
'ANativeActivity' was not declared in this scope ads.cpp /SDLActivity/jni/src line 21 C/C++ Problem
'methodID' was not declared in this scope ads.cpp /SDLActivity/jni/src line 45 C/C++ Problem
expected ';' before 'methodID' ads.cpp /SDLActivity/jni/src line 43 C/C++ Problem
'jmethodID' was not declared in this scope ads.cpp /SDLActivity/jni/src line 43 C/C++ Problem
expected ';' before 'clazz' ads.cpp /SDLActivity/jni/src line 41 C/C++ Problem
make: *** [obj/local/armeabi/objs/main/ads.o] Error 1 SDLActivity C/C++ Problem
'jclass' was not declared in this scope ads.cpp /SDLActivity/jni/src line 41 C/C++ Problem
'JNI_ERR' was not declared in this scope ads.cpp /SDLActivity/jni/src line 31 C/C++ Problem

update galaxyS4 Android 4.4.4 download free   8/30/2016


Emmanuel   9/22/2016

Hi, Thank you, I didn´t know you were able to use the setContentView method with the nativeactivity, I thought this was alredy called from the NativeActivity::OnCreate, I was trying to get the current content to add a layout until I found your post.

Leave Comment



Site Map