Understanding Unity Scene and Android Activity Integration and Custom Unity Android Plugins
The article explains that each Unity scene runs inside an Android Activity by embedding a UnityPlayer FrameLayout, details how UnityPlayerActivity and GoogleUnityActivity load scenes, shows how to subclass these activities for custom integration, and outlines essential steps for creating reliable Unity‑Android plugins and handling assets.
Recently, the author needed to call Android Activity APIs from a Unity Scene script component, which required clarifying the relationship between Unity Scenes and Android Activities and investigating how Unity interacts with Android.
The article explores the mapping between Unity Scenes and Android Activities, the differences between Unity‑generated APKs and those built directly with Android Studio, and how to leverage these differences.
Tools used:
apktool (Android decompilation)
Android Studio built‑in decompiler
1. What is the entry point of an APK compiled from a Unity Scene?
Create a Unity project, add a Scene, and build an APK.
Decompile the APK with apktool d unityTest.apk .
Inspect the generated AndroidManifest.xml :
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.xfiction.p1" platformBuildVersionCode="25" platformBuildVersionName="7.1.1">
<application ...>
<activity android:name="com.unity3d.player.UnityPlayerActivity" android:launchMode="singleTask" ...>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>The manifest shows that the main Activity is com.unity3d.player.UnityPlayerActivity . The question then becomes: how does the Scene get loaded into this Activity?
2. How does UnityPlayerActivity load a Unity Scene?
2.1 UnityPlayerActivity source
By decompiling the Unity Android plugin ( classes.jar ) we obtain part of the Activity source:
public class UnityPlayerActivity extends Activity {
protected UnityPlayer mUnityPlayer;
protected void onCreate(Bundle var1) {
this.requestWindowFeature(1);
super.onCreate(var1);
this.getWindow().setFormat(2);
this.mUnityPlayer = new UnityPlayer(this);
this.setContentView(this.mUnityPlayer);
this.mUnityPlayer.requestFocus();
}
}The call setContentView(this.mUnityPlayer) indicates that the UI is provided by an instance of UnityPlayer .
2.2 GoogleUnityActivity (VR SDK) analysis
The layout file activity_main.xml used by GoogleUnityActivity contains only a FrameLayout :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/android_view_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent" />
</FrameLayout>In its onCreate method, the activity adds the Unity view:
public class GoogleUnityActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback {
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUnityPlayer = new UnityPlayer(this);
((ViewGroup) findViewById(android.R.id.content)).addView(mUnityPlayer.getView(), 0);
mUnityPlayer.requestFocus();
}
}The Unity view is added to the root view ( android.R.id.content ), which is the container of the whole activity.
2.3 What is UnityPlayer?
Decompiling the same classes.jar yields a partial implementation of UnityPlayer :
public class UnityPlayer extends FrameLayout implements com.unity3d.player.a.a {
public static Activity currentActivity = null;
public UnityPlayer(ContextWrapper var1) {
super(var1);
if (var1 instanceof Activity) {
currentActivity = (Activity) var1;
}
}
public View getView() { return this; }
public static native void UnitySendMessage(String obj, String method, String msg);
private final native boolean nativeRender();
}Key points:
UnityPlayer extends FrameLayout , acting as a container view.
It stores a static reference to the current Activity.
The native rendering logic is hidden in native code.
Thus, both UnityPlayerActivity and GoogleUnityActivity ultimately display the Unity scene by embedding a UnityPlayer view into their activity hierarchy.
3. Displaying a Scene in a Custom Activity
To show a Unity Scene in a user‑defined Activity, one can subclass either UnityPlayerActivity or GoogleUnityActivity and handle the UnityPlayer initialization in onCreate . The C# side can then obtain the activity via UnityPlayer.currentActivity and invoke Java methods.
4. Important Considerations for Unity Android Plugins
When an Android Studio project contains multiple modules, copy the generated plugin JAR/AAR into Plugins/Android/lib of the Unity project.
Remove any duplicate unity libraries from the AAR to avoid manifest or class conflicts.
Ensure consistent applicationId and minSdkVersion across modules to prevent manifest merger failures.
To merge Unity’s AndroidManifest with a custom one, place the custom manifest in Plugins/Android so Unity will not merge it automatically.
5. Investigating Unity‑Generated APK Structure
By renaming the APK to .zip and comparing it with a regular Android APK, the only additional directory is assets/bin , which contains Unity native libraries and data. Copying this directory into an Android Studio project’s src/main/assets allows the Unity runtime to be packaged and executed from the native Android side.
The workflow typically involves:
Copy assets/bin from the Unity‑built APK into the Android Studio project.
Build the Android project in “app” mode for debugging.
After debugging, switch the Gradle plugin to “library” mode, build an AAR, remove the previously copied Unity assets, and place the AAR into Plugins/Android/lib for Unity to consume.
6. Conclusion
Each Unity Scene runs inside an Android Activity as a FrameLayout provided by UnityPlayer . The current activity can be accessed via UnityPlayer.currentActivity .
To load a Scene in a custom Activity, inherit from UnityPlayerActivity or GoogleUnityActivity , or implement a similar wrapper.
Efficient Unity‑Android plugin development involves sharing the assets/bin directory, correctly configuring Gradle modules, and avoiding duplicate Unity libraries in the final AAR.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.