Android Java memory usage with AdMob - java

I am experience som major memory issues with my projekt. If you could throw some advice for me that would be really helpfull.. I'll try to explain my whole usage.
In the main activity, the first thing I do is setting upp the AdMob banners. Which seems to use ALOT of memory. This is my whole onCreate method:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
mGameView = new GameView(this, getAssets());
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
mGameView.setMemoryLimit(am.getMemoryClass());
mLayout = new RelativeLayout(this);
mLayout.addView(mGameView);
if(mGameView.getMemoryLimit() > 40 && !mGameView.isFullVersion())
{
mAdView = new AdView(mThis, AdSize.BANNER, "xxxx");
mAdRequest = new AdRequest();
mAdView.loadAd(mAdRequest);
mAdRequest = null;
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
mAdView.setLayoutParams(lp);
mLayout.addView(mAdView);
}
setContentView(mLayout);
hideBannerAd();
}
As I have googled alot, the AdMob code seems to leak memory so I try to keep it alive the whole lifetime of the app and only showing it on loadingscreens.
At this point I am using maybe 10-15mb memory JUST FOR THE ADS.
I have grouped all bitmaps used by Menu and Gameplay. Which means I only load the the menu bitmaps when I show the menu and then recycle and delete the reference of the old bitmaps before loading the new ones.
During the loading process I check how much percent of memory I have left and only continue to load bitmaps if the percent is less than 90%.
I calculate the memory percent by:
Debug.getMemoryInfo(memoryInfo);
percent = (float) (memoryInfo.getTotalPss() / 1024.0) / (float) mGameView.getMemoryLimit() * 100;
tge memoryLimit comes from earlier mentioned activity:
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
mGameView.setMemoryLimit(am.getMemoryClass());
As you saw in the activity I am only showing ads for devices with more than 40mb memory. But I am using about 20mb for the project WITHOUT ADS.
Another problem I have encountered is with devices that share memory with other applications. Like if their memory limit is 64 but is still using like 40-50 from elsewhere than my app. Can I somehow request that memory to be cleared?
Am I doing someting wrong because this is really getting to my nerves. I mean I have seen apps using like 2-5 ads at the same time..
Can I somehow manage this better?

Related

Android O device blank screen after addView / setContentview

