Android7.0 Doze模式分析(一)Doze介绍 & DeviceIdleController



参考:http://blog.csdn.net/gaugamela/article/details/52981984

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

在之前的博客中分析过Doze模式,就是device idle状态。可能有的地方分析的不是很详细,现在在android7.0上重新分析下。

一、基本原理

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运行。

另外我在另一篇博客中:http://blog.csdn.net/kc58236582/article/details/50554174也详细介绍了Doze模式,可以参考下,上面有一些命令使用等。

 

二、DeviceIdleController

Android中的Doze模式主要由DeviceIdleController来控制。

 

public class DeviceIdleController extends SystemService
        implements AnyMotionDetector.DeviceIdleCallback 

 

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

2.1 DeviceIdleController的初始化

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

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

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

构造函数

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

DeviceIdleController的构造函数比较简单,就是在创建data/system/deviceidle.xml对应的file文件,同时创建一个对应于后台线程的handler。这里的deviceidle.xml可以在设置中的电池选项那里。有电池优化,可以将一些应用放到白名单中,调用DeviceIdleController的addPowerSaveWhitelistApp方法,最后会写入deviceidle.xml文件,然后在下次开机的时候DeviceIdleController会重新读取deviceidle.xml文件然后放入白名单mPowerSaveWhitelistUserApps中。

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数组 (可认为是系统和普通应用的集合)
    //mPowerSaveWhitelistAppsExceptIdle从系统目录下的xml得到
    //mPowerSaveWhitelistUserApps从deviceidle.xml得到,或调用接口加入;
    //mPowerSaveWhitelistExceptIdleAppIds并未使用
    mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(mPowerSaveWhitelistAppsExceptIdle,
            mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);

    //构造不受Doze限制的app id数组 (可认为是系统和普通应用的集合)
    //mPowerSaveWhitelistApps从系统目录下的xml得到
    //mPowerSaveWhitelistAllAppIds并未使用
    mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,
            mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);

    //构造不受Doze限制的app id数组(仅普通应用的集合)、
    //mPowerSaveWh
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值