android 7.0 学习笔记(一)
导读
增强的Doze模式
后台优化
Data Saver
一.增强的Doze模式
Android N对Android M引进的Doze模式进行了进一步的增强,变化体现在两个方面.一方面是降低了进入Doze模式的条件,Android M中的条件是不插电,屏幕熄灭且静置一段时间,在Android N中去掉了静置的条件,这个改变大大增加了设备进入Doze模式的机会,因而使得Doze对应用程序的影响大大增加.另一方面,Doze模式被分成了两个阶段,当设备切断电源,熄灭屏幕一段时间,会进入到第一阶段,切断网络连接,推迟任务和同步.如果设备在第一阶段的基础上再静置一段时间,就会进入第二阶段,在第一阶段的基础上增加对维持唤醒(PowerManager.WakeLock),定时任务(AlarmManager alarms),GPS和Wi-Fi扫描的限制,如下图所示:
First level

Second level

应对方案:
方案一:在Android 6.0中AlarmManager中增加了两个方法setAndAllowWhileIdle() and setExactAndAllowWhileIdle(),通过使用这两个方法可以让alarm在Doze模式下运行.
需要注意的是官方文档指出,使用这两个方法时,每个应用每9分钟只能唤醒一次alarm.
Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.
以一个定时任务为例进行测试,核心代码如下:
Service:
在Service的onStartCommand()方法中,获取一个AlarmManager的实例,设置任务执行的时间是10s后,处理定时任务的广播接收器是AlarmReceiver.
public class LongRunningService extends Service {
public static final String TAG = "LongRunningService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG,"executed at " + new Date().toString());
}
}).start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int offset= 10 * 1000;//间隔时间10s
long triggerAtTime = SystemClock.elapsedRealtime() + offset;
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, i, 0);
manager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
// manager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
return super.onStartCommand(intent, flags, startId);
}
}
BroadcastReceiver:
重写onReceive()方法,创建一个Intent,启动LongRunningService.这样一来,就形成了一个每隔10s执行一次的定时任务.
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, LongRunningService.class);
context.startService(i);
}
}
测试结果:
I/LongRunningService: executed at Thu Apr 14 22:32:58 GMT+08:00 2016
I/LongRunningService: executed at Thu Apr 14 22:33:08 GMT+08:00 2016
I/LongRunningService: executed at Thu Apr 14 22:33:18 GMT+08:00 2016
I/LongRunningService: executed at Thu Apr 14 22:42:18 GMT+08:00 2016
I/LongRunningService: executed at Thu Apr 14 22:51:18 GMT+08:00 2016
从测试结果可以看出,设备在正常使用的情况下(前三行),每隔10s运行一次,进入到Doze模式后(后三行),每隔9分钟执行一次.
为方便操作,这里介绍一下测试步骤:
Step1.运行应用程序
Step2.关闭设备的屏幕
Step3.使用如下命令强制系统进入Doze模式
$ adb shell dumpsys battery unplug
$ adb shell dumpsys deviceidle step
需要多次运行第二条命令,直到设备进入到空闲状态.

方案二:在应用程序运行时,引导用户将该应用添加到白名单中.
实现方式1(不推荐):使用ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS使用户跳转到电池优化设置页,手动将该应用添加到白名单中.
核心代码如下:
Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivity(intent);
该方法可行,但存在一个缺点:跳转到电池优化页后,用户需要在”所有应用”列表里(应用安装后会默认设置为”优化”)找到该应用进行设置,而且系统提示会引导用户选择优化,如图所示:

实现方式2(推荐):给应用添加权限REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,并使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,系统会弹出设置窗口,用户可以直接将该应用添加到白名单中,如下图所示:

