Android 7.0加强了idle模式,就是设备不充电且屏幕关闭情况下就会逐渐进入idle模式,这个比6.0加强了限制,也就是不考虑手机是否静止。idle模式的影响可以查看google官网介绍,这里就不多说了。这边主要介绍的是如何处理idle,如果你的应用需要用到推送等功能时,这种模式就是一个致命打击,那么有没有办法应对呢,答案当然是有了,官网推荐的是使用google自带服务GCM,但是国内需要翻墙,所以这种办法不太现实,那么就头疼了,有没有其他办法呢?

可以说是无意中一个发现让我了解到了另一种方式,之前在测试一个应用在android N上面关于Idle模式的影响时发现其不受影响,这就觉得很奇怪了,于是经过一番测试发现好像是由于前台service造成的,但是又不确定,于是在好奇心的驱动下把源码下了下来,经过一番分析,还真是这个原因,接下来就来具体分析一下:

一、DeviceIdleController.java

(framework/base/services/core/java/com/android/server/DeviceIdleController.java),这个类就是主要处理Idle模式的控制类,首先从这个类下手,下面是启动Idle模式时的处理方法:

case MSG_REPORT_IDLE_ON_LIGHT: {EventLogTags.writeDeviceIdleOnStart();final boolean deepChanged;final boolean lightChanged;if (msg.what == MSG_REPORT_IDLE_ON) {deepChanged = mLocalPowerManager.setDeviceIdleMode(true);lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);} else {deepChanged = mLocalPowerManager.setDeviceIdleMode(false);lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);}try {mNetworkPolicyManager.setDeviceIdleMode(true);mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON? BatteryStats.DEVICE_IDLE_MODE_DEEP: BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());} catch (RemoteException e) {}if (deepChanged) {getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);}if (lightChanged) {getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);}EventLogTags.writeDeviceIdleOnComplete();} break;

从上面可以看出,进入Idle模式主要执行三个操作

1)mLocalPowerManager.setDeviceIdleMode(true);

2)mNetworkPolicyManager.setDeviceIdleMode(true);

3)mBatteryStats.noteDeviceIdleMode(..)

我们比较关心的是网络方面的,所以接下来主要分析mNetworkPolicyManager。

二、NetworkPolicyManager.java

(framework/base/services/core/java/com/android/server/net/NetworkPolicyManager.java)这个类就是mNetworkPolicyManager实例对象,首先看setDeviceIdleMode函数:

