Android WakeLock详解

目录

  • 目录
  • 前言
  • WakeLock使用
    • WakeLock levelAndFlags和使用场景
    • 参考场景
  • WakeLock源码分析
  • 结束语

前言

不知道大家是否也想过,当你手机灭屏的时候,为什么一条微信或者QQ信息能够点亮你的屏幕?

答案就是Android的WakeLock机制。这篇文章主要是介绍如何使用WakeLock,应该还达不到详解的地步,各位同学感兴趣的可以看一下WakeLock的基本使用方法。


WakeLock使用

我们先来看一下Android官方对PowerManager和WakeLock的注解:

  • PowerManager:This class gives you control of the power state of the device.
  • WakeLock:A wake lock is a mechanism to indicate that your application needs to have the device stay on.

WakeLock levelAndFlags和使用场景

Level 保持CPU 保持屏幕亮 保持键盘亮 使用场景
PARTIAL_WAKE_LOCK 长时间运行的后台服务,例如Service等
SCREEN_DIM_WAKE_LOCK 低亮度 除非必须保持CPU运行直至运算完成,否则请使用FLAG_KEEP_SCREEN_ON方式
SCREEN_BRIGHT_WAKE_LOCK 高亮度 除非必须保持CPU运行直至运算完成,否则请使用FLAG_KEEP_SCREEN_ON方式
FULL_WAKE_LOCK 高亮度 除非必须保持CPU运行直至运算完成,否则请使用FLAG_KEEP_SCREEN_ON方式

除了这四个Level之外,PowerMager还提供了两个Flag,可以配合Level使用。

