Errors Managing the Unityplayer Lifecycle in a Native Android Application

Errors managing the UnityPlayer lifecycle in a native android application

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.

  1. Create a Android project by setting Build Settings -> Android -> Google Android project
  2. You now have a complete package ready to import into Eclipse or Android Studio
  3. 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!

Vuforia Unity to Android - QCAR has already been initialized

I figure it out myself. Previously, I have added the code mQCARShared.onDestroy() inside QCARActivity.onDestroy() as shown in the code below. This will supposedly de-init any QCAR resources and kills them.

protected void onDestroy ()
{
mQCARShared.onDestroy();
mUnityPlayer.quit();
super.onDestroy();
}

But then I call back to my other Activity through Intent (startActivity(new Intent(GetApplicationContext(), MainActivity.class))) in my QCARActivity class, which is the wrong way to do.

The onDestroy() which supposedly should have destroyed the QCARActivity did not get called. So the right way is to call finish() to return it to the previous activity, which will call the onDestroy() as well. By doing this, we can go back to QCARActivity class and it is still able to initialized the QCAR.

However, when we call finish() , mUnityPlayer.quit() gets called. Then suddenly a weird behavior happens. Your apps quits right away even if your intention was to go back to the previous Activity.
This is because inside UnityPlayer.quit() method, it called:

Process.killProcess(Process.myPid()); //This code will kill the process that the app is running.

This can be solved by making the activity that has Unity/Vuforia act as a single process by adding android:process to the manifest, thus killing the Activity instead of the whole application. (e.g. android:process="myProcessName").

Resources:
Errors managing the UnityPlayer lifecycle in a native android application

Hope this helps anyone out there.

Integrate Unity3d view into Android activity

While I still don't know why it works like that, I've found a way of fixing it.

Instead of simply using setContentView() in onCreate(), extend onResume() and in that method recursively look through all the available views to find the parent view of the UnityPlayer object. Once that's found, layouts and other views can be inflated and added to that parent view.

Here's the link with a code example - I've used this to make my app work: https://developer.vuforia.com/resources/dev-guide/extending-unity-android-activity-and-adding-custom-views-eclipse

Edit: Here's a code snippet showing my solution.

@Override
public void onResume() {

super.onResume();

if (unityPlayer == null) {

View rootView = findViewById(android.R.id.content);
unityPlayer = findUnityPlayerView(rootView);

if (unityPlayer != null) {

ViewGroup unityPlayerParentView = (ViewGroup)(unityPlayer.getParent());

View mainHomeView = getLayoutInflater().inflate(R.layout.activity_main, null);
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
unityPlayerParentView.addView(mainHomeView, layoutParams);

}
}
}

and

private UnityPlayer findUnityPlayerView(View view) {

if (view instanceof UnityPlayer) {

return (UnityPlayer) view;
}
if (view instanceof ViewGroup) {

ViewGroup childrenViews = (ViewGroup) view;

for (int i = 0; i < childrenViews.getChildCount(); i++) {

UnityPlayer foundView = findUnityPlayerView(childrenViews.getChildAt(i));

if (foundView != null) {

return foundView;
}
}
}

return null;
}

Invoking scene when loading Unity as subview in Android

If you plan to load Unity activity from Android, you should create an empty scene. Call this this Main Menu, then make it the default scene that loads through the Build Settings. Make it index 0.

The goal of this empty scene is to load specific scene when script attached to it is called.

In this Main Menu scene, create a GameObject called SceneLoader then attach the script below to it:

public class SceneLoader : MonoBehaviour
{
public void loadScene(string sceneIndex)
{
UnityEngine.SceneManagement.SceneManager.LoadScene(Convert.ToInt16(sceneIndex));
}
}

You should also create a GameObject called SceneLoader in every other scene and attach the script above to all of them.

Now, load Unity Activity which automatically loads the default/empty scene. Since there is only one GameObject/SceneLoader in it, it will load very fast.

You can now load other scenes from Java with:

UnityPlayer.UnitySendMessage("SceneLoader", "loadScene", "9");

I want to create a native Android and iOS app including Unity AR

I guess you can find information here :

For Android you have to follow 3 steps :

  • Build your Android project (select Android platform and enable
    Google Android project)
  • Import your built project to Android Studio
  • Do what you want inside Android Studio and build

For iOS I guess it's quite the same process using views but never went into it.

Native NullPointerException when initializing Firebase in Unity app

So there were two problems here that needed to be fixed.
Firstly, my .aar with extended UnityActivity was linking an older version of libmessaging_unity_player_activity.jar. I'm not sure that had much impact since the main unity build was including the right one but its worth mentioning.
This didn't fix the issue however. Only after reverting the manifest to its "minimal" state I figured out that the tools:node="replace" line in the application tag was causing this odd exception. I don't understand why this caused the crash but removing it fixed it.



Related Topics



Leave a reply



Submit