2

在 Android“N”中,Doze 已扩展为“ Doze on the Go ”。

我正在寻找一种方法来检测设备何时进入和离开这些新的轻瞌睡 IDLE 和 IDLE_MAINTENANCE 状态。(基本上与这里的常规打盹提出的问题相同。)

4

2 回答 2

4

PowerManager的在线文档没有提到它,但最新的源代码(API 24 修订版 1)看起来应该是这个问题的解决方案:

String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED
        = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED"
boolean isLightDeviceIdleMode()

理论上,您可以简单地将一些代码注册为意图的接收者并检查函数的当前值。一些探索dumpsys activity broadcasts表明,当 light doze 状态发生变化时,确实发送了意图。

但是,最新的 SDK 平台(API 24 修订版 2)中没有这些符号 - 我得到编译错误(并且有些戳javapjar表明它们确实不存在)。联系谷歌,我们被告知这是预期的设计。

有一种解决方法,即硬编码上面提到的相同字符串,然后使用反射来调用在 API 中调用的相同函数。像这样:

/**
 * Check if the device is currently in the Light IDLE mode.
 *
 * @param context The application context.
 * @return True if the device is in the Light IDLE mode.
 */
public static boolean isLightDeviceIdleMode(final Context context) {
    boolean result = false;
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    if (pm != null) {
        // result = pm.isLightDeviceIdleMode();
        try {
            Log.d(TAG, "Trying reflection for isLightDeviceIdleMode");
            Field pmServiceField = pm.getClass().getDeclaredField("mService");
            pmServiceField.setAccessible(true);
            Object pmService = pmServiceField.get(pm);

            Method isLightDeviceIdleMode = pmService.getClass().getDeclaredMethod("isLightDeviceIdleMode");
            isLightDeviceIdleMode.setAccessible(true);
            result = (Boolean) isLightDeviceIdleMode.invoke(pmService);
        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            Log.e(TAG, "Reflection failed for isLightDeviceIdleMode: " + e.toString());
        } catch (RemoteException re) {
            Log.e(TAG, "Remote exception checking isLightDeviceIdleMode: " + e.toString());
        }
    }
    return result;
}
于 2016-09-15T18:49:47.900 回答
3

The implementation in TrevorWiley's answer works, but can be simplified a bit. Yes, Nougat's PowerManager has isLightDeviceIdleMode() and it is annotated with @hide. We can use reflection to invoke it, which is more succinct and independent of PowerManager's internal implementation details.

public static boolean isLightDeviceIdleMode(final Context context) {
    boolean result = false;
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    if (pm != null) {
        try {
            Method isLightDeviceIdleModeMethod = pm.getClass().getDeclaredMethod("isLightDeviceIdleMode");
            result = (boolean)isLightDeviceIdleModeMethod.invoke(pm);
        } catch (IllegalAccessException | InvocationTargetException  | NoSuchMethodException e) {
            Log.e(TAG, "Reflection failed for isLightDeviceIdleMode: " + e.toString(), e);
        }
    }
    return result;
}

Agreed mostly with TrevorWiley's use of the String to register for Broadcasts. Same as the above method, you could use reflection to grab the value of field ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED and fall back to the hard-coded String "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED".

于 2017-06-16T20:58:38.940 回答