在原生 Android 应用程序中管理 UnityPlayer 生命周期时出错 [英] Errors managing the UnityPlayer lifecycle in a native android application

查看:16
本文介绍了在原生 Android 应用程序中管理 UnityPlayer 生命周期时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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-Uni​​ty-Android-In-a-Sub-View .

最初,应用程序在名为UnityActivity.java"的活动中正确显示了 UnityPlayer.

Initially the application is correctly displaying the UnityPlayer inside an activity called "UnityActivity.java".

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

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.

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

This is logged when navigating to the UnityActivity for the first time:

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

单击后退按钮时会记录此错误:

This error is logged when clicking the back button:

W/Choreographer(20963):已经有一个待处理的 vsync 事件.一次应该只有一个.

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

This error is logged when navigating to the UnityActivity for the second time:

A/libc(21095):0x00000000 处的致命信号 11(SIGSEGV)(代码=1),线程 21176(线程 5073)

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

...at which point I get kicked out of the application.

这是主要活动MainActivity.java的摘录:

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

这是 Unity 活动的摘录 UnityActivity.java :

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

这是在清单 ../AndroidManifest.xml 中描述活动的方式:

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>

UnityActivity的布局是这样定义的../res/layout/activity_unity.xml:

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.

推荐答案

好的,先做简单的事情

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

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

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.

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

The guide you linked is pretty outdated. You no longer need to copy files from various locations to create a simple Android project.

  1. 通过设置Build Settings -> 创建一个Android项目安卓->Google Android 项目
  2. 您现在有一个完整的包可以导入到 Eclipse 或 Android Studio 中
  3. 编译和部署

在子活动中使用 UnityPlayer

新 Android 项目中的 UnityPlayerNativeActivity 类向您展示了如何设置 UnityPlayer 以及您需要转发哪些事件.这是Unity 4.3.4使用的版本

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);
    }
}

尽管 UnityPlayerNativeActivity 扩展了 NativeActivity,但据我所知,您仍然可以从 ActionBarActivity 扩展而不会出现任何问题.至少在我的实验中它是有效的.

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.

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

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.

修复了您可能会惊讶于现在当您从 UnityActivity 返回时整个应用程序只是关闭的问题.mUnityPlayer.quit() 将终止它正在运行的进程.调用mUnityPlayer.quit()后没有一个方法会执行,甚至onDestroy()方法也不会执行.

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.

通往胜利的道路是通过添加参数 android:process=":UnityKillsMe 到您在 AndroidManifest.xml 中的活动.

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>

我不确定参数 unityplayer.ForwardNativeEventsToDalvik... 一开始创建的项目将其设置为 false官方(过时)文档提及

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

由于触摸/运动事件是在本机代码中处理的,Java 视图通常不会看到这些事件.但是,Unity 中有一种转发机制,允许将事件传播到 DalvikVM.

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

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

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.

根据上述文档,您可以集成您编译的 Android类和 AndroidManifest.xml 作为 Unity 的插件.

According to the aforementioned documentation you may be able to integrate your compiled Android classes and AndroidManifest.xml as plugins into Unity.

生成的 .class 文件应该被压缩成一个 .jar 文件并放置在 Assets->Plugins->Android 文件夹中.由于清单规定了要启动的活动,因此还需要创建一个新的 AndroidManifest.xml.AndroidManifest.xml 文件也应该放在 Assets->Plugins->Android 文件夹中.

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 应用程序中管理 UnityPlayer 生命周期时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