核心代码:
在清单文件中添加权限:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS作为参数创建一个Intent,并以"package:com.example.xxx.xxx"的Uri形式将包名传入.
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:com.example.janiszhang.dozedemo3"));
startActivity(intent);
方案三:使用GCM(Google Cloud Messaging ),该方法不可行,不再赘述.
二.后台优化
优化点1.针对预览版,应用不再接收静态注册的CONNECTIVITY_ACTION广播.但是应用在前台时仍然能够监听到动态注册的CONNECTIVITY_CHANGE广播.
优化点2.应用程序不能发送或者接收ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO广播,这个优化会影响到所有应用,不只是针对预览版.
应对方案:
为了应对CONNECTIVITY_ACTION的变化所带来的影响,官方给出了两种缓解方案.
方案一:使用JobScheduler在无计量网络下调度网络任务.
核心代码:
Activity:
在使用JobInfo.Builder()创建JobInfo对象时,调用setRequiredNetworkType()方法,并将
JobInfo.NETWORK_TYPE_UNMETERED作为参数传递进去,这段代码的作用是当设备接入无计量网络时将调起MyJobService.
public static final int MY_BACKGROUND_JOB = 0;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static void scheduleJob(Context context) {
JobScheduler js =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(
MY_BACKGROUND_JOB,
new ComponentName(context, MyJobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
js.schedule(job);
}
Service:
当条件满足时(在该例中为接入无计量网络),MyJobService中的回调方法onStartJob()将被执行,在实际业务中,可以在这里执行网络任务.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService{
public static final String TAG = "MyJobService";
@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.i(TAG, "on start job: " + jobParameters.getJobId());
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
Log.i(TAG, "on stop job: " + jobParameters.getJobId());
return false;
}
}
注意:
需要在清单文件中为该Service设置权限:android.permission.BIND_JOB_SERVICE”
<service android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
方案二:在应用运行时监控网络连接
方式一:动态注册BroadcastReceiver,监听
“android.net.conn.CONNECTIVITY_CHANGE”
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"onReceive");
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
this.registerReceiver(broadcastReceiver,intentFilter);
方式二:使用ConnectivityManager
首先,使用NetworkRequest.Builder创建一个NetworkRequest对象,然后使用registerNetworkCallback()把这个NetworkRequest对象传递给系统.当网络条件被满足时,应用将收到一个回调去执行定义在ConnectivityManager.MetworkCallback类中的onAvailable()方法.
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
NetworkRequest networkRequest = builder.build();
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
Log.i(TAG, "onAvailable");
}
};
connectivityManager.registerNetworkCallback(networkRequest, networkCallback);
三.Data Saver
当用户在计量网络下启用数据节约功能时,系统会封锁后台数据的使用,运行在前台的应用也会尽量少的使用数据流量.用户可以使用白名单允许指定的应用在数据节约模式下使用后台数据.Android N开发者预览版中扩展了ConnectivityManager API的能力,向用户提供了查看和监控数据节约设置的接口.
检测Data Saver首选项的变化
动态注册监听ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED ("android.net.conn.RESTRICT_BACKGROUND_CHANGED")的广播接收者.
核心代码:
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Data Saver Changed");
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.RESTRICT_BACKGROUND_CHANGED");
this.registerReceiver(broadcastReceiver,intentFilter);
注意:必须使用动态注册的方式才能够监听到该广播,不能在清单文件中静态注册.
检查数据节约设置
ConnectivityManager的getResrictBackgroundStatus()方法的返回值如下:
RESTRICT_BACKGROUND_STATUS_DISABLED
禁用数据节约
RESTRICT_BACKGROUND_STATUS_ENABLED
启用数据节约
RESTRICT_BACKGROUND_STATUS_WHITELISTED
用户启用了数据节约,但是该应用在白名单中,故不受限制.
下面的示例代码来自官方文档,给出了使用ConnectivityManager.isActiveNetworkMetered()
和ConnectivityManager.getRestrictBackgroundStatus()来判断当前Data Saver设置状态的方法.