public void setDeviceIdleMode(boolean enabled) {mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);synchronized (mRulesLock) {if (mDeviceIdleMode != enabled) {mDeviceIdleMode = enabled;if (mSystemReady) {// Device idle change means we need to rebuild rules for all// known apps, so do a global refresh.updateRulesForGlobalChangeLocked(false);}if (enabled) {EventLogTags.writeDeviceIdleOnPhase("net");} else {EventLogTags.writeDeviceIdleOffPhase("net");}}}}

从上面可以看出主要调用的函数是updateRulesForGlobalChangeLocked(false);那我们继续跟踪下去:

Private void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {long start;if (LOGD) start = System.currentTimeMillis();updateRulesForDeviceIdleLocked();updateRulesForAppIdleLocked();updateRulesForRestrictPowerLocked();updateRulesForRestrictBackgroundLocked();setRestrictBackgroundLocked(mRestrictBackground);// If the set of restricted networks may have changed, re-evaluate those.if (restrictedNetworksChanged) {normalizePoliciesLocked();updateNetworkRulesLocked();}if (LOGD) {final long delta = System.currentTimeMillis() - start;Slog.d(TAG, "updateRulesForGlobalChangeLocked(" + restrictedNetworksChanged + ") took "+ delta + "ms");}
}

这个函数里面前面几个调用都是更新所有安装应用rule的,主要还是setRestrictBackgroundLocked(mRestrictBackground);那我们接着来看这个函数:

private void setRestrictBackgroundLocked(boolean restrictBackground) {Slog.d(TAG, "setRestrictBackgroundLocked(): " + restrictBackground);final boolean oldRestrictBackground = mRestrictBackground;mRestrictBackground = restrictBackground;// Must whitelist foreground apps before turning data saver mode on.// TODO: there is no need to iterate through all apps here, just those in the foreground,// so it could call AM to get the UIDs of such apps, and iterate through them instead.updateRulesForRestrictBackgroundLocked();try {if (!mNetworkManager.setDataSaverModeEnabled(mRestrictBackground)) {Slog.e(TAG, "Could not change Data Saver Mode on NMS to " + mRestrictBackground);mRestrictBackground = oldRestrictBackground;// TODO: if it knew the foreground apps (see TODO above), it could call// updateRulesForRestrictBackgroundLocked() again to restore state.return;}} catch (RemoteException e) {// ignored; service lives in system_server}updateNotificationsLocked();writePolicyLocked();}

重点来了,我们看到里面的注释中出现foreground apps,这个说明Idle模式和foreground apps还是有关系的,分析代码可以发现主要函数是updateRulesForRestrictBackgroundLocked();那我们继续下去,感觉快得出答案了:

private void updateRulesForRestrictBackgroundLocked() {final PackageManager pm = mContext.getPackageManager();// update rules for all installed applicationsfinal List<UserInfo> users = mUserManager.getUsers();final List<ApplicationInfo> apps = pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_DISABLED_COMPONENTS| PackageManager.MATCH_DIRECT_BOOT_AWARE| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);final int usersSize = users.size();final int appsSize = apps.size();for (int i = 0; i < usersSize; i++) {final UserInfo user = users.get(i);for (int j = 0; j < appsSize; j++) {final ApplicationInfo app = apps.get(j);final int uid = UserHandle.getUid(user.id, app.uid);updateRulesForDataUsageRestrictionsLocked(uid);updateRulesForPowerRestrictionsLocked(uid);}}}

这个函数比较容易分析,主要就是两个函数调用,这两个函数都是用来更新rule的,所以基本流程是一样的,我们分析一个就行了,这里选择updateRulesForPowerRestrictionsLocked(uid);

private void updateRulesForPowerRestrictionsLocked(int uid) {if (!isUidValidForBlacklistRules(uid)) {if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);return;}final boolean isIdle = isUidIdle(uid);final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);final int oldUidRules = mUidRules.get(uid, RULE_NONE);final boolean isForeground = isUidForegroundOnRestrictPowerLocked(uid);final boolean isWhitelisted = isWhitelistedBatterySaverLocked(uid);final int oldRule = oldUidRules & MASK_ALL_NETWORKS;int newRule = RULE_NONE;// First step: define the new rule based on user restrictions and foreground state.// NOTE: if statements below could be inlined, but it's easier to understand the logic// by considering the foreground and non-foreground states.if (isForeground) {if (restrictMode) {newRule = RULE_ALLOW_ALL;}} else if (restrictMode) {newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;}final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule;if (LOGV) {Log.v(TAG, "updateRulesForNonMeteredNetworksLocked(" + uid + ")"+ ", isIdle: " + isIdle+ ", mRestrictPower: " + mRestrictPower+ ", mDeviceIdleMode: " + mDeviceIdleMode+ ", isForeground=" + isForeground+ ", isWhitelisted=" + isWhitelisted+ ", oldRule=" + uidRulesToString(oldRule)+ ", newRule=" + uidRulesToString(newRule)+ ", newUidRules=" + uidRulesToString(newUidRules)+ ", oldUidRules=" + uidRulesToString(oldUidRules));}if (newUidRules == RULE_NONE) {mUidRules.delete(uid);} else {mUidRules.put(uid, newUidRules);}// Second step: notify listeners if state changed.if (newRule != oldRule) {if (newRule == RULE_NONE || (newRule & RULE_ALLOW_ALL) != 0) {if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid);} else if ((newRule & RULE_REJECT_ALL) != 0) {if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid);} else {// All scenarios should have been covered aboveLog.wtf(TAG, "Unexpected change of non-metered UID state for " + uid+ ": foreground=" + isForeground+ ", whitelisted=" + isWhitelisted+ ", newRule=" + uidRulesToString(newUidRules)+ ", oldRule=" + uidRulesToString(oldUidRules));}mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();}}

这边我们发现出现了一个变量 final boolean isForeground = isUidForegroundOnRestrictPowerLocked(uid);那这个变量什么时候为true呢,我们来分析一下这个函数:

private boolean isUidForegroundOnRestrictPowerLocked(int uid) {final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);}static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;}

答案揭晓了,当前的进程优先级至少是前台service时就为true,好的,我们回到原来的代码,继续看下去我们又发现

if (isForeground) {if (restrictMode) {newRule = RULE_ALLOW_ALL;}}

RULE_ALLOW_ALL一看就是允许了该应用访问网络的权限,后面代码就是发送广播通知rule变化,这里就不多说了。

三、总结

经过上面的分析,我们大致明白了,如果把应用优先级提高到前台service及以上,那么idle模式就不会限制你访问网络等,这样就多了一种方法来绕过idle模式影响,其实想一下也应该知道,就像播放音乐时,即使你屏幕关闭,也不应该把你的音乐停掉,不然肯定被用户骂死,好了,分析就到这里了~~

Android N Idle模式分析相关推荐

  1. Android GPU呈现模式分析功能,手机流畅度。仅供参考

    手机的流畅度直接影响使用体验,很多小伙们非常在意这一点,而手机流畅与否除了取决于系统本身的优化和元件的老化程度,与一些外部因素诸如温度和网络信号都有密切关系.今天跟大家介绍一个手机系统里面自带的小工具 ...

  2. Android电源管理介绍

    一.电源管理基础知识 1.1电源管理的几种状态 Android kernel源码中,定义了三种电源状态,在kernel/power/suspend.c中: 对应的宏定义/include/linux/s ...

  3. android中的C2DM

    第一篇: 原文地址:http://bigcat.easymorse.com/?p=1210 C2DM功能要求Android版本在2.2以上,同时设备中需要安装Android Market.整个架构包括 ...

  4. Android Doze模式分析

    Android 6.0 Doze模式分析 Doze模式是Android6.0上新出的一种模式,是一种全新的.低能耗的状态,在后台只有部分任务允许运行,其他都被强制停止.当用户一段时间没有使用手机的时候 ...

  5. Android源码设计模式分析项目

    原文链接:https://github.com/simple-android-framework/android_design_patterns_analysis Android源码设计模式分析开源项 ...

  6. Android 7.0 Doze模式分析

    白名单机制 Android6.0及更高版本还提供了Doze模式白名单列表,通过设置应用程序进入白名单 列表可逃脱Doze模式的各种限制.检测应用程序是否存在白名单list里面,可使用 PowerMan ...

  7. Android:Handler中的Idle Handler

    [Android] Handler中的IdleHandler 抛出 Handler中的IdleHandler 它有什么能力? 它有什么用处? 能想到一些合适的场景吗? Answer1: 首先看下源码的 ...

  8. Android开发者选项——GPU呈现模式分析

    1玄学曲线各部分到底代表了什么 对于Android用户来说,无论你用的什么品牌的手机,在开发者选项中都能发现"玄学曲线"的开关,之所以称其为玄学曲线,还是因为它被很多网友用于测试一 ...

  9. android 4.0 安全模式分析

    前言 Android系统中具备6个模式,分别为一般启动模式(normal mode).安全模式(safe mode).恢复模式(recovery mode).引导模式(bootloader mode) ...

  10. Android doze模式分析一

    一.引言 DOZE是安卓系统从6.0(API级别23)开始引入的对低电和应用待机模式的优化.具体介绍可以参考developers官方文档. 阅读完官文后,思考几个问题: 6.0之前,APP怎么处理可以 ...

最新文章

  1. 报Java面授班有哪些优势
  2. Python之美[从菜鸟到高手]--一步一步动手给Python写扩展(异常处理和引用计数)
  3. 转:android实时语音问题分析
  4. (1)段寄存器属性探测
  5. MyEclipse 常用设置和操作方法
  6. 使用Scrapy时出现虽然队列里有很多Request但是却不下载,造成假死状态
  7. linux命令之awk终极系列
  8. python 中locals() 和 globals()的区别
  9. 【工具推荐】PDF和其他格式的相关的转换
  10. 自定义Kettle数据库插件 1
  11. java 天上掉东西游戏的源代码_【小游戏】前两天的小游戏终于调试成功了。。。。直接源代码...
  12. 安全厂商发布网络间谍平台ProjectSauron研究报告
  13. Instagram第三方接入
  14. 为什么有人劝别选计算机专业?
  15. 我的非计算机科班好友,斩获了十几个 offer
  16. Gradle 2.0 用户指南翻译——第十九章. Gradle 守护进程
  17. C语言编程————杨辉三角
  18. 华为荣耀路由器虚拟服务器,荣耀路由器怎么设置?
  19. eclipse的放大字体
  20. python 0基础容易学吗_Python0基础好学吗?

热门文章

  1. SAP 因谁而腾飞?NetApp
  2. PHP实现域名whois查询的代码(数据源万网、新网)
  3. 2021-07-30-DJ-005 Django模型的数据批量加载
  4. iGrimaceV8 V8在线威锋源apt.so/qwkjv8手机直接下载安装教程图:
  5. 物联网IP设备添加与入网
  6. 系统漏洞利用与提权攻击机场景
  7. 微信公众号与小程序对接文档
  8. 了解卡尔曼滤波器2--最优状态估计
  9. 腾讯程序员与医生相恋,却被女方父母拆散,你们不能在一起
  10. 互联网人年底加薪指南