android - 在原生 android 应用程序中管理 UnityPlayer 生命周期的错误

标签 android android-activity unity3d

我正在开发一个需要在 Activity 中加载 UnityPlayer 实例的 android 应用,使用以下论坛帖子中的代码作为指南:

http://forum.unity3d.com/threads/98315-Using-Unity-Android-In-a-Sub-View .

最初,应用程序在名为“UnityActivity.java”的 Activity 中正确显示 UnityPlayer。

当用户导航回 MainActivity(通过按下硬件后退按钮或单击 ActionBar 后退按钮)然后尝试重新打开 UnityActivity 时,问题开始 - 在这种情况下,将显示黑屏而不是统一播放器。论坛中的一位用户建议将 onPause 和 onResume 生命周期事件转发到 UnityPlayer,如下面的代码所示。但是,这样做时会出现以下错误并且应用程序崩溃。

这是第一次导航到 UnityActivity 时记录的:

W/libc(21095):pthread_create sched_setscheduler 调用失败:不允许操作

单击返回按钮时会记录此错误:

W/Choreographer(20963):已经有一个未决的垂直同步事件。一次应该只有一个。

第二次导航到 UnityActivity 时会记录此错误:

A/libc(21095):致命信号 11 (SIGSEGV) 位于 0x00000000 (code=1),线程 21176 (Thread-5073)

...此时我被踢出应用程序。

代码

这是主要 Activity MainActivity.java的摘录:

public void startUnityActivity(View view) {
        Intent intent = new Intent(this, UnityActivity.class);
    startActivity(intent);
}

这是 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();
    }

这是 list ../AndroidManifest.xml 中描述 Activity 的方式:

<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>

UnityActivity的布局是这样定义的../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>

如果您有任何提示和解决方案为我指明正确的方向,我将不胜感激。

最佳答案

好的,先简单的事情

W/libc(21095): pthread_create sched_setscheduler call failed: Operation not permitted

对此你无能为力。当你直接从 Unity for Android 编译时,你甚至会得到这个,所以这是引擎内部的问题。

基本设置

您链接的指南已经过时了。您不再需要从不同位置复制文件来创建简单的 Android 项目。

  1. 通过设置Build Settings -> Android -> Google Android project
  2. 创建一个Android项目
  3. 您现在已经准备好将完整的包导入 Eclipse 或 Android Studio
  4. 编译和部署

在子 Activity 中使用 UnityPlayer

您的新 Android 项目中的 UnityPlayerNativeActivity 类向您展示了如何设置 UnityPlayer 以及您需要转发哪些事件。这是 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);
    }
}

虽然 UnityPlayerNativeActivity 扩展了 NativeActivity 您仍然可以从 ActionBarActivity 扩展,据我所知没有任何问题。至少在我的实验中它是有效的。

您缺少的最重要的部分是在 onDestroy() 期间对 mUnityPlayer.quit() 的调用。尝试在旧实例仍在运行时创建 UnityPlayer 的新实例将导致崩溃、挂起 Activity 和无尽的痛苦。

mUnityPlayer.quit() 的意外行为

修复了当您从 UnityActivity 返回时,您的整个 App 会简单地关闭的问题,您可能会感到惊讶。 mUnityPlayer.quit() 会杀死它在里面运行的进程。调用 mUnityPlayer.quit() 后没有一个方法会执行,甚至 onDestroy() 方法也不会完成。

通往胜利的道路是通过添加参数android:process=":UnityKillsMe 将您的UnityActivity 作为一个新进程启动。到您的 AndroidManifest.xml 中的 Activity 。

在你的情况下,它看起来像这样

<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>

我不确定参数 unityplayer.ForwardNativeEventsToDalvik... 一开始创建的项目将其设置为 falseofficial (outdated) documentation提到

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.

在我的小示例项目中,我看不出有什么不同

前面的路

您需要找到一个工作流程,将您的 Unity 开发与 Android 项目集成起来,反之亦然。使用 Unity 再次导出会与您在 Android 项目中所做的更改发生冲突,因此您需要导出到单独的文件夹并链接到 Android 项目中的 Unity 部分。

根据前述documentation您也许可以将您编译的 Android 类和 AndroidManifest.xml 作为插件集成到 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.

祝你好运!

关于android - 在原生 android 应用程序中管理 UnityPlayer 生命周期的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23467994/

相关文章:

c# - unity navmesh ai卡住了

java - 二维球碰撞问题 : no conservation of energy

android - 列出来自 Android 手机的 HTTP 请求

android 布局权重 %

android - 显示在警报 Activity 弹出窗口顶部的应用程序名称

Android:如何从 Activity Activity 中关闭前台 Activity ?

unity3d - #pragma kernal Main上的语法错误

android - apk 文件的加密会阻止黑客对其进行反编译吗?

android - 从 PreferenceActivity 或 PreferenceFragment 中的资源添加特定命名的 SharedPreferences

c# - Hololens v2 与 Android 智能手机之间的通信