【问题标题】:Unity3D Android Lockscreen not shown when screen times out. (Wakelock?)屏幕超时时未显示 Unity3D Android 锁屏。 (唤醒锁?)
【发布时间】:2013-11-07 21:14:07
【问题描述】:

我正在使用 Unity 构建一个 Android 应用程序,并且进展顺利。但是,我遇到了一个与屏幕超时和锁屏不显示有关的奇怪问题。

应该发生什么

  • 用户停止播放
  • 屏幕超时并关闭
  • 稍后,玩家返回并重新打开手机
  • 锁屏显示,用户可以输入密码或解锁手机
  • 应用重新获得焦点并继续

发生了什么

  • 用户停止播放
  • 屏幕超时并关闭
  • 稍后,玩家返回并重新打开手机
  • 锁屏显示!该应用程序是正确的焦点,一起绕过锁屏
  • 用户会因为他们的安全受到威胁而生气:(

备注

  • 无论我是否使用 Android 插件,都会发生这种情况
  • 我使用的是 Unity 4.2.0f4(尽管最新版本的更新日志中没有关于此问题的内容)
  • 它甚至发生在一个空白的 Android 项目中
  • 我已经在 5 种不同的设备上对此进行了测试,都存在相同的问题

我怀疑这是由 Unity 在屏幕超时时不会放弃的唤醒锁引起的。这会导致应用程序保持焦点,并且锁屏永远不会“加载”。这是一个相当严重的问题。

有没有人知道解决这个问题的方法?

注意:我已经在一个多星期前问过this question on Unity Answers,但还没有得到任何回复。我想也许我在这里会有更好的运气。

【问题讨论】:

  • 我已验证我的 nexus 10 也具有完全相同的行为

标签: android unity3d lockscreen wakelock


【解决方案1】:

首先我要感谢MichaelTaylor3D's Answer 为我指明了正确的方向。

他解决问题的方式存在一些问题,我认为我已经纠正了:

第一个问题是KeyguardManager 锁定屏幕的方法。这在 API 版本 8 中已被贬值,并且不适用于 API 9+。新的解决方案使用了 Device Admin API,这对游戏来说似乎非常具有侵入性。

我查看了 eclipse 中的 UnityPlayer 定义,发现了一个名为 setWakeLock(boolean) 的函数,但是它是私有的。

我制作了一个自定义的 android 活动。在其中,我访问了一个受保护的 UnityPlayer 函数 setWakeLock(boolean) 并在 onPause 函数中调用它。

我承认这不太理想,但它似乎确实没有副作用。我已向 Unity 提交了一份错误报告,因此希望这项工作不需要很长时间。

public class UnityPlayerWithLockscreen extends UnityPlayerNativeActivity {


    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
         super.onCreate(savedInstanceState);
    }

    //Provides access to the private UnityPlayer.setWakeLock(boolean) method.
    public void setWakeLock(boolean useWakelock) {
        Method setWakeLockMethod;

        //Use Reflection to get the setWakeLock(boolean) method from the UnityPlayer class
        setWakeLockMethod = mUnityPlayer.getClass().getDeclaredMethod("setWakeLock");

        //Set the method to me accessible
        setWakeLockMethod.setAccessible(true);

        //Invoke the method with our argument
        setWakeLockMethod.invoke(mUnityPlayer, useWakelock);
    }


    @Override
    protected void onPause()
    {
        super.onPause();

        //Force unity to release the wakelock
        setWakeLock(false);     
    }
}

那么你需要在AndroidManifest文件中设置这个activity为main activity:

<?xml version="1.0" encoding="utf-8"?>
<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".UnityPlayerWithLockscreen"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

此解决方案也不需要任何 C# 脚本或 C# Java 互操作。

再一次,我知道这是一种 hacky 解决方案,但它似乎可以在多个设备上运行,无论 API 级别如何,并且没有副作用。希望 Unity 能尽快解决这个问题,并且不再需要丑陋的修复。

【讨论】:

  • 你有没有提到你报告的统一错误?
【解决方案2】:

我没有任何 Unity 经验,但请尝试使用 http://developer.android.com/reference/android/os/PowerManager.WakeLock.html

查看统一是否通过 isHeld() 以某种方式强制唤醒锁;并尝试在您的活动中覆盖它

编辑:我在下载时使用了唤醒锁,这会使屏幕变黑但不会锁定设备,所以可能是这种情况,所以尝试释放它,这可能会产生某种冲突,但总是值得一试。

【讨论】:

    【解决方案3】:

    这是未经测试的,但你可以试试这个,

    基本上你想尝试直接访问 Android SDK,Unity 有一些类允许你这样做。

    尝试编写一个外部 Java 类并将其放在 Plugins/Android 文件夹中。

    Java 类方法可能是这样的:referenced from this question 确保它的compiled into a .jar file

    package com.yourcompany.yourgamename;
    
    import com.unity3d.player.UnityPlayerActivity;
    
    import android.content.Context;
    import android.os.Bundle;
    import android.util.Config;
    import android.util.Log;
    import android.app.Activity;   
    
    public class MainActivity extends UnityPlayerActivity 
    {    
        @Override
        public void onCreate(Bundle savedInstanceState) 
        {
             super.onCreate(savedInstanceState);
        }
    
        public static void lockScreen()
        {
            KeyguardManager keyguardManager = (KeyguardManager)getSystemService(Activity.KEYGUARD_SERVICE); 
            KeyguardLock lock = keyguardManager.newKeyguardLock(KEYGUARD_SERVICE);  
            lock.reenableKeyguard();  
        }
    }
    

    还要确保它在 Android Manifest 文件中,以便 Unity 加载此活动而不是主活动并将其放在 \Plugins\Android 文件夹中:

    <?xml version="1.0" encoding="utf-8"?>
    

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".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>
    </application>
    

    然后创建一个调用外部 Java 类的 Unity C# 脚本:referenced here

    public static void InvokeAndroidLockScreen()
    {
        #if UNITY_ANDROID
        using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
             using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
             {
                  obj_Activity .CallStatic("lockScreen");
             }
        }
        #else
        Debug.LogWarning("Cant invoke lockscreen on a non Android Device");
        #endif
    
    }
    

    最后在一个活跃的monobehavior中调用应用程序暂停事件上的锁屏:referenced here

    void OnApplicationPause(bool pauseStatus)
    {
         InvokeAndroidLockScreen();
    }
    

    您必须确保将以下标志设置为 false

    Application.runInBackground = false;
    

    我想,否则当设备关闭时暂停事件不会被触发。 (默认关闭)

    即使这是一个变通方法,我仍然认为这是一个 Unity Bug,应该是reported to them.

    【讨论】:

    • 感谢您的建议。下班后我会试一试。我同意这是一个统一错误,我会提交报告。
    • 谢谢!这个答案对解决我的问题非常有帮助。所以我给你赏金。但是我最终确实以不同的方式进行了操作,因此我将在最终解决方案中发布另一个答案。如果可以接受,我会的。
    猜你喜欢
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    • 1970-01-01
    相关资源
    最近更新 更多