Android中屏幕常亮的方法

QuibblerQuibbler 2024-02-27 约 15 分钟 1994 次阅读

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);
			}
		}
	}

如果忘记释放,将会增加手机功耗,一款优秀的应用绝不能在后台无端增加功耗。

相关博客:

        showWhenLocked 和 showOnLockScreen的区别

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w