Android7.0 Doze模式

本文详细介绍了Android 7.0的Doze模式,一种节能状态,当设备未充电、屏幕关闭且静止时,限制后台应用的CPU和网络活动以节省电量。Doze模式通过定期的maintenance window允许应用完成延缓的任务。DeviceIdleController作为Doze模式的主要控制器,负责状态管理和条件检测。分析了DeviceIdleController的初始化、状态变化流程,以及充电状态变化时的处理。Doze模式通过监控设备状态并在满足条件时进入深度休眠,延长电池续航。
摘要由CSDN通过智能技术生成

在Android M中,Google就引入了Doze模式。它定义了一种全新的、低能耗的状态。
在该状态,后台只有部分任务被允许运行,其它任务都被强制停止。

本篇博客中,我们就来分析一下Android 7.0中Doze模式相关的流程。

一、基本原理
Doze模式可以简单概括为:
若判断用户在连续的一段时间内没有使用手机,就延缓终端中APP后台的CPU和网络活动,以达到减少电量消耗的目的。

上面这张图比较经典,基本上说明了Doze模式的含义。
图中的横轴表示时间,红色部分表示终端处于唤醒的运行状态,绿色部分就是Doze模式定义的休眠状态。

从图中的描述,我们可以看到:如果一个用户停止充电(on battery: 利用电池供电),关闭屏幕(screen off),手机处于静止状态(stationary: 位置没有发生相对移动),保持以上条件一段时间之后,终端就会进入Doze模式。一旦进入Doze模式,系统就减少(延缓)应用对网络的访问、以及对CPU的占用,来节省电池电量。

如图所示,Doze模式还定义了maintenance window。
在maintenance window中,系统允许应用完成它们被延缓的动作,即可以使用CPU资源及访问网络。
从图中我们可以看出,当进入Doze模式的条件一直满足时,Doze模式会定期的进入到maintenance window,但进入的间隔越来越长。
通过这种方式,Doze模式可以使终端处于较长时间的休眠状态。

需要注意的是:一旦Doze模式的条件不再满足,即用户充电、或打开屏幕、或终端的位置发生了移动,终端就恢复到正常模式。
因此,当用户频繁使用手机时,Doze模式几乎是没有什么实际用处的。

具体来讲,当终端处于Doze模式时,进行了以下操作:
1、暂停网络访问。
2、系统忽略所有的WakeLock。
3、标准的AlarmManager alarms被延缓到下一个maintenance window。
但使用AlarmManager的 setAndAllowWhileIdle、setExactAndAllowWhileIdle和setAlarmClock时,alarms定义事件仍会启动。
在这些alarms启动前,系统会短暂地退出Doze模式。
4、系统不再进行WiFi扫描。
5、系统不允许sync adapters运行。
6、系统不允许JobScheduler运行。

更多基本信息的描述可以参考:
What is Doze mode in android 6.0 Marshmallow?

二、DeviceIdleController的初始化
Android中的Doze模式主要由DeviceIdleController来控制。

public class DeviceIdleController extends SystemService
        implements AnyMotionDetector.DeviceIdleCallback {
   
    ....................
}

可以看出DeviceIdleController继承自SystemService,是一个系统级的服务。
同时,继承了AnyMotionDetector定义的接口,便于检测到终端位置变化后进行回调。

接下来我们看看它的初始化过程。

private void startOtherServices() {
    .........
    mSystemServiceManager.startService(DeviceIdleController.class);
    .........
}

如上代码所示,SystemServer在startOtherServices中启动了DeviceIdleController,将先后调用DeviceIdleController的构造函数和onStart函数。

1、构造函数

public DeviceIdleController(Context context) {
    super(context);
    //deviceidle.xml用于定义idle模式也能正常工作的非系统应用
    //一般终端似乎并没有定义deviceidle.xml
    mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
    mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
}

DeviceIdleController的构造函数比较简单,就是在创建data/system/deviceidle.xml对应的file文件,同时创建一个对应于后台线程的handler。

2、onStart

public void onStart() {
    final PackageManager pm = getContext().getPackageManager();

    synchronized (this) {
        //读取配置文件,判断Doze模式是否允许被开启
        mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(
                com.android.internal.R.bool.config_enableAutoPowerModes);

        //分析PKMS时提到过,PKMS扫描系统目录的xml,将形成SystemConfig
        SystemConfig sysConfig = SystemConfig.getInstance();

        //获取除了device Idle模式外,都可以运行的系统应用白名单
        ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();
        for (int i=0; i<allowPowerExceptIdle.size(); i++) {
            String pkg = allowPowerExceptIdle.valueAt(i);
            try {
                ApplicationInfo ai = pm.getApplicationInfo(pkg,
                        PackageManager.MATCH_SYSTEM_ONLY);
                int appid = UserHandle.getAppId(ai.uid);
                mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
            } catch (PackageManager.NameNotFoundException e) {
            }
        }

        //获取device Idle模式下,也可以运行的系统应用白名单
        ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
        for (int i=0; i<allowPower.size(); i++) {
             String pkg = allowPower.valueAt(i);
            try {
                ApplicationInfo ai = pm.getApplicationInfo(pkg,
                         PackageManager.MATCH_SYSTEM_ONLY);
                int appid = UserHandle.getAppId(ai.uid);
                // These apps are on both the whitelist-except-idle as well
                // as the full whitelist, so they apply in all cases.
                mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
                mPowerSaveWhitelistApps.put(ai.packageName, appid);
                mPowerSaveWhitelistSystemAppIds.put(appid, true);
            } catch (PackageManager.NameNotFoundException e) {
            }
        }

        //Constants为deviceIdleController中的内部类,继承ContentObserver
        //监控数据库变化,同时得到Doze模式定义的一些时间间隔
        mConstants = new Constants(mHandler, getContext().getContentResolver());

        //解析deviceidle.xml,并将其中定义的package对应的app,加入到mPowerSaveWhitelistUserApps中
        readConfigFileLocked();

        //将白名单的内容给AlarmManagerService和PowerMangerService
        //例如:DeviceIdleController判断开启Doze模式时,会通知PMS
        //此时除去白名单对应的应用外,PMS会将其它所有的WakeLock设置为Disable状态
        updateWhitelistAppIdsLocked();

        //以下的初始化,都是假设目前处在进入Doze模式相反的条件上
        mNetworkConnected = true;
        mScreenOn = true;
        // Start out assuming we are charging.  If we aren't, we will at least get
        // a battery update the next time the level drops.
        mCharging = true;

        //Doze模式定义终端初始时为ACTIVE状态
        mState = STATE_ACTIVE;
        //屏幕状态初始时为ACTIVE状态
        mLightState = LIGHT_STATE_ACTIVE;
        mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
    }

    //发布服务
    //BinderService和LocalService均为DeviceIdleController的内部类
    mBinderService = new BinderService();
    publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
    publishLocalService(LocalService.class, new LocalService());
}

除去发布服务外,DeviceIdleController在onStart函数中,主要是读取配置文件更新自己的变量,思路比较清晰。

在这里我们仅跟进一下updateWhitelistAppIdsLocked函数:

private void updateWhitelistAppIdsLocked() {
    //构造出除去idle模式外,可运行的app id数组 (可认为是系统和普通应用的集合&#x
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值