Android 8.1 PowerManagerService分析(三)——WakeLock机制

在《Android 8.1 PMS分析(一)》中,主要分析了PMS的启动流程,在《Android 8.1 PowerManagerService分析(二)》中,主要分析了PMS的核心方法,本篇文章开始分析android中的WakeLock机制。WakeLock机制wakelock是android系统中一种锁的机制,只要有进程持有这个锁,系统就无法进入休眠状态。应用程序要申请wakelo...
摘要由CSDN通过智能技术生成

欢迎大家关注我的掘金帐号
我会在那里定期更新最新版本的Android Framework源码模块分析~~

Android 8.1 PowerManagerService分析(一)中,主要分析了PMS的启动流程,在Android 8.1 PowerManagerService分析(二)中,主要分析了PMS的一些核心方法,本篇文章开始分析android中的WakeLock机制。

WakeLock机制概述

WakeLock是android系统中一种锁的机制,只要有进程持有这个锁,系统就无法进入休眠状态。应用程序要申请WakeLock时,需要在清单文件中配置android.Manifest.permission.WAKE_LOCK权限。

根据作用时间,WakeLock可以分为永久锁和超时锁,永久锁表示只要获取了WakeLock锁,必须显式的进行释放,否则系统会一直持有该锁;后者表示在到达给定时间后,自动释放WakeLock锁,其实现原理为方法内部维护了一个Handler。

根据释放原则,WakeLock可以分为计数锁和非计数锁,默认为计数锁,如果一个WakeLock对象为计数锁,则一次申请必须对应一次释放;如果为非计数锁,则不管申请多少次,一次就可以释放该WakeLock。以下代码为WakeLock申请释放示例,要申请WakeLock,必须有PowerManager实例,如下:

 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
 wl.acquire();
 Wl.acquire(int timeout);//超时锁

释放时:

 wl.release();

Wakelock申请和释放流程如下:
这里写图片描述
在整个WakeLock机制中,对应不同的范围,有三种表现形式:

  • PowerManger.WakeLock:PowerManagerService和其他应用、服务交互的接口;
  • PowerManagerService.WakeLock:PowerManager.WakeLock在PMS中的表现形式;
  • SuspendBlocker:PowerManagerService.WakeLock在向底层节点操作时的表现形式。

下面开始对wakelock的详细分析。

PowerManager中的WakeLock

要获取、申请Wakelock时,直接通过PowerManager的WakeLock进行。它作为系统服务的接口来供应用调用。
1.1.获取WakeLock对象
获取WakeLock实例在PowerManager中进行。
在应用中获取WakeLock对象,方式如下:

 PowerManager.WakeLock mWakeLock = 
                mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);

应用中获取wakelock对象,获取的是位于PowerManager中的内部类——WakeLock的实例,在PowerManager中看看相关方法:

public WakeLock newWakeLock(int levelAndFlags, String tag) {
   
    validateWakeLockParameters(levelAndFlags, tag);
    return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
}

在PowerManager的newWakeLock()方法中,首先进行了参数的校验,然后调用WakeLock构造方法获取实例,构造方法如下:

WakeLock(int flags, String tag, String packageName) {
   
//表示wakelock类型或等级
    mFlags = flags;
//一个tag,一般为当前类名
    mTag = tag;
//获取wakelock的包名
    mPackageName = packageName;
//一个Binder标记
    mToken = new Binder();
    mTraceName = "WakeLock (" + mTag + ")";
}

除了构造方法中必须要传入的参数之外,还有如下几个属性:

//表示内部计数
private int mInternalCount;
//表示内部计数
private int mExternalCount;
//表示是否是计数锁
private boolean mRefCounted = true;
//表示是否已经持有该锁
private boolean mHeld;
//表示和该wakelock相关联的工作源,这在当一个服务获取wakelock执行工作时很有用,以便计算工作成本
private WorkSource mWorkSource;
//表示一个历史标签
private String mHistoryTag;

1.2.WakeLock等级(类别)

Wakelock共有以下几种等级:

//如果持有该类型的wakelock锁,则按Power键灭屏后,即使允许屏幕、按键灯灭,也不会释放该锁,CPU不会进入休眠状态
public static final int PARTIAL_WAKE_LOCK;
//Deprecated,如果持有该类型的wakelock锁,则使屏幕保持亮/Dim的状态,键盘灯允许灭,按Power键灭屏后,会立即释放
public static final int SCREEN_DIM_WAKE_LOCK;
//Deprecated,如果持有该类型的wakelock锁,则使屏幕保持亮的状态,键盘灯允许灭,按Power键灭屏后,会立即释放
public static final int SCREEN_BRIGHT_WAKE_LOCK
//Deprecated,如果持有该类型的wakelock锁,则使屏幕、键盘灯都保持亮,按Power键灭屏后,会立即释放
public static final int FULL_WAKE_LOCK
//如果持有该锁,则当PSensor检测到有物体靠近时关闭屏幕,远离时又亮屏,该类型锁不会阻止系统进入睡眠状态,比如
//当到达休眠时间后会进入睡眠状态,但是如果当前屏幕由该wakelock关闭,则不会进入睡眠状态。
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK
//如果持有该锁,则会使屏幕处于DOZE状态,同时允许CPU挂起,该锁用于DreamManager实现Doze模式,如SystemUI的DozeService
public static final int DOZE_WAKE_LOCK
//如果持有该锁,则会时设备保持唤醒状态,以进行绘制屏幕,该锁常用于WindowManager中,允许应用在系统处于Doze状态下时进行绘制
public static final int DRAW_WAKE_LOCK

