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,避免配置繁 ...
随机推荐
- The network bridge on device VMnet0 is not running
The network bridge on device VMnet0 is not running. The virtual machine will not be able to communic ...
- 窥探Swift之数组与字典
说到数组和字典,只要是编过程的小伙伴并不陌生.在Swift中的数组与字典也有着一些让人眼前一亮的特性,今天的博客就来窥探一下Swift中的Array和Dictionary.还是沿袭之前的风格,在介绍S ...
- 【知识积累】SBT+Scala+MySQL的Demo
一.背景 由于项目需要,需要在Sbt+Scala项目中连接MySQL数据库.由于之前使用Maven+Java进行依赖管理偏多,在Sbt+Scala方面也在不断进行摸索,特此记录,作为小模块知识的积累. ...
- 关于C#静态变量初始化问题
关于这个静态变量,平时自己没有太认真的去认识.最近调项目的bug,让我重新认识了静态变量的特点. 其实,我们一直都在说:静态变量只在类第一次初始化的时候进行初始化,以后都不初始化. 很简单的一句话,但 ...
- 微信公众账号 token 验证失败 解决办法
问题:微信公众账号 开发过程中配置 token 提示 验证失败 如下图: 点击修改配置: 填写相关url与token(自定义):点击提交,会出现 出现这种情况,主要是对相关参数不熟悉,要了解url与 ...
- MVC中路由
篇目 介绍 路线的性质 了解缺省路由 用一个例子的路由 结论 介绍 本文介绍了MVC中的路由.如何执行的路线是由路由引擎和如何定义的URL路由. ASP.NET MVC的路由是一个模式匹配系统,负责传 ...
- healthMonitoring与运行状况监视
配置针对应用程序的运行状况监视的一个服务 配置节内容比以往的较为复杂,如下 <healthMonitoring Enabled="true|false" heartbeatI ...
- js+html5双人五子棋(源码下载)
代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" c ...
- [WCF编程]9.性能与限流
一.性能概述 WCF服务的性能取决于很多因素.出了CPU.RAM和网络性能等常见的因素外,实例上下文模式.并发模式.数据契约的设计或使用的绑定等与WCF有关的因素都起着重要的作用. 实例上下文模式用来 ...
- ArcGIS标注
关于ArcGIS的标注问题整理. 要求: 保留小数点后三位, 若原数据小于1(.12345),对小数加0显示 分行显示两个字段值 解决: 右击图层 -- ”properties“(属性) 中选择“la ...