I recently decided to update my older apps and bring them a little more up to date. In doing so I noticed an odd problem when running them on my new handset (Android O / API Level 29).
My App is essentially a single Activity using OpenGL. This is basically what happens when the app is first loaded...
Create Layout, Create a Splashscreen View, create custom GLSurfaceView
Add GLSurfaceView to Layout
Add Splashscreen to Layout
Start AsyncTask to load resources and do setup ops
GLSurfaceView dismisses Splashscreen to reveal renderer...
So, it all works perfectly well on my older devices (3 x handsets, 1 x tablet all running various versions of Android no higher than Lollipop).
However, on my Android 10 handset, all I get is a blank screen while the app starts. Just to be clear, there are no errors, the app itself works perfectly fine. After the usual startup time, the blank screen is dismissed and the app continues, it's just that the 'splashscreen' has now become a "blankscreen".
But Android Studio Layout Inspector tells a different story
Indeed, if I open the Layout Inspector in Android Studio, oddly it shows exactly what I would expect... the splashscreen with its text/graphics, but not on the actual device...
Some test code
In the following test code I replaced the resource loading / setup with a simple Thread.sleep just to create a longer delay so I could more easily inspect the output in Android Studio. This does still exhibit the problem...
#Override
protected void onCreate(Bundle savedInstanceState) {
int[] deviceResolution = getDeviceResolution();
width = deviceResolution[0];
height = deviceResolution[1];
layout = new RelativeLayout(this);
splash = new Splash(getApplication(), width, height);
myGLView = new CustomGLSurfaceView(this.getApplication());
layout.addView(myGLView);
layout.addView(splash);
setContentView(layout);
loadResource = new LoadResources(newBundle);
loadResources.execute();
super.onCreate(savedInstanceState);
}
class LoadResources AsyncTask<Void, Void, Void> {
Bundle savedBundle;
LoadResources(Bundle savedBundle){
this.savedBundle=savedBundle;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
// Simulate some long-running task here. While this is happening, splashscreen should be visible. And on older devices, it is. Not on Android O handset (but OK in Android Studio Layout Inspector).
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Would typically make my calls here to load resources, and complete other setup tasks here
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
** Other observations **
I know that Async is now deprecated and I'm also aware of other Splashscreen tecnhiques such as using a dedicated Activity. However due to the setup/complexity of the apps and given that it doesn't 'break' them per-se, I don't want to have to refactor the entire codebase (just yet anyway) any new apps going forward will use a different system for the splashscreen. What I'm really trying to understand here is why this now doesn't work as it used to. Is this a bug? Or a behavioural change that happened in an Android version somewhere down the line? Or maybe it's just me doing something wrong. If anyone has come across it, I'd be really interested to know of a fix/workaround.
** Other things I've tried **
In the above example, if I simply don't add 'myGLView' then the splashscreen shows, but of course the app then won't work. So I though about adding it in onPostExecute and then bringing the splashscreen view back to the front with splash.bringToFront(). This kind of works, but is messy. The splashscreen only shows for a brief second, and on devices that show it correctly there is a 'glitch' as the correctly displayed splashscreen is overlayed with the GL Renderer and then bought back to the front.
You are adding a view to an object which is not currently set in an Activity.
The better approach would be calling setContentView() first as well as the super method, then adding the multiple views in it.
So in your case, it'll be like:
super.onCreate(savedInstanceState);
setContentView(layout);
layout = new RelativeLayout(this);
int[] deviceResolution = getDeviceResolution();
width = deviceResolution[0];
height = deviceResolution[1];
layout = new RelativeLayout(this);
splash = new Splash(getApplication(), width, height);
myGLView = new CustomGLSurfaceView(this.getApplication());
layout.addView(myGLView);
layout.addView(splash);
loadResource = new LoadResources(newBundle);
loadResources.execute();

Create an overlay window in android 19

I'm trying to create an overlay window in android (a which will float over any other app in the screen, even when my app is on background)
I followed several guides (some from SO) and here is the important code
this.sp = PreferenceManager.getDefaultSharedPreferences(context);
this.wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
this.main = (FrameLayout) LayoutInflater.from(c).inflate(R.layout.ui_floating_window, null);
int type = WindowManager.LayoutParams.TYPE_TOAST;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
Point p = new Point();
wm.getDefaultDisplay().getSize(p);
this.displayHeight = p.y;
this.displayWidth = p.x;
this.rationWH = this.displayWidth / (float) this.displayHeight;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_WIDTH, this.displayWidth / 2),
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_HEIGHT, this.displayHeight / 2),
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_X, this.displayWidth / 2),
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_Y, this.displayHeight / 2),
type,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.TOP;
params.horizontalMargin = 0;
params.verticalMargin = 0;
this.wm.addView(main, params);
i've tested on android 29 and works really fine
but on android 19 the window opens but as soon as the current app goes background the window goes either. i would like the window stayed on even after user switches the app.
This is how i get 19
this is how it works in android 29 (correct way)
https://i.imgur.com/JjMugfG.mp4
am i doing anything wrong
The problem is that the WindowManager instance from Activity is different than the instance retrieved from the application-level Context.
Let's try a test:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this; // context of the activity
WindowManager wm1 = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager wm2 = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
System.out.println("Are equal? " + wm1.equals(wm2));
}
The result is FALSE.
Actually, a WindowManager is responsible for managing a list of windows on the screen. In the case of Activities, when the Activity gets disappeared, all of the related windows such as your added floating view would be gone. While your app lives in the background after pressing the home button, for instance, its window manager (Application's window manager) keeps all of the managed windows. It means that your floating view has the chance of being shown until the app would be killed by the system.
So, by retrieving WindowManager instance from application-level Context and adding the view to it, the problem will be solved.
For 7 years, I work on Floating Apps (https://floatingapps.net) which is basically a set of floating mini-apps, so I know something about it :-).
I first get it working on Android 2.3.5 and progress until today.
It's a complex problem if you want to do it right across all possible devices and Android versions.
I recently published a series of articles on this topic. Check it out: https://localazy.com/blog/floating-windows-on-android-1-jetpack-compose-and-room
It goes from the main app, background service, permissions, floating windows, moving them, keyboard input to the list of tips, trick and shortcomings I learned along the way.
It should learn you how to do it right.
The window manager you are getting from an activity points out to Activity.getWindow() when adding a view, which naturally disappears if you click the back button or home button. That means you need another decor view to resolve your problem, which is calling the application context in your case, where it will let you manage the phone's window, as you want.
Check application context, and if it's not null, get the WindowManager instance, then try adding your view.
Also I'm not sure but using WindowManager.TYPE_SYSTEM_ALERT also might help.

Disabling AdMob Ads in certain parts of app

My App, (OpenGL ES 2.0) currently has 55 classes, and one activity single.
I have my own custom Scene Manager which I use to change between different Scenes (ie, different scenes would be: Main Menu, Level Select, Level 1, Level 2....Level 20, Game Over etc...)
So I set all of my AdMob stuff up in my Activity's onCreate(); method. So at the moment, the same ad runs throughout the whole app.
How it is possible to 'switch off' the ads on different Scenes?
Bear in mind that none of my classes have direct access to the Activity class itself.
Any suggestions would be appreciated as I'm finding using AdMob extremely challenging, it doesn't seem very intuitive so any AdMob experts out there, your opinions would be appreciated!
I should point out that I don't use XML, everything is done programmatically.
Thanks
Code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create an ad.
adView = new AdView(this);
adView.setAdSize(AdSize.BANNER);
adView.setAdUnitId(AD_UNIT_ID);
// Add the AdView to the view hierarchy. The view will have no size
// until the ad is loaded.
RelativeLayout layout = new RelativeLayout(this);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// Create an ad request. Check logcat output for the hashed device ID to
// get test ads on a physical device.
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(TestDeviceID)
.build();
// Start loading the ad in the background.
adView.loadAd(adRequest);
//Request full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//Create a displayMetrics object to get pixel width and height
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
width = metrics.widthPixels;
height = metrics.heightPixels;
//Work out values for resizing screen while keeping aspect ratio
width = (int) Math.min(width, height * 1.702127659574468);
height = (int) Math.min(height, width / 1.702127659574468);
//Create and set GL view (OpenGL View)
myView = new MyGLSurfaceView(MainActivity.this);
RelativeLayout.LayoutParams adParams =
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
adParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
adParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
//Set the colour if we don't, the ad won't show (bug?)
adView.setBackgroundColor(Color.BLACK);
layout.addView(myView);
layout.addView(adView, adParams);
//Create a copy of the Bundle
if (savedInstanceState != null){
newBundle = new Bundle(savedInstanceState);
}
setContentView(layout);
}
First of all you're causing yourself a lot of pain by doing your layout programmatically (I gave a talk on this just last night). You should really consider understanding and using the XML layout.
But to answer your question, you will need to find a way for the classes that want to control whether an ad is shown or not to have some method of sending a message to the Activity. I would suggest injecting them with an interface that is implemented by an inner classes in your Activity.
The interface is going to need to look something like:
- startShowingAds - this should start requesting ads and make the AdView visible.
- stopShowingAds - this should pause ad showing and hide the AdView.
To temporarily hide the ads you just need to hide the AdView, eventually calling pause() even though I think that hidden ad views are not refreshed (you can check in the logs).
About how to reach the AdView object from your code, it depends on what you have accessible from there. If you just have the View where you are drawing a solution could be to store the AdView object in the View's tag (see setTag() and getTag() here: https://developer.android.com/reference/android/view/View.html#getTag%28int%29).

outofmemory error using bitmaps Android

I've created an android app, which chooses a picture from Gallery and displays a preview.
#Override
public void onClick(View v) {
if (v.getId()== R.id.button){
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_PICK);
startActivityForResult(Intent.createChooser(intent,
"Select Picture"), SELECT_PICTURE);
}
After the image is selected, the preview should be shown.
Yet, it works only for the first time. And later when I click back, it shows outOfMemoryException
working with bitmaps in android costs you a lot of memory, which needs a hude attention because of memory leaks.
you can always use
System.gc()
to garbage collect and free up some memory.
or
bitmap.recycle();
cheack out these blog post that I used when I developed my image editing app.
Curious-create.org
Evendanan.net
Working with bitmaps in android often throws the OutOfMemory error. Bitmaps need to be handled properly. you might want to look at the following libraries used specifically for image loading and working with bitmaps in android:
https://github.com/nostra13/Android-Universal-Image-Loader
https://github.com/novoda/ImageLoader
You can also implement your own imageloader. You can find the code for that easily.
you are probably caching a lot of bitmaps, so you could use ImageLoader and do something like this:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
this).memoryCache(new WeakMemoryCache())
.discCache(new UnlimitedDiscCache(new File("/cache"))).build();
also try somehow to release bitmaps after you no longer need them

