Unity3D在Android上屏幕超时后无法显示锁屏界面。(唤醒锁?)

5
我正在使用Unity构建一个安卓应用程序,进展非常顺利。然而,我遇到了一个关于屏幕超时和锁屏不显示的奇怪问题。 预期发生的情况
  • 用户停止玩游戏
  • 屏幕自动超时并关闭
  • 稍后,玩家返回并打开手机
  • 锁屏显示,用户可以输入密码或以其他方式解锁手机
  • 应用恢复焦点并继续
实际发生的情况
  • 用户停止玩游戏
  • 屏幕自动超时并关闭
  • 稍后,玩家返回并打开手机
  • 锁屏没有显示!应用程序直接获得焦点,完全绕过了锁屏
  • 用户感到他们的安全受到了威胁 :(
  • 无论我是否使用Android插件,都会出现这种情况
  • 我使用的是Unity 4.2.0f4(尽管更近版本的变更日志中没有关于此问题的描述)
  • 即使在空白的Android项目中也会发生这种情况
  • 我已经在5个不同的设备上测试了,所有设备都有同样的问题

我怀疑这是由于Unity在屏幕超时时不放弃唤醒锁所导致的。这会导致应用保持焦点,而锁屏永远无法‘加载’。这是一个相当严重的问题。

有人知道如何解决这个问题吗?

注意: 我已经在Unity Answers上提过这个问题,但已经一个多星期没有得到任何回复。我想在这里可能会更好一些。


1
我已经验证了我的Nexus 10也有完全相同的行为。 - MichaelTaylor3D
3个回答

2

首先,我要感谢MichaelTaylor3D的答案指导我正确解决问题。

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

第一个问题是使用KeyguardManager锁定屏幕的方法。这种方法在API版本8中已经被弃用,在API 9+上不起作用。新的解决方案使用设备管理器API,但对于游戏来说似乎过于侵入性。

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

我创建了一个自定义的Android Activity。在其中,我访问了一个受保护的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文件中将此活动设置为主要活动:

<?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很快就能解决这个问题,并且不再需要一个丑陋的补丁。


你有报告的Unity Bug的参考吗? - gollumullog

0

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

基本上,你想要尝试直接访问Android SDK,Unity有一些类可以让你做到这一点。

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

Java类的方法可能类似于这样: 参考此问题 确保它 编译成 .jar 文件

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>

然后创建一个Unity C#脚本,调用外部的Java类:在这里引用
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中,在应用程序暂停事件上调用锁屏:在这里引用

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

您需要确保以下标志设置为false

Application.runInBackground = false;

我认为否则当设备关闭时暂停事件将不会被触发。(默认情况下关闭)

即使这是一个解决方法,我仍然认为这是一个Unity Bug,应该向他们报告


谢谢你的建议。下班后我会试一试。我同意这是一个Unity的bug,我会提交报告。 - Khalos
谢谢!这个答案对解决我的问题非常有帮助。所以我会授予你奖金。然而,我最终采用了另一种方法,所以我将发布另一个答案,附上我的最终解决方案。如果可以接受两个答案,那就太好了。 - Khalos

0

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

可以使用isHeld()查看Unity是否以某种方式强制wakelock,并尝试在activity中覆盖它。

编辑:当下载时我使用wakelock,这会使屏幕变黑,但不会锁定设备,所以这可能是问题所在,试着释放它,这可能会产生某种冲突,但值得一试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接