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移除

时间: 2024-10-05 21:52:17

android 7.0 学习笔记(一)的相关文章

Android中ActivityManager学习笔记(3)

在之前写过2篇关于ActivityManager的文章,如下所示.都是通过实战去了解里面的信息,现在感觉太繁琐,所以做一个api介绍比较详细.就像之前学习Telephony那样,对整体了解整个ActivityManager很有用. Android中ActivityManager学习笔记(1)-MemoryInfo Android中ActivityManager学习笔记(2)-RunningServiceInfo ActivityManager android.app包,含有6个内部类: Memo

Android自定义View学习笔记04

Android自定义View学习笔记04 好长时间没有写相关的博客了,前几周在帮学姐做毕设,所以博客方面有些耽误.过程中写了一个类似wp的磁贴的view,想再写个配套的layout,所以昨天看了一下自定义viewGroup的相关知识-晚上睡觉想了一下可行性不是很高-代码量还不如直接自己在xml上写来得快,速度上也是个问题.今天看了一下张鸿洋老师的Android 自定义View (三) 圆环交替 等待效果这篇博文,再加上前一段时间看到的一幅图,结合之前写的一个圆形imageView的实现博文And

Android Socket编程学习笔记

通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务. 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定. 在java中,Socke

android 浮动窗口学习笔记及个人理解(仿360手机助手)

非常感谢原文作者 http://blog.csdn.net/guolin_blog/article/details/8689140 经自己理解 程序运行界面如下图: 1.程序入口界面 2.小浮动窗口 3.大浮动窗口 由上图可看出,可以看出我们基本需要: 1.一个主Activity 2.小浮动窗口view界面 3.大浮动窗口view界面 对于浮动窗口的管理我们还需要 4.一个Service(在后台监控管理浮动窗口的状态) 5.窗口管理类(创建/消除浮动窗口) 代码: package com.ww.

Android自定义view学习笔记02

Android自定义view学习笔记02 本文代码来自于张鸿洋老师的博客之Android 自定义View (二) 进阶 学习笔记,对代码进行些许修改,并补充一些在coding过程中遇到的问题.学习的新东西. 相关代码 //CustomImageView.java package mmrx.com.myuserdefinedview.textview; import android.content.Context; import android.content.res.TypedArray; im

微软企业库5.0学习笔记(10)ASP.NET模块依赖注入

您可以使用HTTP模块,一个到ASP.NET HttpApplicationState类的扩展,在Global.asax编写代码强制ASP.NET在每一个页面请求时自动注入依赖的对象,就像在ASP.NET Web窗体应用程序中讨论的一样. 下列方法显示了一个合适的方法能够获取PreRequestHandlerExecute事件将它自己注入到ASP.NET的执行流水线,在每个页面请求中通过容器的BuildUp方法运行Http模块,并获取OnPageInitComplete事件.当OnPageIni

一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移

不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.supervisor.mysql环境搭建搭建好了.net core linux的相关环境,今天就来说说ef core相关的配置及迁移: 简介: Entity Framework(以下简称EF) 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案,EF Core是Entity

[Android游戏开发学习笔记]View和SurfaceView

本文为阅读http://blog.csdn.net/xiaominghimi/article/details/6089594的笔记. 在Android游戏中充当主要角色的,除了控制类就是显示类.而在Android中涉及到显示的是View类,及继承自它的SurfaceView类和SurfaceView的其他子类等. 这里先只说View和SurfaceView.SurfaceView的直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及CAmera摄像头一般均使用Su

Swift 2.0学习笔记(Day5)——我所知道的标识符和关键字

Swift 2.0学习笔记(Day5)--我所知道的标识符和关键字   原创文章,欢迎转载.转载请注明:关东升的博客 好多计算机语言都有标识符和关键字,一直没有好好的总结,就是这样的用着,现在小小的整理一下Swift中的标识符和关键字. 什么是标识符呢? 标识符就是给变量.常量.方法.函数.枚举.结构体.类.协议等由开发人员指定的名字. 其实,构成标识符的字母是有一定规范的,Swift中命名规则是: 区分大小写,Myname与myname是两个不同的标识符: 标识符首字符可以以下划线(_)或者字