Amazon Ads

Fill Rate: 80-90% (not much request yet though, about 2000 per day)
1.00 RPM
I’m not sure about the banner size. In my test app there was definitely some large banners.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:Amazon="http://schemas.android.com/apk/lib/com.amazon.device.ads"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:maxHeight="50dp"
    android:orientation="vertical" >
    <com.amazon.device.ads.AdLayout
        android:id="@+id/adview"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        Amazon:adSize="320x50" />
</LinearLayout>

Thanks. I define the ad in code but I might force the size (I know how) because it seems the eCPM is roughly the same and my players will like the game better with smaller ads.

Yeah, auto-size on a 7" tablet in landscape takes almost 1/4 the height of the display. wtf Amazon ?

Do NOT go live with the Amazon SDK … I did on WordHero and have over 1000 crash reports in 4 hours coming back to me via ACRA.

Here is just ONE of the traceback’s (they have multiple threading bugs like this!):

0android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
1at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4125)
2at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:716)
3at android.view.View.requestLayout(View.java:12957)
4at android.view.View.requestLayout(View.java:12957)
5at android.view.View.requestLayout(View.java:12957)
6at android.view.View.requestLayout(View.java:12957)
7at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:268)
8at android.view.View.requestLayout(View.java:12957)
9at android.view.View.requestLayout(View.java:12957)
10at android.view.View.requestLayout(View.java:12957)
11at android.view.ViewGroup.removeAllViews(ViewGroup.java:3681)
12at com.amazon.device.ads.MraidRenderer.boolean render()(SourceFile:86)
13at com.amazon.device.ads.AdController.void handleResponse()(SourceFile:439)
14at com.amazon.device.ads.AdLoader.void finalizeFetchAd()(SourceFile:273)
15at com.amazon.device.ads.AdLoader$AdFetcherTask.void onPostExecute(com.amazon.device.ads.AdLoader)(SourceFile:397)
16at com.amazon.device.ads.AdLoader$AdFetcherTask.void onPostExecute(java.lang.Object)(SourceFile:383)
17at android.os.AsyncTask.finish(AsyncTask.java:602)

For those of you interested… on most devices, you can happily run an AsyncTask and get the return from it to update a ListView or some other GUI widget (in this case, they are updating the AdView). However, on some other devices, this causes an error. It seems some ROM’s (including Samsung, LG, Droid) enforce a kill for cross-threading display updates. ugh.

I saw your bug report… maybe this bug is only on the latest version of the SDK? Or it depends on the implementation? I didnt get a single report…

Btw, whats this ACRA? Is it some kind of automated stacktrace report? I was thinking about implementing something like this myself, didnt know it exist.

Yeah, I did not have any problems either on my Nexus devices… but the reports are from other devices… LG, Samsung, Droid, etc.

ACRA - Know your BUGS.

I have it connected to Bugsense.com, which will auto magically retrace using the proguard files (if you upload them). It is pretty awesome to see exactly which devices are dying on which line numbers :slight_smile:

Cool, I will take a look, thanks.

Regarding the bug, from the stacktrace it seems that it’s a bug of the devices, right? Because it’s on “onPostExecute”, so it is (or should be) being executed on the UI thread. Or maybe somehow you or the SDK created the AdView on another thread? Then when it tries to touch the view it crashes due to being on another thread?

onPostExecute is a callback that is called after the ‘do all the work’ part of an AsyncTask is finished. See: AsyncTask | Android Developers

I believe what happens is this:

  1. GUI thread -> spawns AsyncTask (new thread)
  2. AsyncTask completes and calls onPostExecute … which is no longer running on the GUI thread, but the new AsyncTask thread and something in the ROM config then kills the app.

Code to fix this sort of thing if you are running in a view:


Activity a = (Activity) mContext;
if (a != null) {
    a.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (!((Activity) mContext).isFinishing()) {
                // do work here
            }
        }
    });
}

Yes, this should be explicitly stated in most Android tutorials - but is often glossed over. But is something most developers need to do eventually in “real world progrramming” - i.e. call a UI element from a separately running thread. And so this should be included in any intro explanation of Android threads or activity lifecycle (but is often not mentioned).

A nuance to add - Eclipse will usually also point out that the mContext should be declared “final” (i.e. a local copy is made so that it can be used in the later-running Runnable):


