- Home /
Android plugin & Activity help needed - "GetMethodID method not found" problems
Hi there,
I've been building an android plugin for Parse SDK and I'm running into some issues with stability when trying to access the activity from the Application/Game while using C#.
Overview: The plugin's functionality is working fine on the first startup, the problem occurs after an Application.Quit() and on concurrent startup attempts.
Problem: Java Plugin methods becomes inaccessible via it's C# interface after the game shuts down and tries to start back up. First run through, everything works fine.
Error Signature:
D/dalvikvm( 9747): GetMethodID: method not found: Ljava/lang/RuntimeException;.GetUserObjectId:()Ljava/lang/String;
E/dalvikvm( 9747): Class lookup Ljava/lang/NoSuchMethodError; attempted while exception Ljava/lang/RuntimeException; pending
The simplest test case, begins by building my plugin and application to my device, letting things run, then quitting the application via pressing the home button on main menu and issuing an Application.Quit() event. When the user tries to start the game back up, he starts to suffer concurrent crashes.
There are 7 references to take into consideration to give full length of the code structure and crash:
1 - AndroidManifest.xml
2 - Java Plugin Startup Output
3 - Java Plugin Code Overview
4 - Unity Script Startup Output
5 - Unity Script Code Overview
6 - Shutting Down Application - Application.Quit()
7 - Startup Crash
Ref #1 - AndroidManifest.xml:
Before my application starts executing, the Java plugin starts ahead because of AndroidManifest.xml which defines the activity in there.
<activity android:name="com.ceGames.parseplugin.ParsePluginActivity"
android:noHistory="true"
android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.ParseBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<receiver android:name="com.ceGames.parseplugin.ParseCustomReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.ceGames.parseplugin.PUSH_NOTIFICATION" />
</intent-filter>
</receiver>
Ref #2 - Parse Java Startup Output:
The following output tells us what's happening in the Parse Java plugin.
D/Parse Push Notifications( 9042): >>PARSE PLUGIN ACTIVITY<< ON CREATE
D/Parse Push Notifications( 9042): >>PARSE PLUGIN ACTIVITY<< Set instance to THIS
D/Parse Push Notifications( 9042): >>PARSE PLUGIN ACTIVITY<< Initialize instance
D/Parse Push Notifications( 9042): >>PARSE PLUGIN ACTIVITY<< BEGIN Initializing Parse Push Notifications
D/Parse Push Notifications( 9042): >>PARSE PLUGIN ACTIVITY<< INITIALIZE PARSE PLUGIN w/ TOKEN!!!!!!!
D/Parse Push Notifications( 9042): >>PARSE PLUGIN ACTIVITY<< COMPLETED Initializing Parse Push Notifications
Ref #3 - Parse Java Plugin:
The Parse plugin initializes itself and maintains a singleton available so that my Application/Game can access it.Please note all of the stubs with debug logs so I could track what's firing internally when Unity shuts down/starts up.
public class ParsePluginActivity extends UnityPlayerActivity {
public static boolean plugin_initialized = false;
public static String installation_object_id = "";
public static String user_object_id = "";
public static boolean enable_push_notification = true;
public static boolean application_running = true;
private static ParsePluginActivity m_instance;
public static ParsePluginActivity instance() {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< begin return instance");
if (m_instance == null) {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< Create new ParsePluginActivity");
m_instance = new ParsePluginActivity();
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< Initialize instance");
m_instance.Initialize();
}
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< end return instance");
return m_instance;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< ON CREATE");
if( m_instance == null ) {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< Set instance to THIS");
m_instance = this;
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< Initialize instance");
m_instance.Initialize();
}
super.onCreate(savedInstanceState);
}
private void Initialize() {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< BEGIN Initializing Parse Push Notifications");
if( plugin_initialized == false ) {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< INITIALIZE PARSE PLUGIN w/ SECRET KEYS!!!!!!!");
Parse.initialize(this, "mySecretKey", "myOtherSecretKey");
plugin_initialized = true;
user_object_id = "UserObjectId_12345";
}
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< COMPLETED Initializing Parse Push Notifications");
}
public String GetUserObjectId()
{
return ParsePluginActivity.user_object_id;
}
public static void Shutdown()
{
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< SHUTTING DOWN");
if( m_instance != null ) {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< Call Activity.Finish()");
m_instance.finish();
m_instance = null;
}
}
@Override
protected void onPause()
{
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< ON PAUSE");
super.onPause();
}
@Override
public void onDetachedFromWindow() {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< ON DETACHED FROM WINDOW!");
// TODO Auto-generated method stub
super.onDetachedFromWindow();
}
@Override
protected void onResume() {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< ON RESUME");
super.onResume();
}
@Override
protected void onDestroy()
{
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< ON DESTROY");
super.onDestroy();
}
@Override
protected void onStop() {
Log.d("Parse Push Notifications", ">>PARSE PLUGIN ACTIVITY<< ON STOP");
super.onStop();
}
}
Ref #4 - Unity Script Startup Output
We can see that the game starts up properly and the Parse plugin is initialized properly. We're able to call the functionality from Unity Script and we get our results AS EXPECTED.
I/Unity ( 9707): >>PUSH NOTIFICATIONS<< Initializing Parse Plugin
D/Parse Push Notifications( 9707): >>PARSE PLUGIN ACTIVITY<< begin return instance
D/Parse Push Notifications( 9707): >>PARSE PLUGIN ACTIVITY<< end return instance
I/Unity ( 9707): >>PUSH NOTIFICATIONS<< Initialized!!!
I/Unity ( 9707): >>PUSH NOTIFICATIONS<< Get User Object ID!!!
I/Unity ( 9707): >>PARSE PUSH NOTIFICATION<< USER OBJECT ID: UserObjectId_12345
So, we can see here that everything seems to be working properly. We can get access to the activity and we're able to get the User Object ID.
Ref #5 - ParsePushNotificationAndroid
Let's take a look at the parse android unity C# script just to make sure it's good.
public class ParsePushNotificationAndroid
{
private static AndroidJavaClass _parsePushAgent;
private static AndroidJavaObject _plugin;
//Returns the parse push notification plugin instance
static ParsePushNotificationAndroid()
{
if( Application.platform != RuntimePlatform.Android )
return;
Util.Instance.DebugLog(">>PUSH NOTIFICATIONS<< Initializing Parse Plugin", (int)GlobalReferences.OUTPUTCHANNEL.OUTPUT_PUSHNOTIFICATIONS);
_parsePushAgent = new AndroidJavaClass( "com.ceGames.parseplugin.ParsePluginActivity" );
_plugin = _parsePushAgent.CallStatic<AndroidJavaObject>("instance");
Util.Instance.DebugLog(">>PUSH NOTIFICATIONS<< Initialized!!!", (int)GlobalReferences.OUTPUTCHANNEL.OUTPUT_PUSHNOTIFICATIONS);
}
public static void Shutdown()
{
if( Application.platform != RuntimePlatform.Android )
return;
_parsePushAgent.CallStatic( "Shutdown" );
}
public static string GetUserObjectId()
{
if( Application.platform != RuntimePlatform.Android )
return "";
Util.Instance.DebugLog(">>PUSH NOTIFICATIONS<< Get User Object ID!!!", (int)GlobalReferences.OUTPUTCHANNEL.OUTPUT_PUSHNOTIFICATIONS);
return _plugin.Call<string>( "GetUserObjectId" );
}
}
When the game starts up, the main game menu calls ParsePushNotificationAndroid.GetUserObjectId(), causing the script to access the plugin, get the User Object ID and then print it out (please see Ref #4 for output).
Ref #6 - Shutting Down
So, all of this happens properly. Until Application.Quit() is called during a point in the game (i.e. Home Button on Main Screen). During the shut down, the following messages are printed, suggesting that the activity is never destroyed, but simply suspended.
I/Unity ( 9707): >>PARSE PUSH NOTIFICATION<< Shutting down!
D/Parse Push Notifications( 9707): >>PARSE PLUGIN ACTIVITY<< ON PAUSE
D/Parse Push Notifications( 9707): >>PARSE PLUGIN ACTIVITY<< SHUTTING DOWN
D/Parse Push Notifications( 9707): >>PARSE PLUGIN ACTIVITY<< Call Activity.Finish()
This tells us that the onPause event got called in the Android Java plugin, and Activity.Finish() was therefore called as well. We would then expect that the plugin activity would startup again (as in Ref #2) after shutting down, and everything would work again.
However, after shutting down, the following Activity Manager events can be noticed.
W/ActivityManager( 1388): Activity pause timeout for HistoryRecord{408ed970 com.ceGames.WordWays/com.ceGames.parseplugin.ParsePluginActivity}
W/ActivityManager( 1388): Unable to start service Intent { act=appStartup }: not found
I/ActivityManager( 1388): Process com.ceGames.WordWays (pid 9707) has died.
I/WindowManager( 1388): WIN DEATH: Window{407d3590 com.ceGames.WordWays/com.ceGames.parseplugin.ParsePluginActivity paused=false}
Ref #7 - On Restart
When looking at the log cat when the game starts up, there is no output related to the ParsePluginActivity starting up again (please see Ref #2). So when the game tries to access it, it tries to create a new instance of ParsePluginActivity (please see Ref #3 ParsePluginActivity.instance() for logic), causing no instance of ParsePluginActivity to be created (I don't know why) and we run into the exception. This is what I'm seeking to actively resolve as I'm assuming that the way that I'm constructing the Activity might not be correct.
I/Unity ( 9747): >>PUSH NOTIFICATIONS<< Initializing Parse Plugin
D/Parse Push Notifications( 9747): >>PARSE PLUGIN ACTIVITY<< begin return instance
D/Parse Push Notifications( 9747): >>PARSE PLUGIN ACTIVITY<< Create new ParsePluginActivity
I/Unity ( 9747): >>PUSH NOTIFICATIONS<< Initialized!!!
I/Unity ( 9747): >>PUSH NOTIFICATIONS<< Get User Object ID!!!
D/dalvikvm( 9747): GetMethodID: method not found: Ljava/lang/RuntimeException;.GetUserObjectId:()Ljava/lang/String;
E/dalvikvm( 9747): Class lookup Ljava/lang/NoSuchMethodError; attempted while exception Ljava/lang/RuntimeException; pending
If I can be of any more help with information, please ask me a question. I'm looking forward to getting this resolved.
Cheers!
Your answer
Follow this Question
Related Questions
How Set Java Plugin on Unity? 0 Answers
Android/Unity/Eclipse Help 0 Answers
How to start the UnityPlayerActivity from a java plugin that overrides the basic Android Activity 1 Answer
How can I replace correctly the main activity on the Android Manifest? 0 Answers
Passing texture to android plugin 0 Answers