Call a method in an activity? (using AndroidJavaObject?)

Hi,

I added a .jar in the Android folder, and I would like to call a method “startFunc” and see if the connection works fine between Unity and the plugin. When I use a static method inside a class that does not extend “Activity”, the log works fine. But when I want to use “AndroidJavaObject” with a public void method, in a class that extends Activity, the log never shows up.

Would someone know how to call “startFunc” which is inside “MainActivity” from Unity?

Something like this :

AndroidJavaObject jo = new AndroidJavaObject("com.my.test.MainActivity");
	jo.Call<AndroidJavaObject>("startFunc");

It does not work, the Log.i() never shows up in the Terminal with AndroidJavaObject.

It works with a static class though, and AndroidJavaClass, like in the following code, but I don’t know how to call MainActivity then from the static method “startFunc”?

//in  Unity
AndroidJavaClass jc = new AndroidJavaClass("com.my.test.MyClass");
jc.CallStatic("startFunc");

//in Eclipse
public class MyClass {
	public static void startFunc() {
		//...
    }
}

Thanks

Your first code snippet does the following:

  1. Create an instance of MainActivity.
  2. Call an instance method (NOT a static method) on the newly created object.

However, since your method is static, the above code doesn’t make sense. In fact, it should raise an exception, if you look close enough.

Your second snippet does the right thing:

  1. Get a handle to the class MyClass.
  2. Call the static method startFunc.

Since it matches your Java code, it runs fine.

If your MainActivity has a static method startFunc, then the code that runs it should look like your second snippet, not the first.

Sorry, I maybe not said that with JavaObject I did not use a static method. With the simple example with a static method, it works fine. Now, I would like to to call a simple method “public void startSimpleFunc()” inside MainActivity.

(I also tried to use the same static method “startFunc” though in MainActivity, but it means the variables have to be static too, and there is a problem with how to call “context” in the static method. )

Anyway, if you would know how to use JavaObject the correct way with an Activity ?

Thank you very much

Calling “new AndroidJavaObject()” creates a new instance of the class you pass to the constructor, in your case a new instance of the MainActivity. What you need is to get an already existing instance of the MainActivity. Consider implementing the singleton pattern in the main activity, that is, a static method that will return an instance of the activity. Alternatively, maybe there are static utility methods to get the currently active activity in the Android SDK, but you’ll have to search for it.

If you’re implementing UnityPlayerActivity you can use UnityPlayer.currentActivity to retrieve the current activity in your MainActivity class.

Alternatively, you can store the Context value when Android activity is created:

public class MainActivity extends UnityPlayerActivity {

	public static Context mContext;

	@Override
	public void onCreate(Bundle bundle)
	{
		super.onCreate(bundle);
		mContext = this;
	}

	public void startFunc()
	{
		// Your function here, use mContext to retrieve current context/activity
	}
}
1 Like

So, maybe you can help me understand it better.

Is it Class or Object to “define” the class? I found both examples on the net :

//CLASS
AndroidJavaClass cls = new AndroidJavaClass("com.my.test.MainActivity");//class
AndroidJavaObject obj = cls.Call<AndroidJavaObject>("startFunc");

//OR OBJECT, the same?
AndroidJavaObject jo = new AndroidJavaObject("com.my.test.MainActivity");//object
AndroidJavaObject obj = cls.Call<AndroidJavaObject>("startFunc");

Then, with this example, the log returns nothing, whereas with the example in my first post (with CallStatic) the log returned something. So it makes me think I don’t know how to use JavaObject.

With the code above in Unity (with class or object), then with this in Eclipse, the ./adb logcat never shows this log :

//#1
public class MainActivity extends Activity {
	
	public void startFunc(){
		Log.i("------- ME -------", "--------------- ME ----------------");
		Log.i("------- ME -------", "--------------- ME ----------------");
		Log.i("------- ME -------", "--------------- ME ----------------");
		Log.i("------- ME -------", "--------------- ME ----------------");
		Log.i("------- ME -------", "--------------- ME ----------------");
		Log.i("------- ME -------", "--------------- ME ----------------");

So there is no connection with “JavaObject”.

I also tried with a Singleton, I am not sure if this is what you wanted to tell me Vladimirg, but when I looked for some examples of a Singleton with an Activity, I found answers saying it should work with an extent of “Application” :

(Edit: I actually think this is wrong because the singleton is not MainActivity, but I don’t really know how to make MainActivity as the Singleton.)

//#2

//MAIN ACTIVITY
@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//Singleton
	    app = (MyApplication)getApplication();
	    MySingleton.getInstance().customSingletonMethod();
	}
	
	
	

//APPLICATION
import android.app.Application;

public class MyApplication extends Application
{
  @Override
  public void onCreate()
  {
    super.onCreate();
    initSingletons();
  }
 
