Android中屏幕常亮的方法

Quibbler 2月前 107

Android中屏幕常亮的方法


        某些场景需要应用的界面保持常亮,当然只是设置在本应用内常亮,不影响系统或者其它应用的显示行为。简单了解一下Android中如何保持页面常亮的几种方式:



1、给Window添加Flag

        向当前Window的flag添加FLAG_KEEP_SCREEN_ON,此方法最简单,不需要申请任何权限。

    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

        FLAG_KEEP_SCREEN_ONWindowManager中的定义如下:

    /** 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、获取锁

        获取WakeLockWakeLockPowerManager的内部类,其代码路径位于: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);
			}
		}
	}

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


不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com