android app設置亮屏的幾種方式以及功耗對比

在Android app開發中,爲了讓某個頁面持續顯示一定時間,需要設置亮屏代碼。

常用的方法有四種,分別如下:

方法1:通過PowerManager獲取wakelock。

方法2:在view中設置view.setKeepScreenOn(true);

方法3:設置WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 和 setTurnScreenOn(true);

方法4:在xml中設置android:keepScreenOn="true"

下面說一下上面四種方法的對比:

方法1一般用在非activity、fragment,無法操作UI界面的服務,如service、receiver。

需要申請權限

  1. <uses-permission android:name="android.permission.WAKE_LOCK" />  
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wakeLock = pm.newWakeLock((PowerManager.SCREEN_BRIGHT_WAKE_LOCK
                | PowerManager.ACQUIRE_CAUSES_WAKEUP), "reminder:");
        //wakeLock.setReferenceCounted(false);
        wakeLock.acquire(6000L);

wakelock.acquire()不帶參數的不建議使用,建議使用帶參數的,帶參數的系統會自動release,是比較安全的,即便是手動release後被系統handler自動釋放,也不會出現crash。因爲PowerManager有兩個計數值,mInternalCount和mExternalCount,系統自動釋放時不會操作mExternalCount。

 public void release(int flags) {
            synchronized (mToken) {
                if (mInternalCount > 0) {
                    // internal count must only be decreased if it is > 0 or state of
                    // the WakeLock object is broken.
                    mInternalCount--;
                }
                if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
                    mExternalCount--;
                }
                if (!mRefCounted || mInternalCount == 0) {
                    mHandler.removeCallbacks(mReleaser);
                    if (mHeld) {
                        Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                        try {
                            mService.releaseWakeLock(mToken, flags);
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                        mHeld = false;
                    }
                }
                if (mRefCounted && mExternalCount < 0) {
                    throw new RuntimeException("WakeLock under-locked " + mTag);
                }
            }
        }

但如果應用層手動執行release比acquire要多,且沒有設置mRefCounted爲true,系統就會拋異常。

其中wakeLock.setReferenceCounted(false);的作用是無論調用多少次acquire,只要最後執行了一次release就會釋放wakelock,該值默認爲true;

WakeLock獲取時相關的flag如下所示:

    PARTIAL_WAKE_LOCK :保持CPU 運轉,屏幕和鍵盤燈有可能是關閉的。
    SCREEN_DIM_WAKE_LOCK :保持CPU 運轉,允許保持屏幕顯示但有可能是灰的,允許關閉鍵盤燈
    SCREEN_BRIGHT_WAKE_LOCK :保持CPU 運轉,允許保持屏幕高亮顯示,允許關閉鍵盤燈
    FULL_WAKE_LOCK :保持CPU 運轉,保持屏幕高亮顯示,鍵盤燈也保持亮度
————————————————
版權聲明:本文爲CSDN博主「llljjlj」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/llljjlj/article/details/80631664

在含有UI界面的activity、fragment中不推薦使用pm wakelock,推薦使用方法2和3。

@RequiresApi(api = Build.VERSION_CODES.O_MR1)
    private void clearKeepScreenOnFlag() {
        Activity activity = getActivity();
        if (activity != null) {
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            activity.setTurnScreenOn(false);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.O_MR1)
    private void setKeepScreenOnFlag() {
        Activity activity = getActivity();
        if (activity != null) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            activity.setTurnScreenOn(true);
        }
    }
或者
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        fragmentLayoutRoot=inflater.inflate(R.layout.fragment_dobreath_prepare, container, false);
        fragmentLayoutRoot.setKeepScreenOn(true);
        mHandler.postDelayed(mCleanKeepScreenTask,120*1000);
        return fragmentLayoutRoot;
    }

 下面分析三種方式的功耗分析:

執行方法1.查看dumpsys power信息。

 private void wakeLockPm(Context context) {
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wakeLock = pm.newWakeLock((PowerManager.SCREEN_BRIGHT_WAKE_LOCK
                | PowerManager.ACQUIRE_CAUSES_WAKEUP), "reminder:");
        //wakeLock.setReferenceCounted(false);
        wakeLock.acquire(6000L);
    }
$ adb shell dumpsys power | grep -i wake
    no_cached_wake_locks=true
  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x23
  mLastWakeTime=4321190 (82051 ms ago)
  mHoldingWakeLockSuspendBlocker=true
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDrawWakeLockOverrideFromSidekick=false
  mDoubleTapWakeEnabled=false
Wake Locks: size=1
  SCREEN_BRIGHT_WAKE_LOCK        'reminder:' ACQUIRE_CAUSES_WAKEUP ACQ=-1s927ms (uid=10054 pid=8081)
  PowerManagerService.WakeLocks: ref count=1
  mGravitySensor={Sensor name="gravity  Non-wakeup", vendor="qualcomm", version=1, type=9, maxRange=1.0, resolution=0.1, power=0.515, minDelay=20000}

lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i screen
  mLastScreenBrightnessBoostTime=0 (4406128 ms ago)
  mScreenBrightnessBoostInProgress=false
  mSuspendWhenScreenOffDueToProximityConfig=true
  mDozeAfterScreenOff=false
  mMinimumScreenOffTimeoutConfig=5000
  mMaximumScreenDimDurationConfig=1000
  mMaximumScreenDimRatioConfig=0.20000005
  mScreenOffTimeoutSetting=30000
  mMaximumScreenOffTimeoutFromDeviceAdmin=9223372036854775807 (enforced=false)
  mScreenBrightnessSetting=0
  mScreenBrightnessModeSetting=1
  mScreenBrightnessOverrideFromWindowManager=-1
  mDozeScreenStateOverrideFromDreamManager=0
  mDozeScreenBrightnessOverrideFromDreamManager=-1
  mScreenBrightnessSettingMinimum=10
  mScreenBrightnessSettingMaximum=254
  mScreenBrightnessSettingDefault=208
Screen off timeout: 30000 ms
Screen dim duration: 1000 ms
  SCREEN_BRIGHT_WAKE_LOCK        'reminder:' ACQUIRE_CAUSES_WAKEUP ACQ=-4s813ms (uid=10054 pid=8081)


上面設置的是wakelock 6秒後自動釋放,釋放後再看信息

lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i wake
    no_cached_wake_locks=true
  mWakefulness=Asleep
  mWakefulnessChanging=false
  mWakeLockSummary=0x0
  mLastWakeTime=4321190 (177007 ms ago)
  mHoldingWakeLockSuspendBlocker=false
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDrawWakeLockOverrideFromSidekick=false
  mDoubleTapWakeEnabled=false
Wake Locks: size=0
  PowerManagerService.WakeLocks: ref count=0
  mGravitySensor={Sensor name="gravity  Non-wakeup", vendor="qualcomm", version=1, type=9, maxRange=1.0, resolution=0.1, power=0.515, minDelay=20000}

lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i screen
  mLastScreenBrightnessBoostTime=0 (4501875 ms ago)
  mScreenBrightnessBoostInProgress=false
  mSuspendWhenScreenOffDueToProximityConfig=true
  mDozeAfterScreenOff=false
  mMinimumScreenOffTimeoutConfig=5000
  mMaximumScreenDimDurationConfig=1000
  mMaximumScreenDimRatioConfig=0.20000005
  mScreenOffTimeoutSetting=30000
  mMaximumScreenOffTimeoutFromDeviceAdmin=9223372036854775807 (enforced=false)
  mScreenBrightnessSetting=0
  mScreenBrightnessModeSetting=1
  mScreenBrightnessOverrideFromWindowManager=-1
  mDozeScreenStateOverrideFromDreamManager=0
  mDozeScreenBrightnessOverrideFromDreamManager=-1
  mScreenBrightnessSettingMinimum=10
  mScreenBrightnessSettingMaximum=254
  mScreenBrightnessSettingDefault=208
Screen off timeout: 5000 ms
Screen dim duration: 1000 ms

使用wakelock可以加tag,方便功耗分析定位問題。

使用該方法的風險在於,必須有正確配對的release方式纔不會功耗異常,若沒有地方釋放就會一直亮屏耗電。如果是在activity中使用,即便是activity 頁面pause了,也不會自動release,所以應該儘量避免使用walock的方式。

                fragmentLayout.setKeepScreenOn(true);
lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i screen
  mLastScreenBrightnessBoostTime=0 (5352101 ms ago)
  mScreenBrightnessBoostInProgress=false
  mSuspendWhenScreenOffDueToProximityConfig=true
  mDozeAfterScreenOff=false
  mMinimumScreenOffTimeoutConfig=5000
  mMaximumScreenDimDurationConfig=1000
  mMaximumScreenDimRatioConfig=0.20000005
  mScreenOffTimeoutSetting=30000
  mMaximumScreenOffTimeoutFromDeviceAdmin=9223372036854775807 (enforced=false)
  mScreenBrightnessSetting=0
  mScreenBrightnessModeSetting=1
  mScreenBrightnessOverrideFromWindowManager=-1
  mDozeScreenStateOverrideFromDreamManager=0
  mDozeScreenBrightnessOverrideFromDreamManager=-1
  mScreenBrightnessSettingMinimum=10
  mScreenBrightnessSettingMaximum=254
  mScreenBrightnessSettingDefault=208
Screen off timeout: 30000 ms
Screen dim duration: 1000 ms
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-23s285ms (uid=1000 pid=1400 ws=WorkSource{10054})

lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i wake
    no_cached_wake_locks=true
  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x23
  mLastWakeTime=5285832 (69745 ms ago)
  mHoldingWakeLockSuspendBlocker=true
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDrawWakeLockOverrideFromSidekick=false
  mDoubleTapWakeEnabled=false
Wake Locks: size=1
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-26s761ms (uid=1000 pid=1400 ws=WorkSource{10054})
  PowerManagerService.WakeLocks: ref count=1
  mGravitySensor={Sensor name="gravity  Non-wakeup", vendor="qualcomm", version=1, type=9, maxRange=1.0, resolution=0.1, power=0.515, minDelay=20000}

執行view.setkeepScreenon(true)不受擡腕影響。view pause或destroy時,會自動釋放SCREEN_BRIGHT_WAKE_LOCK,推薦使用這種方式。

方法3:

在activity中執行
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setTurnScreenOn(true);
$ adb shell dumpsys power | grep -i wake
    no_cached_wake_locks=true
  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x23
  mLastWakeTime=5636910 (28381 ms ago)
  mHoldingWakeLockSuspendBlocker=true
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDrawWakeLockOverrideFromSidekick=false
  mDoubleTapWakeEnabled=false
Wake Locks: size=1
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-22s316ms (uid=1000 pid=1400 ws=WorkSource{10054})
  PowerManagerService.WakeLocks: ref count=1
  mGravitySensor={Sensor name="gravity  Non-wakeup", vendor="qualcomm", version=1, type=9, maxRange=1.0, resolution=0.1, power=0.515, minDelay=20000}

lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i screen
  mLastScreenBrightnessBoostTime=0 (5670577 ms ago)
  mScreenBrightnessBoostInProgress=false
  mSuspendWhenScreenOffDueToProximityConfig=true
  mDozeAfterScreenOff=false
  mMinimumScreenOffTimeoutConfig=5000
  mMaximumScreenDimDurationConfig=1000
  mMaximumScreenDimRatioConfig=0.20000005
  mScreenOffTimeoutSetting=30000
  mMaximumScreenOffTimeoutFromDeviceAdmin=9223372036854775807 (enforced=false)
  mScreenBrightnessSetting=0
  mScreenBrightnessModeSetting=1
  mScreenBrightnessOverrideFromWindowManager=-1
  mDozeScreenStateOverrideFromDreamManager=0
  mDozeScreenBrightnessOverrideFromDreamManager=-1
  mScreenBrightnessSettingMinimum=10
  mScreenBrightnessSettingMaximum=254
  mScreenBrightnessSettingDefault=208
Screen off timeout: 30000 ms
Screen dim duration: 1000 ms
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-27s602ms (uid=1000 pid=1400 ws=WorkSource{10054})

效果與方法2相同,activity pause後自動釋放,再次resume自動screen_bright_wake_lock。

爲了避免用戶誤操作到該頁面長時間亮屏耗電,建議加入超時釋放邏輯。

方法4:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:keepScreenOn="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</android.support.constraint.ConstraintLayout>
lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i wake
    no_cached_wake_locks=true
  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x23
  mLastWakeTime=5733001 (174149 ms ago)
  mHoldingWakeLockSuspendBlocker=true
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDrawWakeLockOverrideFromSidekick=false
  mDoubleTapWakeEnabled=false
Wake Locks: size=1
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-8s160ms (uid=1000 pid=1400 ws=WorkSource{10054})
  PowerManagerService.WakeLocks: ref count=1
  mGravitySensor={Sensor name="gravity  Non-wakeup", vendor="qualcomm", version=1, type=9, maxRange=1.0, resolution=0.1, power=0.515, minDelay=20000}
lingx@MI-20170703YUWL MINGW64 /e/watch/Apps/fitness (master)
$ adb shell dumpsys power | grep -i screen
  mLastScreenBrightnessBoostTime=0 (5909168 ms ago)
  mScreenBrightnessBoostInProgress=false
  mSuspendWhenScreenOffDueToProximityConfig=true
  mDozeAfterScreenOff=false
  mMinimumScreenOffTimeoutConfig=5000
  mMaximumScreenDimDurationConfig=1000
  mMaximumScreenDimRatioConfig=0.20000005
  mScreenOffTimeoutSetting=30000
  mMaximumScreenOffTimeoutFromDeviceAdmin=9223372036854775807 (enforced=false)
  mScreenBrightnessSetting=0
  mScreenBrightnessModeSetting=1
  mScreenBrightnessOverrideFromWindowManager=-1
  mDozeScreenStateOverrideFromDreamManager=0
  mDozeScreenBrightnessOverrideFromDreamManager=-1
  mScreenBrightnessSettingMinimum=10
  mScreenBrightnessSettingMaximum=254
  mScreenBrightnessSettingDefault=208
Screen off timeout: 30000 ms
Screen dim duration: 1000 ms
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-10s177ms (uid=1000 pid=1400 ws=WorkSource{10054})

從現象看與方法2、3效果相同。但實際使用有很大區別,因爲在xml中設置了keepScreenOn=true,在代碼中執行getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

setTurnScreenOn(false);

後發現SCREEN_BRIGHT_WAKE_LOCK並未釋放。因此只有當確定本頁面顯示時不會被息屏才能在xml中配置亮屏,例如一個閃屏頁面,過度頁面,這些頁面一般持續時間不能太長。

綜上所述:

1. 如果是在receiver、broadcast中亮屏、喚醒cpu,只能使用wakelock,建議使用wakelock.acquire(xxxx)帶參數的。

必須有地方釋放wakelock,否則會持續亮屏、cpu活躍會對功耗有較大影響。

2. 如果是在activity、fragment等包含view 資源的頁面中亮屏,建議使用方法2、3,設置view.setKeepScreenOn(true);

或者設置WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 和 setTurnScreenOn(true);

3.如果是閃現過度頁面,可以使用方法4在xml中配置keepScreenOn=true,因爲這類頁面持續時間短,也不需要考慮自動釋放機制。

方法234所顯示的頁面,只要頁面退到後臺後都會自動釋放SCREEN_BRIGHT_WAKE_LOCK。所以在包含view的頁面中,儘量避免方法1。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章