  protected void initSingletons()
  {
    MySingleton.initInstance();
  }
}


//#SINGLETON
public class MySingleton
{
  private static MySingleton instance;
   
  public static void initInstance()
  {
    if (instance == null){
      instance = new MySingleton();
    }
  }
 
  public static MySingleton getInstance()
  {
    return instance;
  }
   
  private MySingleton() {  }
   
  public void customSingletonMethod()
  {
	    Log.i("------- ME -------", "------////--------- ME ----------------");
		Log.i("------- ME -------", "-----////------- ME ----------------");
		Log.i("------- ME -------", "-----////-------- ME ----------------");
		Log.i("------- ME -------", "-----////-------- ME ----------------");
		Log.i("------- ME -------", "------////------- ME ----------------");
  }
}

This does not show anything in the Terminal.

Maybe you have an example that works with JavaObject ? (or a correct Singleton?)

@jvil: it seems extending this class could lead to issues with multiple plugin? If so, I should maybe try not to extend it, if by any chance I can make this work :slight_smile:

It does not cause conflicts with another plugins if you know how to declare correctly your Android manifest. Read about on Android developer website and Android plugins docs for Unity.

You don’t have to extend Activity, instead extend UnityPlayerActivity.

Thanks jvil. I cannot test it because I always get the same error : Eclipse keeps trying to search for a previous class that used UnityPlayerActivity, from another package, which was already deleted. Because it does not find it, it always send an error : (my package is not com.jidul.unity_plugin but com.myPlugin.plugintest)

And Android closes the app before it is launched, saying “PluginTest” (the name of the previous project) could not be opened.

I cleaned all the projects in Eclipse, I removed the previous project, I switched off and on the computer. I reimported “classes.jar” from the path in Unity, but it still looks for this project. Does it come from Unity and the “classes.jar” ? Is there a way to refresh Unity?

My AndroidManifest is like this, and nothing is referring to the previous package anymore (in Eclipse or in Unity) :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.myPlugin.plugintest">
  
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">
	
	<activity android:name=".MainActivity"
			  android:label="@string/app_name"
			  android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
        <intent-filter>
			<action android:name="android.intent.action.MAIN" />
			<category android:name="android.intent.category.LAUNCHER" />
		</intent-filter>
	</activity>
	
  </application>
</manifest>

(Second little question : I will then have to put all my plugins in the same package in Eclipse, but with different activities, is that correct? So that the package name in Unity stays the same with “com.myPlugin.plugintest” (in the AndroidManifest), and let me avoid any conflict with UnityPlayerActivity?)

Create a new Android project on Eclipse, empty. Link classes.jar to this project from your Unity folder, then export your class to Assets/Plugins/Android folder.

Also, re-paste the full code of your plugin here and the call from Unity you’re using.

You can create any package name you want, also can create other package names, it does not matter if you reference the packages name correctly.

OK, with a new project in Unity, it launches the app. But the error is the same in the logcat of Eclipse, with the correct project name this time, the error is :

“hello” is the log I added in GuiTest.cs, the script attached to a gameObject in the scene :

public class GUITest : MonoBehaviour {
	
