2

I am currently training to make applications which use Unity to add some features (AR, VR, etc...). For now I've been working on Android, with Android Studio, and once I'll be done with I'll train on iOS.

My aim is simple: my MainActivity displays two buttons, each one calls a separate Unity project (exported from Unity as Google Android project) to launch its scene.

To do so, I imported those two Unity projects Scene1 and Scene2 as libraries and I call them by starting their activities (see the code below).

public class MainActivity extends AppCompatActivity {

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

    public void goToUnity1(View v){
        Intent intent = new Intent(this, com.example.unityscene1.UnityPlayerActivity.class);
        startActivity(intent);
    }

    public void goToUnity2(View v){
            Intent intent = new Intent(this, com.example.unityscene2.UnityPlayerActivity.class);         
            startActivity(intent);

     }
}

And its AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.multipleunity">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity android:name="com.example.unityscene1.UnityPlayerActivity"></activity>
    <activity android:name="com.example.unityscene2.UnityPlayerActivity"></activity>


</application>

The UnityPlayerActivity files in Scene1 and Scene2 are generated by Unity so they are similar (here are their onCreate method):

public class UnityPlayerActivity extends Activity {
    protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code


    // Setup activity layout
    @Override protected void onCreate (Bundle savedInstanceState)
    {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);

        getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy

        mUnityPlayer = new UnityPlayer(this);
        setContentView(mUnityPlayer);
        mUnityPlayer.requestFocus();
    }

    ...
}

The code compiles without any problem, the application launches and displays the MainActivity with its two buttons. When I click on the first button, it launches the first Unity scene as I expected. However, when I click on the second button it also launches the first Unity scene instead of the second one.

So I tried to understand why this happens and here is what I can tell so far:

  • I didn't put twice the same Unity project by mistake
  • Before transforming Scene2 into library it works well on its own
  • The activity com.example.unityscene2.UnityPlayerActivity is called when I click on the second button

UPDATE

After spending hours on this I'm sure the problem comes from the name of the ressources Unity delivers when I exported in Google Android project which are always the same (unity-classes.jar).

Here's what I read here:

The Android development tools merges the resources of a library project with the resources of the application project. In the case that a resource’s ID is defined several times, the tools select the resource from the application, or the library with highest priority, and discard the other resource.

Adding to that, if I have multiple libraries the priority matches the dependency order (the order they appear in the Gradle dependencies block).

So I tried to reverse the order of the dependencies, I put the second Unity project before the first one and as that was expected both buttons launch the second Scene of Unity.

Now I know that, is there a way to avoid this name conflict ?

1
  • Did you solve this issue? Commented Aug 29, 2021 at 7:46

2 Answers 2

2

I did not solve my problem, but I managed to find a way to avoid it: instead of importing two small Unity projects I decided to make a bigger one which includes two different scenes (one with Vuforia AR and the other with some UI text). Then the challenge was to find a way to call the right Unity scene when I click on the associated button I designed on Android.

Unity

On each scene I created an empty object (I called it SceneManagerObject) which will be attached the script GoToScene. In this script there will be the following methods:

private void ChangeScene(string sceneName)
{
    SceneManager.LoadScene (sceneName);
}

public void ReceiveJavaMessage(string message)
{
    if (message.Equals ("Scene1") || message.Equals ("Scene2")) 
    {
        ChangeScene (message);
    } 
    else 
    {
        Debug.Log ("The scene name is incorrect");
    }
}

The second method will be the one being called from the Java code.

Then I created a UI button on each scene which will call onClick method when clicked, its purpose is to be able to go back to Android without destroying the Unity activity so we can resume it after if I wanted to. For this, I created another Empty Object with the script GoBackToAndroid attached:

public class GoBackToAndroid : MonoBehaviour {
public string activityName; // contains the name of the activity I want to go when I quit this scene

public void onClick()
{
    callJavaMethod();
}


private void callJavaMethod()
{
    AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer);
    AnroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
    jo.Call("ReceiveUnityMessage", activityName); // Will call a method in Java with activityName as parameter
}

That's it for the Unity, after I exported the project as a Google Android project and I kept it warm for the next step.

Android Studio

After importing the Unity project as a library. I created three activities: MainActivity, SecondActivity and MultipleScenesUnityActivity:

public class MultipleScenesUnityActivity extends UnityPlayerActivity {
    public static boolean unityIsRunning = false; // true if a Unity Activity is running on background


    @Override protected void onDestroy()
    {
        unityIsRunning = false;
        super.mUnityPlayer.quit();
        super.onDestroy();
    }