Android Admob showing blank RocketFuel ads often

I have a working AdMob AdView on top of my opengl game. Its working great but I notice that it keeps showing a particular blank ad. I thought it was a bug on my side but then I noticed that the ad is functioning partially. The ad appears to be a RocketFuel ad according to the provided info button in the top right of the AdView. Clicking within the blank AdView area does nothing unless the info button it hit. The images and code below help explain.
I thought RocketFuel was a separate ad service, is it possible to limit its appearance in Google AdMob using the Filter Settings > Category / Type Settings > Affiliate offers? Or perhaps one of the other categories? I am going to try adding a URL Block for rocketfuel.com under the Ad Filters. However I am unsure if they are serving ads directly from their domain.
Otherwise, is there any way to block, report, or limit these types of ads from being shown through my app? Has anyone had similar problems with such ads?
Here is how I slap the adview on top of my java based OpenGL ES 2 renderer:
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mView = new nopgRenderer(this);
setContentView(mView);
adView = new AdView(this, AdSize.IAB_LEADERBOARD, "<AdMobAdAppID>");
RelativeLayout layout = new RelativeLayout(this);
layout.addView(adView);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
params.addRule(RelativeLayout.CENTER_HORIZONTAL);
adView.setLayoutParams(params);
this.addContentView(layout, params);
adView.loadAd(new AdRequest());
}
public static void hideAd(){
adView.setVisibility(AdView.GONE);
}
public static void showAd(){
adView.setVisibility(AdView.VISIBLE);
}
The app on Google Play:
https://play.google.com/store/apps/details?id=com.dmtsource.nopg
Example of a normal ad:
Example of the blank ad:
Clicking on the info item in the blank ad:

Categories

Resources