private void someMethod(final Context mContext) {

    Activity a = (Activity) mContext;
    if (a != null) {
        a.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (!((Activity) mContext).isFinishing()) {
                    // do work here
                }
            }
        });
    }

}

A note about casting Context to Activity

I cast from Activity to Context without much issue - but I suspect the cast from Context to Activity maybe problematic - if nothing else for harder-to-read code. So I would suggest that maybe use an explicit Activity reference/variable might be better.

Also it seems in some situations, a cast from Context to Activity can fail - since a context can be either an activity context, or an application context (on which the cast will fail).

android - Is it always safe to cast Context to Activity within View - Stack Overflow
QUOTE:

As I know, It is not always safe because, the context also can be passed from os to a Service, BroadcastReceiver, etc. But, almost of case, that is not a problem. just check with this code
if(context instanceof Activity)
and feel free to use.

Technically, Views can be created with any Context (via the LayoutInflater)
So unless you are super sure that your Views are only instantiated by Activities, I wouldn’t suggest this. Doing this is not a clean idea.

Here is another explanation - suggesting the cast will fail if the context is supplied from:
this.getApplicationContext()

layout - Getting activity from context in android - Stack Overflow

QUOTE:

There are two different contexts in Android. One for your application (Let’s call it the BIG one) and one for each view (let’s call it the activity context).

A linearLayout is a view, so you have to call the activity context. To call it from an activity, simply call “this”. So easy isn’t it?

When you use

this.getApplicationContext();

You call the BIG context, the one that describes your application and cannot manage your view.

A big problem with Android is that a context cannot call your activity. That’s a big deal to avoid this when someone begins with the Android development. You have to find a better way to code your class (or replace “Context context” by “Activity activity” and cast it to “Context” when needed).

Both onPreExecute and onPostExecute are by design called on the UI Thread, while the doInBackground runs on the separate (newly spawned) thread. That bug doesnt make sense. Well, some new info to me: by doing a bit of research it seems that on pre-Jelly Bean, both onPreExec and onPostExec will be called on the same thread that the AsyncTask was spawned, so if it’s not on the UI Thread that may be the problem. If that is the problem, then definitely it’s a bug on the SDK, as they’re prolly not checking if they’re on the correct thread before starting the AsyncTask.

Do you happen to be creating the AdRequest on a non-UI thread? On a callback from a call to your server or something? Anyway, Amazon should fix it, but you might be able to work around it by forcing that the AsyncTasks are always called through the UI thread.

I’ve found that to be safe it is best to wrap all such UI references, and Toast messages etc. (for example Toast messages you want to issue to debug) within a runOnUiThread (and Runnable).

For example in callback code (that will be run in a separate thread) if you want to issue a Toast message you can use something like this:


//----------
// utility methods
//----------
private static void announceFromThread(final Activity activity, final String str) {

	// http://stackoverflow.com/questions/901239/android-using-runonuithread-to-do-ui-changes-from-a-thread
	// Android - using runOnUiThread to do UI changes from a
	// thread
	//
	// this is another thread so make sure this runs on UI
	// thread
	// (the thread which created those views)
	activity.runOnUiThread(new Runnable() {

		@Override
		public void run() {
			// ----------
			Toast.makeText(activity, str, Toast.LENGTH_SHORT).show();
			// ----------
		}
	});
}
//----------

Well, I have a custom view for adverts. The custom view is created as part of a larger XML sheet. The view looks like this:


public class Banner extends Advert_BaseClass implements AdListener {
  private AdLayout amazonAd;