FLAG 描述
ACQUIRE_CAUSES_WAKEUP 默认情况下wake locks并不是马上开启CPU、Screen或者Keyboard的illumination(对于Screen是Dim或Bright,Keyboard是Bright. wake locks只是在被开启后(比如用户的活动),让设备延续(保存)你设定开启的状态. 但是如果加上ACQUIRE_CAUSES_WAKEUP就可以让Screen或Keyboar的illumination没开启的情况,马上开启它们。 典型的应用就是在收到一个重要的notifications时,需要马上点亮屏幕。
ON_AFTER_RELEASE 当wake lock被释放的时候,当前调用wake lock的activity的计数器会被重置,所以屏幕会继续亮一段时间

注意:

这两个Flag和PARTIAL_WAKE_LOCK组合是没有作用的。


参考场景

写一个应用,可以完成如下场景:

  1. 主Activity点击一个Button,起了一个线程,先sleep 10秒,然后关闭屏幕。
  2. 线程里去获取WakeLock锁,点亮屏幕并打印信息到日志中。

具体代码如下:

MainActivity.java

    private void testWakeLock() {
        new Thread(new Runnable() {
            private void printLog() {
                for (int i = 0; i < 10; i ++) {
                    Log.e("wangzhengyi", "hello log " + i);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void run() {
                Log.e("wangzhengyi", "ready to acquire wakelock!");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                TestWakeLock twl = new TestWakeLock(MainActivity.this);
                twl.acquireWakeLock();
                printLog();
                twl.releaseWakeLock();
            }
        }).start();
    }

TestWakeLock.java

import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;

public class TestWakeLock {
    private WakeLock mWakeLock;
    private Context mContext;

    public TestWakeLock(Context context) {
        this.mContext = context;
    }

    public void acquireWakeLock() {
        if (mWakeLock == null) {
            PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "ZHENGYI.WZY");
            if (mWakeLock != null) {
                mWakeLock.acquire();
                Log.e("wangzhengyi", "get powermanager wakelock!");
            }
        }
    }

    public void releaseWakeLock() {
        if (mWakeLock != null) {
            mWakeLock.release();
            Log.e("wangzhengyi", "release powermanager wakelock!");
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.WAKE_LOCK"/>

WakeLock源码分析

不深入到WakeLock源码,怎么敢称为详解,对吧!!接下来,我们看一下WakeLock的源码实现。

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

    public static void validateWakeLockParameters(int levelAndFlags, String tag) {
        switch (levelAndFlags & WAKE_LOCK_LEVEL_MASK) {
            case PARTIAL_WAKE_LOCK:
            case SCREEN_DIM_WAKE_LOCK:
            case SCREEN_BRIGHT_WAKE_LOCK:
            case FULL_WAKE_LOCK:
            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                break;
            default:
                throw new IllegalArgumentException("Must specify a valid wake lock level.");
        }
        if (tag == null) {
            throw new IllegalArgumentException("The tag must not be null.");
        }
    }

可以看到,newWakeLock方法首先检测LevelAndFlags和Tag的合法性,代码很简单,大家可以自己看一下。其实就是:tag不能为空,Level必须用PowerManager提供的几个Level。

接下来,我们就进入到WakeLock的构造函数了。WakeLock是PowerManager的内部类。这里我删除了WakeLock类中暂时我们用不到的方法。

    public final class WakeLock {
        private final int mFlags;
        private final String mTag;
        private final String mPackageName;
        private final IBinder mToken;
        private int mCount;
        private boolean mRefCounted = true;
        private boolean mHeld;

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

        WakeLock(int flags, String tag, String packageName) {
            mFlags = flags;
            mTag = tag;
            mPackageName = packageName;
            mToken = new Binder();
        }

        /**
         * 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();
            }
        }

        private void acquireLocked() {
            if (!mRefCounted || mCount++ == 0) {
                // 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);
                try {
                    mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource);
                } catch (RemoteException e) {
                }
                mHeld = true;
            }
        }

        /**
         * Releases the wake lock.
         * <p>
         * This method releases your claim to the CPU or screen being on. The
         * screen may turn off shortly after you release the wake lock, or it
         * may not if there are other wake locks still held.
         * </p>
         */
        public void release() {
            release(0);
        }

        /**
         * Releases the wake lock with flags to modify the release behavior.
         * <p>
         * This method releases your claim to the CPU or screen being on. The
         * screen may turn off shortly after you release the wake lock, or it
         * may not if there are other wake locks still held.
         * </p>
         *
         * @param flags
         *            Combination of flag values to modify the release behavior.
         *            Currently only {@link #WAIT_FOR_PROXIMITY_NEGATIVE} is
         *            supported.
         *
         *            {@hide}
         */
        public void release(int flags) {
            synchronized (mToken) {
                if (!mRefCounted || --mCount == 0) {
                    mHandler.removeCallbacks(mReleaser);
                    if (mHeld) {
                        try {
                            mService.releaseWakeLock(mToken, flags);
                        } catch (RemoteException e) {
                        }
                        mHeld = false;
                    }
                }
                if (mCount < 0) {
                    throw new RuntimeException("WakeLock under-locked " + mTag);
                }
            }
        }

        /**
         * Returns true if the wake lock has been acquired but not yet released.
         *
         * @return True if the wake lock is held.
         */
        public boolean isHeld() {
            synchronized (mToken) {
                return mHeld;
            }
        }
    }

我们以acquire方法为例,通过对源码的分析,我们发现获取WakeLock的实现是通过mService进行的:

mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource);

而mService是在PowerManager类里实例化的:

final IPowerManager mService;

mService实例化类为/frameworks/base/services/java/com/android/server/power/PowerManagerService.java,而到这里类里,你最终发现acquireWakeLock是由JNI层的native方法实现的。

private static native void nativeAcquireSuspendBlocker(String name);

而这个方法的实现是在/frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp代码中:

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

这个acquire_wake_lock是在/hardware/libhardware_legacy/power/power.c里定义的

acquire_wake_lock(int lock, const char* id)
{
    initialize_fds();

//    ALOGI("acquire_wake_lock lock=%d id=‘%s‘\n", lock, id);

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
    }
    else {
        return EINVAL;
    }

    return write(fd, id, strlen(id));
}

可以看到,acquire wake lock真正的实现是在fd所指向的文件中写了一串字符即可。

fd所指向的文件定义如下:

const char * const NEW_PATHS[] = {
    "/sys/power/wake_lock",
    "/sys/power/wake_unlock",
};

结束语

好了,到此Android WakeLock分析结束,欢迎大家拍砖

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-29 13:20:59

Android WakeLock详解的相关文章

[gitbook] Android框架分析系列之Android Binder详解

请支持作者原创: https://mr-cao.gitbooks.io/android/content/android-binder.html Android Binder详解 Table of Contents 1. binder简介 2. binder的实现 2.1. IBinder类简介 2.2. IInterface类简介 2.3. BpBinder和BBinder简介 2.4. ProcessState和IPCThreadState简介 2.5. ServiceManager简介 2.

android动画详解三 动画API概述

· 属性动画与view动画的不同之处 view动画系统提供了仅动画View 对象的能力,所以如果你想动画非View 对象,你就要自己实现代码. view动画系统实际上还被强制仅能对 View 的少数属性进行动画,比如缩放和旋转,而不能对背景色进行. view动画的另一个坏处是它仅修改View的绘制位置,而不是View的实际位置.例如,如果你动画一个移动穿越屏幕,button的绘制位置是正确的,但实际你可以点击它的位置却没有变,所以你必须去实现你自己的逻辑来处理它. 使用属性动画系统时,这个限制被

android矩阵详解

Matrix,中文里叫矩阵,高等数学里有介绍,在图像处理方面,主要是用于平面的缩放.平移.旋转等操作. 在Android里面,Matrix由9个float值构成,是一个3*3的矩阵.最好记住.如下图: 解释一下,上面的sinX和cosX,表示旋转角度的cos值和sin值,注意,旋转角度是按顺时针方向计算的. translateX和translateY表示x和y的平移量.scale是缩放的比例,1是不变,2是表示缩放1/2,这样子. 在android.graphics.Matrix中有对应旋转的函

Android ProgressBar详解以及自定义

版本:1.0 日期:2014.5.16 版权:© 2014 kince 转载注明出处 这一次主要说一下Android下的进度条,为什么是它呢,因为近期被其各种美轮美奂的设计所倾倒,计划逐渐去实现.另外一个因素也是它也是为数不多的直接继承于View类的控件,从中可以学习到一些自定义控件的知识.下面列举了一些个人觉得还算漂亮的进度条,仅供参考. 是不是很漂亮,其实就像上面图形展示的那样,进度条大体上无非就是这几种形式.这样一来肯定是需要自定义了,所以方向有两个:要么继承于系统的ProgressBar

Android 菜单详解

Android中菜单分为三种,选项菜单(OptionMenu),上下文菜单(ContextMenu),子菜单(SubMenu) 选项菜单 可以通过两种办法增加选项菜单,一是在menu.xml中添加,该种方式参见Android 资源详解(二) 菜单资源,二是在.java中添加 1.覆盖Activity 的 onCreateOptionsMenu(Menu  menu)方法,当我们第一次打开菜单 时该方法被自动调用. 2.调用Menu的 add()方法添加菜单项(Menultem) ,可以调用Men

Android菜单详解(一)——理解android中的Menu

前言 今天看了pro android 3中menu这一章,对Android的整个menu体系有了进一步的了解,故整理下笔记与大家分享. PS:强烈推荐<Pro Android 3>,是我至今为止看到的最好的一本android书,中文版出到<精通Android 2>. 理解Android的菜单 菜单是许多应用程序不可或缺的一部分,Android中更是如此,所有搭载Android系统的手机甚至都要有一个"Menu"键,由此可见菜单在Android程序中的特殊性.An

Android surfaceview详解

周末看<精通Android游戏开发>(Pro Android Games),里面讲到游戏的框架,其中一个重要的概念surfaceview,觉得不是很理解,于是花了一点时间研究了下,写下自己的心得. surface,这个单词的意思是浮在表面的,那么surfaceview就是浮在表面的view了.如果真的这样解释,估计有人要拍砖了.然而,话虽不能这么说,取这个名儿,多少还是有点关系的.surface是一个可见区域. 我们在屏幕上看到的这些view,在屏幕上看到的就是画面,在内存中就是一块内存区.绘

android ViewPager详解

Viewpager 在android界面布局中属于常用类型 ,它可以做导航,页面菜单,进入软件是的欢迎界面 等等.比现在最流行的几款手机软件  ,QQ,微信,微博 等 ,其主界面 都用到了ViewPager,所以学好它,势在必得 ,在这里总结了下, 先用图解 : 这是一个仿微博界面的xml布局 ,他们之间的关系经常搞混淆,怕记不住 ,总结了几句话:ViewPager里面含界面,它的改变控制(title)Imageview的变化,Textview控制页面,并间接控制Title(imageview)

Android LayoutInflater 详解

Android LayoutInflater 详解 简介: 在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById(). 不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化:而findViewById()是找xml布局文件下的具体widget控件(如Button,TextView等等). 使用场景: ①对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflater()来