这些值会在下面以图标的形式总结。除了等级之外,还有几个标记:

//该值为0x0000FFFF,用于根据flag判断Wakelock的级别,如:
//if((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) == PowerManager.PARTIAL_WAKE_LOCK){}
public static final int WAKE_LOCK_LEVEL_MASK
//用于在申请锁时唤醒设备,一般情况下,申请wakelock锁时不会唤醒设备,它只会导致屏幕保持打开状态,如果带有这个flag,则会在申
//请wakelock时就点亮屏幕,如常见通知来时屏幕亮,该flag不能和PowerManager.PARTIAL_WAKE_LOCE一起使用。
public static final int ACQUIRE_CAUSES_WAKEUP
//在释放锁时,如果wakelock带有该标志,则会小亮一会再灭屏,该flag不能和PowerManager.PARTIAL_WAKE_LOCE一起使用。
public static final int ON_AFTER_RELEASE
//和其他标记不同,该标记是作为release()方法的参数,且仅仅用于释放PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK类型的
//锁,如果带有该参数,则会延迟释放锁,直到传感器不再感到对象接近
public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY

申请WakeLock

当获取到WakeLock实例后,就可以申请WakeLock了。前面说过了,根据作用时间,WakeLock锁可以分为永久锁和超时锁,根据释放原则,WakeLock可以分为计数锁和非计数锁。申请方式如下:

 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
 wl.acquire();//申请一个永久锁
 Wl.acquire(int timeout);//申请一个超时锁

acquire()方法源码如下:

public void acquire() {
   
    synchronized (mToken) {
   
        acquireLocked();
    }
}
public void acquire(long timeout) {
   
    synchronized (mToken) {
   
        acquireLocked();
        //申请锁之后,内部会维护一个Handler去完成自动释放锁
        mHandler.postDelayed(mReleaser, timeout);
    }
}

可以看到这两种方式申请方式完全一样,只不过如果是申请一个超时锁,则会通过Handler延时发送一个消息,到达时间后去自动释放锁。
到这一步,对于申请wakelock的应用或系统服务来说就完成了,具体的申请在PowerManager中进行,继续看看
acquireLocked()方法:

private void acquireLocked() {
   
    //应用每次申请wakelock,内部计数和外部计数加1
    mInternalCount++;
    mExternalCount++;
    //如果是非计数锁或者内部计数值为1,即第一次申请该锁,才会真正去申请
    if (!mRefCounted || mInternalCount == 1) {
   
        mHandler.removeCallbacks(mReleaser);
        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
        try {
   
            //向PowerManagerService申请锁
            mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                    mHistoryTag);
        } catch (RemoteException e) {
   
            throw e.rethrowFromSystemServer();
        }
        //表示此时持有该锁
        mHeld = true;
    }
}

是否是计数锁可以通过setReferenceCount()来设置,默认为计数锁:

public void setReferenceCounted(boolean value) {
   
    synchronized (mToken) {
   
        mRefCounted = value;
    }
}

从acquire()方法可以看出,对于计数锁来说,只会在第一次申请时向PowerManagerService去申请锁,当该wakelock实例第二次、第三次去申请时,如果没有进行过释放,则只会对计数引用加1,不会向PowerManagerService去申请。如果是非计数锁,则每次申请,都会调到PowerManagerService中去。

释放WakeLock锁

如果是通过acquire(long timeout)方法申请的超时锁,则会在到达时间后自动去释放,如果是通过acquire()方法申请的永久锁,则必须进行显式的释放,否则由于系统一直持有wakelock锁,将导致无法进入休眠状态,从而导致耗电过快等功耗问题。

在前面分析申请锁时已经说了,如果是超时锁,通过Handler.post(Runnable)的方式进行释放,该Runnable定义如下:

private final Runnable mReleaser = new Runnable() {
   
    public void run() {
   
        release(RELEASE_FLAG_TIMEOUT);
    }
};

RELEASE_FLAG_TIMEOUT是一个用于release()方法的flag,表示释放的为超时锁。
如果是永久锁,则必须通过调用release()方法进行释放了,该方法如下:

public void release() {
   
    release(0);
}
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值