	void OnGUI () {
		GUI.Box(new Rect(10,10,100,90), "Android Plugin");
		
		if(GUI.Button(new Rect(20,40,150,150), "Test android")) {
			Debug.Log("hello");
			AndroidJavaObject jo = new AndroidJavaObject("com.plugin.one");
			AndroidJavaObject obj = jo.Call<AndroidJavaObject>("startFunc");

Then in Eclipse, the MainActivity :

import com.unity3d.player.UnityPlayerActivity;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends UnityPlayerActivity {
	
	public void startFunc(){
		Log.i("test","-----------------test");
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//setContentView(R.layout.activity_main);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		//getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

And the Manifest in Unity, under Plugins > Android :

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.plugin.one">
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">

    <activity android:name=".MainActivity"
              android:label="@string/app_name"
              android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

  </application>
</manifest>

Do you see anything wrong?

1 Like

There are some issues on your code.

  • startFunc returns void, your Call function don’t expect any return-type.
  • You need to get a reference of the current Android activity because you’re calling a non-static method

Try this:

Android code

public class MainActivity extends UnityPlayerActivity {
	
	public static Context ctx;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
        	super.onCreate(savedInstanceState);
		    ctx = this;
	}

	public void startFunc(){
        	Log.i("test","-----------------test");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		return true;
    }
}

Unity code

using (AndroidJavaClass javaClass = new AndroidJavaClass("com.plugin.one.MainActivity"))
{
	using (AndroidJavaObject activity = javaClass.GetStatic<AndroidJavaObject>("ctx"))
	{
		activity.Call("startFunc");
	}
}

Here I’m saving a Context on ctx (where you can get the current activity) and you get this static field to obtain the current activity on your plugin. Once you have it, then just call the non-static method without any return-type because startFunc it’s a void function.

2 Likes

OK, very nice, I understand. I think you meant “public static Context ctx;” (with the static word), it works fine, thanks a lot.

I am wondering how I should do with the Manifest to add more plugins? If I have this second package “com.otherPlugin.two” (with the same class using “startFunc”), how should I write the Manifest?

Yes, that’s right, Context should be static on your Android code, I changed on previous post.

Simply declare your new Activity in your Android Manifest, check the Android developer docs.

Ok, I have got this :

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">

    <activity android:name="com.plugin.one.MainActivity"
              android:label="@string/app_name"
              android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    
    <activity android:name="com.myOtherPlugin.two.MainActivity" />

  </application>
</manifest>

and this code in Unity :

	void OnGUI () {
		GUI.Box(new Rect(10,10,100,90), "Android Plugin");
		
		if(GUI.Button(new Rect(20,40,150,150), "TEST")) {
			Debug.Log("hello ONE");
			using (AndroidJavaClass javaClass = new AndroidJavaClass("com.plugin.one.MainActivity"))
			{
				using (AndroidJavaObject activity = javaClass.GetStatic<AndroidJavaObject>("ctx"))
				{
					activity.Call("startFunc");
				}
			}
		}


		if(GUI.Button(new Rect(20,250,150,150), "TWO")) {
			Debug.Log("hello TWO");
			using (AndroidJavaClass javaClassB = new AndroidJavaClass("com.myOtherPlugin.two.MainActivity"))
			{
				using (AndroidJavaObject activityB = javaClassB.GetStatic<AndroidJavaObject>("ctx"))
				{
					activityB.Call("startFunc");
				}
			}
		}

and in Eclipse, in the project “Two”, in the package : com.myOtherPlugin.two :

	public void startFunc(){
		Log.i("TWO test","-----------------TWO test");
	}

This never shows up. Would you know why? (I removed the “package=” in the title in the Manifest, and let MAIN and LAUNCHER for the first plugin.

That’s because you are don’t receiving the correct Activity on your second plugin.

The Context on your second plugin is never filled because the onCreate method on your second plugin is never called. You don’t need to create a new Context on each Activity.

You need to structure your project and plugins to only get one instance of your Android activity, for example using a Singleton or similar.

Could you explain me how it works? Unity needs one activity only to be running, which is given by

AndroidJavaClass ajc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject ajo = ajc.GetStatic<AndroidJavaObject>("currentActivity");

If I put com.plugin.test.MainActivity as the MAIN and LAUNCHER activity, it will start this activity as the “current activity” ?
So then, I have a different package, com.myOtherPlugin.two.SecondActivity, for the second plugin…

Should I instead create a package like “com.my.root.RootPackage”, and a static variable for the Context, then make the calls in Eclipse to the first package for MainActivity and to the second package for SecondActivity via “Intent” (so in Unity, replace the MAIN and LAUNCHER activity with “.RootActivity”) ?
Or, do you know a solution to do this from the .cs file in Unity?

Thanks, again, I really appreciate your help.

Simplify everything, keep it simple. If you write your custom Android plugins I’ll suggest to create a MainPlugin and let this plugin handle all the calls to other packages or classes, this will make your life easier.

You just need to know which Activity are running your plugins, by default will be the same Activity, but will be a different Activity only if you launch a new Android Activity on your plugin. The Unity Player Activity runs on the same Activity of your plugin when you extend UnityPlayerActivity, so the currentActivity you get is the same as your MainPlugin Context.

jvil, thank you, very very much, it works fine.

Thanks a lot for your answers. Problem fixed!

EDIT: if this can help :

To create a plugin with Android :

1.Create a project “as a library” in Eclipse

2.Copy the “Classes.jar” from the location of Unity to the libs folder in Eclipse, using the Terminal (Mac) :

cp /Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/bin/classes.jar /Users/YourName/yourWorkspaceInEclipse/NameOfProject/libs

right click on the project > Properties > Add JARs > click on the new Classes.jar in libs

3.Add this in the class “MainActivity” :

import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {
	
	public void startOne(){ //same thing with "startTwo"
		Log.i("-----------","------TEST-------");
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	}
}

4.Add this code in OnGUI() when we click on a button :

void OnGUI () {
		GUI.Box(new Rect(10,10,100,90), "Android Plugin");

		if(GUI.Button(new Rect(20,50,150,150), "ONE")) {
			using (AndroidJavaClass javaClass = new AndroidJavaClass("com.plugin.one.MainActivity"))
			{
				using (AndroidJavaObject activity = javaClass.GetStatic<AndroidJavaObject>("ctx"))
				{
					activity.Call("startOne");
				}
			}
		}

		if(GUI.Button(new Rect(20,250,150,150), "TWO")) {
			using (AndroidJavaClass javaClass = new AndroidJavaClass("com.plugin.one.MainActivity"))
			{
				using (AndroidJavaObject activity = javaClass.GetStatic<AndroidJavaObject>("ctx"))
				{
					activity.Call("startTwo");
				}
			}
		}
  1. copy the .jar file from the bin folder of the Eclipse project, and add it under Plugins > Android in Unity (create the folders if they don’t already exist).

Done!

Hi,

Am able to access the static method where as am unable to access the
Non-static methods.

Every time am getting an error like “exception: jni: init’d androidjavaobject
with null ptr!”

Eclipse code :

package com.example.testingjar;
import com.unity3d.player.UnityPlayerActivity;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.unity3d.player.UnityPlayer;
public class Main extends UnityPlayerActivity {

         public static Context mContext;
         public static test instance;
         @ 
         protected void onCreate(Bundle bundle)
         {
                 super.onCreate(bundle);
                mContext = this;
                test.instance = this;
                Log.d("TAG","MY JAR Test");
         }

         public void nonStaticMethod()
         {
             UnityPlayer.UnitySendMessage("GameManager",
"HelloFromAndroidStatic", "Hello!");

         }
         public static void StaticMethod(){
             UnityPlayer.UnitySendMessage("GameManager",
"HelloFromAndroidStatic", "Hello world!");
         }
}

Eclipse Androidmanifest :

<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testingjar"

    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label=" @anon_76954444 /app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".Main"
            android:label=" @anon_76954444 /app_name">
            <intent-filter>
                <actionandroid:name="android.intent.action.MAIN"/>
                <categoryandroid:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

Unity Code :

void OnGUI(){
        if(GUI.Button(new Rect(0,0,150,100),"static")){
            AndroidCallStatic();
        }
        if(GUI.Button(new Rect(0,200,150,100),"Non-Static")){
            AndroidCallNonStatic();
        }
    }
    public void AndroidCallStatic()
    {

        using (AndroidJavaClass javaClass = new
AndroidJavaClass("com.example.testingjar.Main"))
        {
            javaClass.CallStatic("StaticMethod");
        }
    }
    public void AndroidCallNonStatic()
    {

        using (AndroidJavaClass javaClass1 = new
AndroidJavaClass("com.example.testingjar.Main"))
        {

            using (AndroidJavaObject activity = javaClass1.GetStatic("mContext"))
            {
                activity.Call("nonStaticMethod");
            }

        }
    }

For Send messages from Eclipse :

void HelloFromAndroidStatic (string MSG) {
        print ("Unity Static  : "+MSG);
    }

    void HelloFromAndroidNonStatic (string MSG) {
        print ("Unity Non-Static  : "+MSG);
    }

Successfully am able to see the print Unity Static : Hello! when i click on
static button of unity.

The problem is with the non-static method calling only.

Can you help me to resolve the issue.

I know this is old but THANK YOU!!! That was, at least for me, the clearest explanation of how to do this. You rock! :smile: