Android中屏幕常亮的方法
某些场景需要应用的界面保持常亮,当然只是设置在本应用内常亮,不影响系统或者其它应用的显示行为。简单了解一下Android中如何保持页面常亮的几种方式:
1、给Window添加Flag
向当前Window的flag添加FLAG_KEEP_SCREEN_ON,此方法最简单,不需要申请任何权限。
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
FLAG_KEEP_SCREEN_ON在WindowManager中的定义如下:
/** Window flag: as long as this window is visible to the user, keep
* the device's screen turned on and bright. */
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
2、View的属性方法
在xml布局文件中,任意View添加keepScreenOn="true"属性:
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true" />
代码中通过setKeepScreenOn(boolean keepScreenOn)方法设置:
view.keepScreenOn = true
setKeepScreenOn(boolean keepScreenOn)方法也是通过设置flag实现的:
/**
* Controls whether the screen should remain on, modifying the
* value of {@link #KEEP_SCREEN_ON}.
*
* @param keepScreenOn Supply true to set {@link #KEEP_SCREEN_ON}.
*
* @see #getKeepScreenOn()
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
public void setKeepScreenOn(boolean keepScreenOn) {
setFlags(keepScreenOn ? KEEP_SCREEN_ON : 0, KEEP_SCREEN_ON);
}
当View有设置KEEP_SCREEN_ON标志位,当前包含此View的窗口会保持常亮,和第一种方式效果是一样的。
/**
* View flag indicating that the screen should remain on while the
* window containing this view is visible to the user. This effectively
* takes care of automatically setting the WindowManager's
* {@link WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON}.
*/
public static final int KEEP_SCREEN_ON = 0x04000000;
3、Wakelock 锁定机制
WakeLock是Android框架层提供的一套机制,应用使用该机制可以达到控制Android设备状态的目的。这里的设备状态主要指屏幕的打开关闭,CPU的保持运行。简单的理解WakeLock是让系统保持”清醒”的一种手段。
3.1、WAKE_LOCK权限
需要在AndroidManifest.xml中添加权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
获取和释放锁的过程中,在PowerManagerService中会通过enforceCallingOrSelfPermission()校验应用是否有WAKE_LOCK权限:
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag, int displayId,
@Nullable IWakeLockCallback callback) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
...
//检查调用应用是否有WAKE_LOCK权限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
...
try {
acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,
uid, pid, callback);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
3.2、获取锁
获取WakeLock,WakeLock是PowerManager的内部类,其代码路径位于:frameworks/base/core/java/android/os/PowerManager.java。
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "tag:Quibbler")
申请WakeLock有两个方法,acquire():
/**
* Acquires the wake lock.
* <p>
* Ensures that the device is on at the level requested when
* the wake lock was created.
* </p>
*/
public void acquire() {
synchronized (mToken) {
acquireLocked();
}
}
和acquire(long timeout):
/**
* Acquires the wake lock with a timeout.
* <p>
* Ensures that the device is on at the level requested when
* the wake lock was created. The lock will be released after the given timeout
* expires.
* </p>
*
* @param timeout The timeout after which to release the wake lock, in milliseconds.
*/
public void acquire(long timeout) {
synchronized (mToken) {
acquireLocked();
mHandler.postDelayed(mReleaser, timeout);
}
}
后者相对更安全点。如果忘记了释放WakeLock,经过 timeout 的时长后,系统会自动释放。这两个方法都是通过acquireLocked()从PowerManagerService系统服务中拿到锁:
private void acquireLocked() {
mInternalCount++;
mExternalCount++;
if (!mRefCounted || mInternalCount == 1) {
// Do this even if the wake lock is already thought to be held (mHeld == true)
// because non-reference counted wake locks are not always properly released.
// For example, the keyguard's wake lock might be forcibly released by the
// power manager without the keyguard knowing. A subsequent call to acquire
// should immediately acquire the wake lock once again despite never having
// been explicitly released by the keyguard.
mHandler.removeCallbacks(mReleaser);
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_POWER,
"WakeLocks", mTag, mTagHash);
try {
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
mHistoryTag, mDisplayId, mCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mHeld = true;
}
}
3.3、释放锁
通常可以在onResume()中调用acquire()方法:
//wakeLock.acquire()
wakeLock.acquire(60 * 60)
一般情况下,使用完WakeLock需要尽快释放,在生命周期onPause()方法中调用release()方法:
wakeLock.release()
释放PowerManagerService系统服务中持有的锁:
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.asyncTraceForTrackEnd(Trace.TRACE_TAG_POWER,
"WakeLocks", mTagHash);
try {
mService.releaseWakeLock(mToken, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mHeld = false;
}
}
if (mRefCounted && mExternalCount < 0) {
throw new RuntimeException("WakeLock under-locked " + mTag);
}
}
}
如果忘记释放,将会增加手机功耗,一款优秀的应用绝不能在后台无端增加功耗。