CoronaEnvironment.getCoronaActivity() returns null on one of the testing devices

Hello,

We have a few testing devices, but only on this one we have a problem:

Sony Xperia Z1

Android 4.4.4

When the app is started and then gracefully closed [native.requestExit()] and then started again, function:

CoronaEnvironment.getCoronaActivity()

returns null.

I’m not sure why this happens, this happens for us only on this device.

This causes a lot of headache, because none of the advertisements plugins can load when app is restarted.

Do you have any advice how to proceed with this?

Corona Enterprise version:

2015.2647

2015.2692

The plugin which throws this error is a first plugin loaded during application launch.

This is happening after 2-3 seconds from starting the app (we first load some animations which take some time and then load plugins).

The function is called inside constructor of the LuaLoader class of my plugin.

To summarize, here’s how we test it:

start the app

confirm plugins are loaded

exit the app ( using native.requestExit() )

start the app again

plugin gets null instead of activity

We’re not having this problem in any of the other plugins we offer.  So, sorry to say this, but this is likely a bug in your Java code.

Note that an Android plugin LuaLoader instance is only created once for the lifetime of the Android application.  Meaning that your LuaLoader instance will be reused by future CoronaActivity instances.  Because of this, your LuaLoader constructor will only be called once for the lifetime of the application.  But that one LuaLoader instance’s invoke() method will be called for every CoronaActivity the first time it gets required.  I suspect your code is not handling this situation.

So, to sum it up:

  • Only 1 instance of your LuaLoader will ever be created

  • This means your LuaLoader constructor will only be called once.

  • Your 1 LuaLoader instance will be used by subsequent Corona activities in the future.

  • Your 1 LuaLoader instance will have its invoke() method called for each new CoronaActivity.

And CoronaEnvironment.getActivity() will return null if a CoronaActivity is no longer available, such as when the user backs out of the app.  But it will return a new CoronaActivity instance when the user navigates back to your app.  This definitely works and as can be seen by our other plugins.  Also, your code needs to handle the case where it can return null from a Runnable object posted on the UI thread because there is a race condition where the user can back out of the activity just before the Runnable gets invoked.  On Android, your application’s UI thread is always running, even after all activities have been destroyed, which is a very real case you have to handle.

Anyways, I hope this helps.

Hi Joshua,

Thanks for taking the interest.

The plugins are in operation for over a year and they work fine on most of the devices, this is the first we came across which has this.

It’s difficult to suspect that there’s something with a one line of code (I’ve created an example plugin, with invoke method calling only getCoronaActivity), and it works everywhere accept for that device. It’s running stock software etc etc, no modifications made to the OS.

I will take a closer look on Monday and will have to handle this somehow, it looks very strange though.

In your application, are you deriving from the CoronaActivity class?

If so, then you need to make sure that your derived activity’s onCreate() method calls super.onCreate() first, because that’s where our CoronaActivity registers itself into the CoronaEnvironment.

Here’s the code of my failing plugin:

// // LuaLoader.java // TemplateApp // // Copyright (c) 2012 \_\_MyCompanyName\_\_. All rights reserved. // // This corresponds to the name of the Lua library, // e.g. [Lua] require "plugin.library" package plugin.library; import com.ansca.corona.\*; import com.naef.jnlua.JavaFunction; import com.naef.jnlua.LuaState; /\*\* \* Implements the Lua interface for a Corona plugin. \* \<p/\> \* Only one instance of this class will be created by Corona for the lifetime of the application. \* This instance will be re-used for every new Corona activity that gets created. \*/ public class LuaLoader implements JavaFunction, CoronaRuntimeListener { protected void initialize() { CoronaActivity activity = CoronaEnvironment.getCoronaActivity(); if (activity == null) { throw new IllegalArgumentException("Activity cannot be null."); } } /\*\* \* Warning! This method is not called on the main UI thread. \*/ @Override public int invoke(LuaState L) { initialize(); return 1; } @Override public void onLoaded(CoronaRuntime coronaRuntime) { } @Override public void onStarted(CoronaRuntime coronaRuntime) { } @Override public void onSuspended(CoronaRuntime coronaRuntime) { } @Override public void onResumed(CoronaRuntime coronaRuntime) { } @Override public void onExiting(CoronaRuntime coronaRuntime) { } }

It works on all of our test devices [6] accept for the single one I mentioned in the original post.

