Errors managing the UnityPlayer lifecycle in a native android application
android, android-activity, unity-game-engine
Solution
Ok, easy things first
W/libc(21095): pthread_create sched_setscheduler call failed: Operation not permitted
There is nothing you can do about it. You even get this when you compile directly from Unity for Android, so it's a problem inside the engine.
Basic Setup
The guide you linked is pretty outdated. You no longer need to copy files from various locations to create a simple Android project.
- Create a Android project by setting `Build Settings -> Android -> Google Android project`
- You now have a complete package ready to import into Eclipse or Android Studio
- Compile and deploy
Using UnityPlayer in a subactivity
The class `UnityPlayerNativeActivity` in your new Android project shows you how to setup the `UnityPlayer` and what events you need to forward. Here is the version used by Unity 4.3.4
package de.leosori.NativeAndroid;
import com.unity3d.player.*;
import android.app.NativeActivity;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class UnityPlayerNativeActivity extends NativeActivity
{
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
// UnityPlayer.init() should be called before attaching the view to a layout - it will load the native code.
// UnityPlayer.quit() should be the last thing called - it will unload the native code.
protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().takeSurface(null);
setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
getWindow().setFormat(PixelFormat.RGB_565);
mUnityPlayer = new UnityPlayer(this);
if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))
getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
boolean trueColor8888 = false;
mUnityPlayer.init(glesMode, trueColor8888);
View playerView = mUnityPlayer.getView();
setContentView(playerView);
playerView.requestFocus();
}
protected void onDestroy ()
{
mUnityPlayer.quit();
super.onDestroy();
}
// onPause()/onResume() must be sent to UnityPlayer to enable pause and resource recreation on resume.
protected void onPause()
{
super.onPause();
mUnityPlayer.pause();
}
protected void onResume()
{
super.onResume();
mUnityPlayer.resume();
}
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
mUnityPlayer.configurationChanged(newConfig);
}
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
mUnityPlayer.windowFocusChanged(hasFocus);
}
public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
return mUnityPlayer.onKeyMultiple(event.getKeyCode(), event.getRepeatCount(), event);
return super.dispatchKeyEvent(event);
}
}
Although `UnityPlayerNativeActivity` extends `NativeActivity` you can still extend from `ActionBarActivity` instead without any problems as far as I can tell. At least it worked during my experiments.
The most important part you are missing is the call to `mUnityPlayer.quit()` during `onDestroy()`. Trying to create a new instance of `UnityPlayer` while the old one is still running will lead to crashes, hanging activities and endless suffering.
Unexpected behavior of mUnityPlayer.quit()
Fixing that you may be surprised that now your whole App simply closes when you return from your `UnityActivity`. `mUnityPlayer.quit()` will kill the process it is running inside. Not a single method will execute after calling `mUnityPlayer.quit()`, not even the `onDestroy()` method will finish.
The path to victory is to start your `UnityActivity` as a new process by adding the parameter `android:process=":UnityKillsMe` to your activty inside your `AndroidManifest.xml`.
In your case it would look like this
<activity
android:name="com.package.example.UnityActivity"
android:label="@string/title_activity_unity"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:process=":UnityKillsMe"
android:parentActivityName="com.package.example.MainActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale">
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
<meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
</activity>
I'm not sure about the parameter `unityplayer.ForwardNativeEventsToDalvik`... The project created in the beginning sets it to `false` and the official (outdated) documentation mentions
Since touch/motion events are processed in native code, Java views would normally not see those events. There is, however, a forwarding mechanism in Unity which allows events to be propagated to the DalvikVM.
In my small example project I could not see a difference
The road ahead
You need to find a workflow to integrate your development with Unity with the Android project or vice versa. Exporting again with Unity would conflict with the changes you made in your Android project, so you would need to export into a separate folder and link to the Unity part from your Android project.
According to the aforementioned documentation you may be able to integrate your compiled Android classes and `AndroidManifest.xml` as plugins into Unity.
The resulting .class file(s) should be compressed into a .jar file and placed in the Assets->Plugins->Android folder. Since the manifest dictates which activity to launch it is also necessary to create a new AndroidManifest.xml. The AndroidManifest.xml file should also be placed in the Assets->Plugins->Android folder.
Good luck!
Problem
I am working on an android app that needs to load a UnityPlayer instance in an activiy, using code from the following forum post as a guide: http://forum.unity3d.com/threads/98315-Using-Unity-Android-In-a-Sub-View . Initially the application is correctly displaying the UnityPlayer inside an activity called "UnityActivity.java". The problem starts when the user navigates back to the MainActivity (by either pressing the hardware back button or clicking on the ActionBar back button) and then tries to re-open the UnityActivity - in which case a black screen is shown instead of the UnityPlayer. A user in the forums suggested forwarding the onPause and onResume lifecycle events to the UnityPlayer, as shown in the code bellow. When doing that, however, the following errors show up and the app crashes. This is logged when navigating to the UnityActivity for the first time: `W/libc(21095): pthread_create sched_setscheduler call failed: Operation not permitted` This error is logged when clicking the back button: `W/Choreographer(20963): Already have a pending vsync event. There should only be one at a time.` This error is logged when navigating to the UnityActivity for the second time: `A/libc(21095): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 21176 (Thread-5073)` ...at which point I get kicked out of the application. Code This is an excerpt of the main activity `MainActivity.java` : ``` public void startUnityActivity(View view) { Intent intent = new Intent(this, UnityActivity.class); startActivity(intent); } ``` This is an excerpt of the Unity activity `UnityActivity.java` : ``` public class UnityActivity extends ActionBarActivity { UnityPlayer m_UnityPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_unity); m_UnityPlayer = new UnityPlayer(this); int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1); m_UnityPlayer.init(glesMode, false); FrameLayout layout = (FrameLayout) findViewById(R.id.unityView); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); layout.addView(m_UnityPlayer, 0, lp); m_UnityPlayer.windowFocusChanged(true); m_UnityPlayer.resume(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); m_UnityPlayer.windowFocusChanged(hasFocus); } @Override public void onPause() { super.onPause(); m_UnityPlayer.pause(); } @Override public void onResume() { super.onResume(); m_UnityPlayer.resume(); } ``` This is how the activities are described in the manifest `../AndroidManifest.xml`: ``` <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.package.example.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.package.example.UnityActivity" android:label="@string/title_activity_unity" android:screenOrientation="portrait" android:launchMode="singleTask" android:parentActivityName="com.package.example.MainActivity" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"> <meta-data android:name="unityplayer.UnityActivity" android:value="true" /> <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" /> </activity> </application> ``` This is how the layout of the UnityActivity is defined `../res/layout/activity_unity.xml`: ``` <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.package.example.UnityActivity" tools:ignore="MergeRootFrame" > <FrameLayout android:id="@+id/unityView" android:layout_width="fill_parent" android:layout_height="fill_parent" > </FrameLayout> </FrameLayout> ``` I'd be thankful for any tips and solutions pointing me in the right direction.