注意:这段代码目前还不能使用,RESTRICT_BACKGROUND_STATUS_ENABLED等三个状态值尚不可用.
使用adb命令进行测试
$ adb shell dumpsys netpolicy
生成一个报告,包括当前的全部后台网络限制的设置,白名单中的包的UID,其他已知包的网络限制.
$ adb shell cmd netpolicy
显示一个完整的网络策略管理命令列表.
$ adb shell cmd netpolicy set restrict-background <boolean>
启用或者禁用数据节约命令
$ adb shell cmd netpolicy add restrict-background-whitelist <UID>
将指定的包的UID加入白名单中
$ adb shell cmd netpolicy remove restrict-background-whitelist <UID>
从白名单中将指定包的UID移除
android 7.0 学习笔记(一)的更多相关文章
- DirectX 总结和DirectX 9.0 学习笔记
转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html DirectX 总结 DDS DirectXDraw Surfac ...
- 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移
不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...
- vue2.0学习笔记之路由(二)路由嵌套+动画
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue2.0学习笔记之路由(二)路由嵌套
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Android安装器学习笔记(一)
Android安装器学习笔记(一) 一.Android应用的四种安装方式: 1.通过系统应用PackageInstaller.apk进行安装,安装过程中会让用户确认 2.系统程序安装:在开机的时候自动 ...
- hdcms v5.7.0学习笔记
hdcms v5.7.0学习笔记 https://note.youdao.com/ynoteshare1/index.html?id=c404d63ac910eb15a440452f73d6a6db& ...
- dhtmlxgrid v3.0学习笔记
dhtmlxgrid v3.0学习笔记 分类: dhtmlx JavaScript2012-01-31 15:41 1744人阅读 评论(0) 收藏 举报 stylesheetdatecalendar ...
- OAuth 2.0学习笔记
文章目录 OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动. OAuth在"客户端&quo ...
- android cocos2d-x for Android安装和学习笔记(请用adt-bundle21.1或以上导入)
引用:http://weimingtom.iteye.com/blog/1483566 (20121108)注意:这篇文章用cdt编译ndk工程的内容已过时(现在可以用adt-bundle,避免配置繁 ...
随机推荐
- 应用程序框架实战三十:表现层及ASP.NET MVC介绍(一)
本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon G ...
- iOS开发之集成iOS9中的Core Spotlight Framework搜索App的内容
Spotlight在iOS9上做了一些新的改进, 也就是开放了一些新的API, 通过Core Spotlight Framework你可以在你的app中集成Spotlight.集成Spotlight的 ...
- MongoDB碎碎念
1. 如何从备份节点读取数据 默认是不允许的,会报如下错误: testReplSet:SECONDARY> show dbs --19T10:: E QUERY [thread1] Error: ...
- Percona博客学习总结
1. Upgrading to MySQL 5.7, focusing on temporal types 在MySQL 5.6.4中,对TIME, TIMESTAMP and DATETIME三种时 ...
- MyCAT简易入门
MyCAT是mysql中间件,前身是阿里大名鼎鼎的Cobar,Cobar在开源了一段时间后,不了了之.于是MyCAT扛起了这面大旗,在大数据时代,其重要性愈发彰显.这篇文章主要是MyCAT的入门部署. ...
- ASP.NET 5 使用 TestServer 进行单元测试
之前如果对 ASP.NET WebAPI 进行单元测试(HttpClient 发起请求,并且可调试 WebAPI),一般采用 Owin 的方式,具体参考:<开发笔记:用 Owin Host 实现 ...
- Visual Studio Code 智能提示文件
Visual Studio Code 开发前端和node智能提示 visual studio code 是一个很好的编辑器,可以用来编写前端代码和nodejs. 我很喜欢使用VSC,现在流行框架对VS ...
- Coreseek Windows下安装调试
由于项目需要全文检索,后面就去网上查了下资料,找到了Sphinx[中文是狮身人面像]这个全文检索引擎,听说挺好用的,不过没有中文分词.后面又去找了一下,找到了Coreseek,一款中文全文检索/搜索软 ...
- 【Bug】看不见的分隔符: Zero-width space
今天在调试一段代码的时候,有一个输入不能为空的库函数抛出了异常(为空就会抛出异常,就是这么傲娇).自己暗骂了自己一番,怎么这么大意,于是追溯源头,开始寻找输入控制的地方.但是当我找到时我惊呆了,我明明 ...
- C#基础知识三之new关键字
运算符 new一个Class时,new完成了两方面内容:一是调用new class命令来为实例在托管堆中分配内存;二是调用构造函数来实现对象初始化. 修饰符 显示隐藏从基类继承来的成员.虽然可以不用n ...