I don’t see anything wrong in the code on our side.  It’s pretty simple on our end.  As long as the CoronaActivity’s onCreate() gets called, the CoronaEnvironment class will provide an instance to it.

Perhaps you should have a look at the Android log via “adb logcat” for any clues to what’s going wrong on that device.

Yeah, tried that. It’s just weird.

I’ve changed that code so I don’t need the activity right away, but it’s very strange.

Anyway, thanks a lot for your support.

We’re not having this problem in any of the other plugins we offer.  So, sorry to say this, but this is likely a bug in your Java code.

Note that an Android plugin LuaLoader instance is only created once for the lifetime of the Android application.  Meaning that your LuaLoader instance will be reused by future CoronaActivity instances.  Because of this, your LuaLoader constructor will only be called once for the lifetime of the application.  But that one LuaLoader instance’s invoke() method will be called for every CoronaActivity the first time it gets required.  I suspect your code is not handling this situation.

So, to sum it up:

  • Only 1 instance of your LuaLoader will ever be created

  • This means your LuaLoader constructor will only be called once.

  • Your 1 LuaLoader instance will be used by subsequent Corona activities in the future.

  • Your 1 LuaLoader instance will have its invoke() method called for each new CoronaActivity.

And CoronaEnvironment.getActivity() will return null if a CoronaActivity is no longer available, such as when the user backs out of the app.  But it will return a new CoronaActivity instance when the user navigates back to your app.  This definitely works and as can be seen by our other plugins.  Also, your code needs to handle the case where it can return null from a Runnable object posted on the UI thread because there is a race condition where the user can back out of the activity just before the Runnable gets invoked.  On Android, your application’s UI thread is always running, even after all activities have been destroyed, which is a very real case you have to handle.

Anyways, I hope this helps.

Hi Joshua,

Thanks for taking the interest.

The plugins are in operation for over a year and they work fine on most of the devices, this is the first we came across which has this.

It’s difficult to suspect that there’s something with a one line of code (I’ve created an example plugin, with invoke method calling only getCoronaActivity), and it works everywhere accept for that device. It’s running stock software etc etc, no modifications made to the OS.

I will take a closer look on Monday and will have to handle this somehow, it looks very strange though.

In your application, are you deriving from the CoronaActivity class?

If so, then you need to make sure that your derived activity’s onCreate() method calls super.onCreate() first, because that’s where our CoronaActivity registers itself into the CoronaEnvironment.

Here’s the code of my failing plugin:

// // LuaLoader.java // TemplateApp // // Copyright (c) 2012 \_\_MyCompanyName\_\_. All rights reserved. // // This corresponds to the name of the Lua library, // e.g. [Lua] require "plugin.library" package plugin.library; import com.ansca.corona.\*; import com.naef.jnlua.JavaFunction; import com.naef.jnlua.LuaState; /\*\* \* Implements the Lua interface for a Corona plugin. \* \<p/\> \* Only one instance of this class will be created by Corona for the lifetime of the application. \* This instance will be re-used for every new Corona activity that gets created. \*/ public class LuaLoader implements JavaFunction, CoronaRuntimeListener { protected void initialize() { CoronaActivity activity = CoronaEnvironment.getCoronaActivity(); if (activity == null) { throw new IllegalArgumentException("Activity cannot be null."); } } /\*\* \* Warning! This method is not called on the main UI thread. \*/ @Override public int invoke(LuaState L) { initialize(); return 1; } @Override public void onLoaded(CoronaRuntime coronaRuntime) { } @Override public void onStarted(CoronaRuntime coronaRuntime) { } @Override public void onSuspended(CoronaRuntime coronaRuntime) { } @Override public void onResumed(CoronaRuntime coronaRuntime) { } @Override public void onExiting(CoronaRuntime coronaRuntime) { } }

It works on all of our test devices [6] accept for the single one I mentioned in the original post.

I don’t see anything wrong in the code on our side.  It’s pretty simple on our end.  As long as the CoronaActivity’s onCreate() gets called, the CoronaEnvironment class will provide an instance to it.

Perhaps you should have a look at the Android log via “adb logcat” for any clues to what’s going wrong on that device.

Yeah, tried that. It’s just weird.

I’ve changed that code so I don’t need the activity right away, but it’s very strange.

Anyway, thanks a lot for your support.