    @Override protected void onResume()
    {
        Intent currentIntent = getIntent();
         // If there's no Unity Activity inside the back stack
        // (meaning it just had been created)
        if (!unityIsRunning)
        {
             super.mUnityPlayer.UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", currentIntent.getStringExtra("sceneName");
        }
        unityIsRunning = true;
        super.onResume();
        super.mUnityPlayer.resume();
    }

    public void ReceiveUnityMessage (String messageFromUnity)
    {
        Intent intent;
        if (messageFromUnity.equals("MainActivity"))
        {
            intent = new Intent(this, MainActivity.class);
            startActivity(intent);
        }
        else if (messageFromUnity.equals("SecondActivity"))
        {
            intent = new Intent(this, SecondActivity.class);
            startActivity(intent);
        }
        else 
            Log.d("Test", "The activity " + messageFromUnity + " doesn't exist");
    }
}

The ReceiveUnityMessage is the method called when we leave the Unity activity and its purpose is to lead us to the activity we desire to go.

Then the MainActivity (the SecondActivity follows the same pattern):

public class MainActivity extends AppCompatActivity {

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

    public void goToActivity2(View view) // called when I click on a button
    {
        Intent intent = new Intent(this, SecondActivity.class);
        startActivity(intent);
    }

    public void goToUnity(View view) // called when I click on another button
    { 
        Intent intentUnity = new Intent(this, MultipleScenesUnityActivity.class);

        if (unityIsRunning)
        {
            intentUnity.removeExtra("sceneName"); // We have to clean the extra before putting another one otherwise we'll get always the same Unity scene

            /*  If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought
             *  to the front of its task's history stack if it is already running. */
            intentUnity.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

            /* We send a string to Unity which, when received, will change the scene.
            * This ONLY works when the Unity activity is running on background. */
            UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", "Scene1");
        }

        else
        {
            /* The intent gets a key with its String value. The value has to be the name of the Unity
            * scene and is received in UnityActivity1 when the activity starts.*/
            intentUnity.putExtra("sceneName", "Scene1");
        }
        startActivity(intentUnity);
    }
}

So what I did is connecting everything by sending messages so I can manage the scene I'd like to launch and the same goes for the activities.

I'm sure there are a better ways to do so, I'm still a novice and I can be clumsy.. Anyway, I managed to avoid my original problem so if anyone faces the same issue this answer might be an interesting alternative.

1
  • Hi Frobei, did this work for you? Or did you find a better way? I've been trying to get this to work. For me, the same scene opens up every time, regardless of which button I press. And the back button to go to Android app doesn't work at all. I'm a Unity novice so maybe I missed something in the setup.
    – Hadi
    Commented Sep 20, 2019 at 1:48
0

One possible solution which i came across is dynamically loading multiple smaller unity apks inside a single apk . I have done some research and found out that it is possible to launch one apk from other without even actually installing the apk. This can be done by various methods and there are many library available for doing this to. Few of them are.

DexClassLoader class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.

Grab-n-Run Securely load code dynamically into your Android application from APK containers or JAR libraries translated to be executable by both the Dalvik Virtual Machine (DVM) and the Android Runtime (ART).

5
  • That seems interesting indeed. I didn't know that was possible, though I knew we can launch an app from another one but I didn't want to install an app for each Unity project. Did you manage to make this work ?
    – Frobei
    Commented Apr 7, 2017 at 13:05
  • It is possible to load apk without installing.I tried to implement using this approach but only class files can accessed by this method and not resource files hence this approach fails . Looking forward for a better solution but it seems like impossible task to me . Commented Apr 7, 2017 at 13:32
  • Then maybe you can try what I did, however you may need to fuse your two Unity projects into one before. I don't know how to to it but it doesn't seem impossible and then you can call your scenes from Android.
    – Frobei
    Commented Apr 7, 2017 at 14:12
  • I have mentioned that adding two unity project as module in single app seems to be impossible as you cannot have two version of JNI libs in a single app . Commented Apr 10, 2017 at 5:01
  • I agree with you we can't (or I don't know how) add some Unity projects as modules. However you can try to fuse your projects into a big Unity project so you'll have to import only this one and you'll get one module. Hence you won't have any conflict this way. I did not try to fuse Unity projects, I just created one that has 2 scenes which I imported into Android Studio and I'm able to call them thanks to a Java method.
    – Frobei
    Commented Apr 10, 2017 at 6:48

Not the answer you're looking for? Browse other questions tagged or ask your own question.