    public Banner(Context context, AttributeSet attrs) {
        super(context, attrs);
        ... determine 'network' using sharedprefs
        LayoutInflater layoutInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (network.contentEquals("mopub") || network.length() == 0) {

        } else if (network.contentEquals("amazon")) {
            AdRegistration.setAppKey("asdf");
				
            AdTargetingOptions options = new AdTargetingOptions();
            options.enableGeoLocation(true);

            LinearLayout layout = (LinearLayout) layoutInflater.inflate(R.layout.ad_banner_amazon, null);
            amazonAd = (AdLayout) layout.findViewById(R.id.adview);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            addView(layout, params);
            amazonAd.loadAd(options);
        }

And you probably want this:

public abstract class Advert_BaseClass extends LinearLayout {

And the layout/ad_banner_amazon.xml layout is just an Amazon XML snippet with a LinearLayout around it.

Question… I always assumed that custom views like this would be created in the GUI thread. AmIwrong?

I am pretty sure you are right, unless there’s some huge weird bug.

But how are you refreshing the ads? I believe this bug in the Amazon SDK is being caused by the AdRequests, and not the AdLayout creation: I am assuming the AdRequest starts an AsyncTask (inside Amazon SDK), which runs the “onPreExecute” / “onPostExecute” methods. On post-Jelly Bean, they will always run on the UI thread, but on pre-Jelly Bean they will run on whichever thread the AsyncTask was started. So if you start de AdRequest on a non-UI thread (such as on the return of a call to your server), that may be what’s causing the bug (on pre-JB devices).

But again, if my hypothesis is correct, this is 100% fault of Amazon. They should check which thread they’re at before starting the AsyncTask, and post the message to the UI thread if needed.

I am not doing refreshes. Each game is only 2.5 minutes anyway…

The ad request is done in the layout. See last line: amazonAd.loadAd(options);

At this point, I am almost 100% sure the problem is Amazon. Unfortunately, their idiot support people just stick to the script and tell me to load the advert in the GUI thread.

You can see me bitching them out here: https://forums.developer.amazon.com/forums/thread.jspa?threadID=1378&tstart=0

Isn’t the AdLayout.loadAd() call already asynchronous? Hence the use of the listener pattern. Maybe it assumes that it’s being called from the UI thread.

I’ll look at my MoPub adapter later and see what I did.

I found this question on SO that has a lot of useful info on this topic:

Android: got CalledFromWrongThreadException in onPostExecute() - How could it be? - Stack Overflow

Basically it seems that on the first time you run an AsyncTask on your app it will initialize and save the thread id of which thread is going to run the onPostExecute / onPreExecute for every time you use an AsyncTask on your code. And if for some reason you start that first AsyncTask on another thread other than the main UI thread (on pre-JB devices), then every time you run an AsyncTask the onPre/onPost will run on this other thread.

So either:

  1. make sure you always start your AsyncTask from the UI Thread, and if for some reason you can’t guarantee that (due to the AsyncTask being called inside a library or something), just start a dummy AsyncTask right on the beginning of your app, before any other AsyncTask, so that it initializes correctly for the subsequent calls.
  2. Or never run code that depend on the UI Thread on onPre/onPost.

That is definitely new to me, I have always trusted that onPre/onPost would be on UI Thread. I have now to pay a bit more attention on this.

Anyway, going back to your problem, either Amazon should fix it using “2)” above (that is, not trusting it is on UI thread, and using runOnUiThread), and meanwhile you could fix using “1)” above: find where is the first point in your code that is calling an AsyncTask and check if it’s not being started on the UI thread (or starting the dummy AsyncTask on UI thread).

EDIT: Also they mention a problem with Flurry v3.2.1 on the thread. Do you use it? It seems that Flurry v3.2.1 does that (start an AsyncTask on a non-UI thread), so it would be the cause of the problem.

@Skurry: yes, it is… and it is running the onPostExecute() return on a non-GUI thread. Hence the problem.
@rottz: The first time I call a async task, I call it from a runnable. I assume this is what is causing the problem ? I do this because FSCKING Android will often launch an app twice, then kill the first copy… BUT if each copy launched an async task, which after finishing launches Activity2… well, you now have 2 copies of Activity2 running.

Yeah, that seems to be what triggers the problem… but I dont understand very well why you did it :slight_smile:

I am assuming that the 1st activity is the Splash and the 2nd is the main menu… if so, cant you just check on the end of Splash if the Main Menu activity is running already? Using a static flag on the Main Menu class or something.

Obs: I never use more than 1 activity on my apps/games, I just hate all the unnecessary complexity that comes with it for almost no practical gain :slight_smile:

Tried that… timing issues meant it was not reliable. Now I just use a fat delay at startup. A whole 1.5secs. Works ok.

When you have as much code as I do… Activities = win. Activity Fragments = hell.

Also, I was using Flurry 3.2.1 in the latest build. That was also mentioned as a source of problems in the OS thread… THANK YOU VERY, VERY MUCH for the link to that thread btw! I was going crazy trying to figure out the problem.