关于Android N的那些事

时间:2022-04-25
本文章向大家介绍关于Android N的那些事,主要内容包括假如我是产品经理、强化通知、Quick Setting、其他的一些(PM们可能不会关心的)feature、程序员的世界、瘦身计划、Java 8支持、其他应该注意的事项、禁止Native动态链接系统库、感想、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

今年3月,Google破天荒提前半年发布了Android N开发者预览版。当然,作为一个不合格的谷粉并没有第一时间体验安装,因为至今仍然能够回忆起来去年今日此门中(雾)兴冲冲刷了Android M Preview的时候发现各种crash就连微信也(不出所料得)中招时自己一脸懵逼的心情。当然,为自己的机智而庆幸并没有过多久,很快就有微信好友(当然也是纯纯的谷粉)反馈微信又双叒叕在Android新版本下crash了……好吧这次我们的时间很充裕,因为5个preview之后才会发布最终release版本。令人失望(咦)的是,我们的工程师在一天之内就修复了这个bug并且赶在当天6.3.15alpha版本发布之前修复并合入主线,辜负了Google的一片苦心。

痛定思痛,当天我就拎起来麒麟臂,在chrome的地址栏重重得敲入:http://developer.android.com/preview/overview.html,并且听说Google在北京举办了Android开发者大会的时候,屁颠屁颠得过去了。

假如我是产品经理

在这个『人人都是产品经理』的年代,作为程序员当然是敲得起代码,当得起经理(。。。)。如果我是产品经理,Android N的更新无非是以下三个点:

  • 1. 默认多窗口支持
  • 2. 强化通知,里边有你最喜欢的直接回复
  • 3. 没了…当然不是:Android Developer一笔带过的重磅feature:允许第三方应用在快速设置中添加自己的服务

默认多窗口支持

注意『默认』二字:这很重要,这很重要,这很重要。

Android M里边,系统允许应用在启动某Activity(对于PM(这里的PM统一指代产品经理)来说可以不严谨得理解成界面)时带上特殊参数,该应用可以在最近任务窗口中和主应用分开显示,即multi-tasking支持。当然,并没有多少应用鸟这个Android M中为数不多的新特性之一,因为效果实在是不明显。也有一定的原因是在这个大部分产品经理不会关注Android Developer的年代,这个非默认的特性实在不会引起他们的注意。

在Android N中,竟然直接支持了multi-window!虽然这个特性并不惊艳,在iOS、三星和华为等机型中早已支持,甚至在Android M中,也可以预埋了这个特性,并可以通过某些特殊方法开启(详见:http://www.androidauthority.com/multi-window-mode-android-6-0-marshmallow-647230)。然而,和iOS应用需要特殊声明才能支持多窗口的特性不同,Android N竟然默认支持了多窗口。这意味着任何一个应用,无论target-api是否是Android N,都支持从最近任务中长按应用标题栏进入多窗口模式。demo视频(需访问外国网站):https://www.youtube.com/watch?v=XrySr1KBKIs。

默认支持也就意味着除非特殊声明,任何应用都支持前述视屏所示效果,也就是说如果应用不针对这种模式进行完美适配,或者说用了绝对布局的话,你的应用就会。。。。呵呵呵。

当然,这种老掉牙的特性是不会引起高冷的PM的注意的,只会扔给开发狗交给我们去适配。那好吧,来一个one more thing刺激一下你:

在Android N中,将支持分屏情况下drag and drop,让这个4.0开始就支持faeture焕发了新生。这也就意味着你可以将一个应用内、甚至不同应用间的分屏情况将一个分屏幕控件拖拽到另外一个分屏幕。也许可以用来拖拽图片快速发图,或者。。随便你想干什么。

当然,从开发狗的角度来说,这里有一点安全隐患:如果通过拖拽将数据传递过来,你甚至不知道来源是什么。但是想想也是,毕竟用的和粘贴板一样的接口,还能指望什么呢?

另外,作为分屏的一种特殊形式,画中画(picture in picture)也得到了相应的支持。不过据Google的工程师说,画中画模式主推Android TV中应用(也许Google认为在手持设备上场景不足)。不过whatever,现在很多功能已经可以通过浮窗接口实现。画中画对于做视频应用或者有视频支持功能的应用非常有帮助。

此外,给程序员朋友们几个小贴士

  • 1. 虽然分屏状态下两个应用都可见,但是对于非Focus状态的应用当前是处于onStop状态的,也就是说,并没有实际在运行中。原本onStop的时候应用应该是不可见,但是现在可见了。。。原本的一些恶心逻辑注意修改下。
  • 虽然分屏状态下的应用不会double内存占用,但是内存占用肯定会比正常状态大,注意分屏模式下即时释放内存。
  • 2. 适配好你的程序,该加scroll的地方加scroll。当然,如果原本的你的程序就已经针对多尺寸屏幕有了处理,就已经完美适配了这个模式

强化通知

通知栏一直是Android引以为豪的方面。相对于iOS的通知栏来说,Android的通知栏具有几乎完爆的功能:自定义控件,自定义Action,可以定义下来拓展的控件……除了快速回复。

在这之前,先上一段Android N新版本的通知栏和快速设置栏,至于为什么放视频,嗯。。。因为我觉得很好看:

如今,这一点已经被Google迎头赶上,并且体验绝不亚于iOS,甚至好很多。废话少说,来一个demo apk……还是算了,反正PM不会用Android手机当主用机。还是直接来一段视频好了:

当然,如果一次来了多条消息并且都不是一个会话中,快速回复也是毫无压力(视频数量限制,有需要的同学脑补下,或者自己下个Sample跑一下)

这个新特性简而言之就是满足了快速回复的一切需求,也许从此再也不用担心沉浸式阅读时需要跳出回复消息这种伤害体验的情况。

当然了,除了快速回复,还有根据应用归档通知,这无疑是一个大杀器:

同时,这里需要同时提醒PM和开发同学的是:之前的setView被拿掉了,这就意味着如果你的应用中使用了完全自定义的控件,到了跟系统整体完全不搭的境界,那么赶紧找一个好的设计师重新设计符合规范的通知样式。对于这一个看似自废武功的调整,我举双手赞成。

Quick Setting

说到这个话题,真的很想用标准朋友圈标题:《Android Developer一定不会告诉你的事!怒转!》,嗯,希望朋友圈的产品经理和运营们不要打我。

几乎所有人刷了Android N之后最大的感触只有三个:1. 这和Android 6.0有毛区别!2. 通知栏好看了 3. 通知栏拉到一半就会有快捷通知,真是懒人福利。(参考前面发过的视频)

嗯。。好吧。。。

99%的人不会发现的秘密:Android N中允许第三方应用程序向快速设置栏添加自己的快速部件。这个可比传统Widget方便快捷得多。当然,这个入口千万不要做特别重的操作。同时,区别于iOS的通知栏部件,你的入口将和WiFi开启、GPS设置等系统设置同等级。当然,这一切的一切前提是用户将你的QuickSetting部件拖拽到了快捷入口的位置。我写了一个小sample:

虽然接口还没有出现在Android Developer,但是已经有离线文档可以下载(http://developer.android.com/preview/setup-sdk.html#docs-dl),里边就有Tile API。

好吧。。。其实我写了一个小sample,点击这个网址:https://github.com/yexuan910812/android_n_tile 就可以下崽下载了骚年。

你咋不上天呢!

总之,我已经看到PM们扬起的嘴角,并且计划着如何强奸这个快速设置栏(幸好有需要用户拖拽设置这一条)。不过还是希望各位维护这个生态啊亲们!

其他的一些(PM们可能不会关心的)feature

  • 1. 号码阻止: 全局的电话号码黑名单,如果你是做电话相关的应用,可以使用这个API进行全局黑名单控制,并且为这个黑名单贡献自己的一份力量(加油)。
  • 2. 允许第三方通化应用自定义Call Screen:是的,从现在开始再也不用做什么hack就可以将自己的通话应用彻彻底底得取代系统电话了。
  • 更加多元的多语言支持:就像papi酱视频里的Sophie,越来越多的中国人已经可以(自以为)熟练得掌握了第二语言、第三语言。Android N中,用户可以在系统语言选择中选择多种语言,应用程序也可以通过全新API获取用户使用语言的列表,而非单一语种。
  • 3. 直接启动(Directly Boot):这个名字看上去很碉堡,但是实际上大部分应用可能用不上。实际上就是系统在启动了但是还在锁屏状态时,允许特定声明的程序快速启动并访问加密文件数据。希望不要变成应用抢占运行状态的一个接口,大家高抬贵手,有点节操。
  • 4. 快捷方式强化:以后终于知道自己的app有没有已经设置过某个快捷方式,并且能够更加方便得管理它们,而不用傻傻得先删除快捷方式再添加一个。
  • 5. 辅助功能强化:今后将会支持辅助功能手势操作。不过。。。求PM放过这个原本给残障人士使用的feature。现在辅助功能都被用来做抢红包插件了。

好了,到这里为止,对于一部分PM来说,这个文章就结束了,可以去准备需求文档了(去吧,皮卡丘)!

当然,我们非常欢迎热爱探索的PM们继续与我们愉♂快♂讨♂论♂程序员的世界。

程序员的世界

在这个章节,我不会具体去讲刚刚提到的那些feature该怎么实现,几乎所有的feature(除了我已经提供的快速设置API之外)都有相应的Sample(http://developer.android.com/preview/samples.html)。

Doze模式更加强大

Doze模式是Android M中推出的一个省电模式,当手机灭屏一段时间之后(一般而言是十几甚至几十分钟),设备会进入Doze模式。在Doze模式中,系统会通过减少应用CPU调用以及禁止掉网络连接,达到省电的目的。与此类似还有一种机制叫App Standby,顾名思义只要你的应用在一段时间内没有操作并且不在前台,比如主动弹一个Notification之类就会把你暂时冻结掉,Standby嘛,一边玩去。嗯。。。好像哪里不太对。

『这不是逼着应用疯狂弹通知么!』

所以,Android N中,Doze模式变得更加强大且不容易被突破了。只要手机灭屏一小段时间,并且当前不在充电状态,就会进入Doze模式。在Doze模式下,你的应用就几乎可以肯定拿不到WakeLock,甚至于普通的AlarmManager也已经无法使用。

『可是我的应用是一个闹钟应用/有定时提醒功能,怎么办!』

这种情况倒也好办,要么就是让用户将自己的的应用加入白名单,要么就主动申请权限:

An app holding the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission can trigger a system dialog to let the user add the app to the whitelist directly, without going to settings. The app fires aACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS Intent to trigger the dialog

当然,对于大部分应用来说,影响最大的就是网络方面。应用灭屏了就不能联网,IM应用们该怎么办!

对此,Google给了两个解决办法:

  • 1. 应用可以通过PowerManager.isIgnoringBatteryOptimizations(java.lang.String)接口查询到自己是不是在白名单内,如果不在,则通过ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS这个动作引导用户进入系统设置添加自己为白名单。
  • 2. 应用使用GCM服务。即使是Doze模式,GCM依然可以有效运行。但是,中国大陆的Google服务。。。你懂得。各位可以通过在这里:https://developers.google.com/cloud-messaging/ 查看如何让你的应用支持GCM。毕竟即使Google不入华,让歪果仁或者港澳台同胞们用的时候也要保证你的应用的可用性。

对了,顺便安利一个开源的分析工具:耗电记录(https://github.com/google/battery-historian)。这个也是Google官方的耗电分析工具,可以方便得查看系统内耗电占用,甚至某个应用的某个组件耗电。像这样:

一目了然。

瘦身计划

这个并不是说Android机器人变瘦了,实际上,它还是那个胖胖的样子:

额。。。天天吃苹果当然会胖。。。

言归正传,实际上,自Android L开始,Google就在强力推荐应用使用JobScheduler代替其他方式来进行后台服务,甚至在2014年的Google I/O大会上将这个新接口放在了大会上演示,这是其他接口都不曾有的待遇。当时我甚至还在朋友圈声称这个将会是以后实现后台服务的唯一方式。

可是现实重重得打了我的脸,这个接口的使用率并不理想。终于,在Android N上,Google重提了这个接口,并进行了大规模的强化。今后,如果有需要后台处理的数据、或者特定情况的后台处理。举个例子,如果你需要在用户充电并且不在数据网络的时候处理一些逻辑,你就可以这样:

1.public static final int MY_BACKGROUND_JOB = 0;
2....
3.public static void scheduleJob(Context context) {
4.  JobScheduler js =
5.      (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
6.  JobInfo job = new JobInfo.Builder(
7.    MY_BACKGROUND_JOB,
8.    new ComponentName(context, MyJobService.class))
9.      .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
10.      .setRequiresCharging(true)
11.      .build();
12.  js.schedule(job);
13.}

『何必这样,我写一个BroadcastReceiver监听CONNECTIVITY_ACTION然后处理不就行了,naive!』

科科。

为了防止这个没有节操的事情发生,Google在Android N中拿掉了这三个广播

  • CONNECTIVITY_ACTION:网络变化
  • ACTION_NEW_PICTURE:添加新图片
  • ACTION_NEW_VIDEO:添加新视频

这也是我非常佩服Google的一个点,敢于做减法。当然,留下的坑就多了,比如CONNECTIVITY_ACTION,很多应用(包括微信)都会监听。今后需要使用JobScheduler实现相同的逻辑了。JobScheduler有非常多的好处,他会根据用户当前设备的情况,比如当前RAM、电量、模式、是否应用在前台等等,决定是否执行该逻辑。你也不希望自己的程序变成用户手机变卡的罪魁祸首,从而让用户怒删,对吧?

当然了,不允许监听CONNECTIVITY_ACTION针对的是静态注册的BroadcastReceiver,如果是动态注册的BroadcastReceiver则并不会受到影响。

Java 8支持

早在前年开始研究Annotation的时候,就在感慨为什么Android一直不支持Java 8,即使现在Java 9都快出了。终于的终于,Android从N版本开始支持Java 8的编译,前提是要在Gradle文件中显式声明使用Jack编译器。

这个Jack是什么鬼呢?简单来说,传统的编译工具链是将java代码通过javac编译成.class文件,再通过dx编译成.dex。也就是酱紫的:

1.javac (.java --> .class) --> dx (.class --> .dex)

而Jack则是一条龙服务,中间不需要经过其他工具或者命令,一条命令就可以将.java文件编译成.jack从而编程.dex:

1.Jack (.java --> .jack --> .dex)

使用Jack非常简单,gradle配置即可

1.android {
2.  ...
3.  defaultConfig {
4.    ...
5.    jackOptions {
6.      enabled true
7.    }
8.  }
9.  compileOptions {
10.    sourceCompatibility JavaVersion.VERSION_1_8
11.    targetCompatibility JavaVersion.VERSION_1_8
12.  }
13.}

不过,Android N版本的Java 8特性并没有支持全,不过主流的feature已经支持,包括:

  • 1. 定义接口默认实现方法
  • 2. Lamda表达式支持(喜欢语法糖的同学的福利)
  • 3. Repeatable annotations。这个已经可以新开一篇文章了,改天有空给大家介绍。
  • 4. Method Reference。这个实话实说我并不是太了解,也是语法糖一种。感兴趣的同学可以看看这个链接:https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

但是现在还没有支持一个很重要的特性:Stream。但是现在还在Preview阶段,比如刚刚的第四条Method Reference就是Preview2支持的,可以期待下release中是否会支持(最新消息:已经支持java.util.stream接口,棒棒的!)。

此外还需要注意两点:

  • 1. Lamda表达式本质上回生成匿名类,在性能敏感的模块慎用
  • 2. 由于Jack编译器不会产生.class中间文件,因此在.class上做trick的一些库或者项目可能就会失效或者出问题。因此在使用之前,一定要好好测试

其他应该注意的事项

  • 1. Data Saver:乍看上去是一个数据存储的API,感觉很兴奋,结果点开一看是流量节省。。。好吧,博大精深的英文。从Android N开始,系统层级支持用户针对每一个应用添加自己的流量控制限制。今后开发的时候需要先通过ConnectivityManager.getRestrictBackgroundStatus()接口获取本应用流量控制情况。
  • 2. Key Attestation:对于绝大部分应用并不需要仔细研究的feature,甚至可以当做不存在,但是对于我个人所做的生物认证项目来说,可谓是非常重要的feature。这个要是详细解释可以后面KM详细介绍,涉及TEE、密钥体系、指纹模块、密钥信任链、签名算法等等方面。
  • 3. 针对文件目录或类型申请权限:实话实说,这个也算是一个很重要的feature。从Android 6.0开始,如果需要使用存储空间,包括读写,需要动态申请权限。然而对于大部分应用来说,都需要申请这个权限,而且一旦用户允许,应用就可以为所欲为。因此,Android N中允许应用声明仅仅授权某个文件夹或者文件类型的存储。

禁止Native动态链接系统库

这一点Android Developer没有讲,至少暂时没有讲(截止到2016年4月22日) 还记得之前我说的微信升级到N会Crash么?实际上就是这个原因。

自Android N开始,系统将禁止第三方应用so文件链接系统lib库,包括并不限于libcrypto.so,libandroidruntime.so,libicu.so,libbinder.so。动态链接上述库轻则弹Toast提示,重则直接crash。Android此举原因大家可以讨论,但是事实已然如此,尽管对于大多数应用而言并无妨碍,但是对于类似微信这种在底层做了大量优化和调用的应用来说还是很伤脑筋的。至于解决方法。。。暂时只想到了改用静态链接,对于包的大小增加并不会太大。如果有更好的方法,欢迎大家讨论。

实话实说,这次Android N的更新对于我们程序员来说还是干货满满,满到我有些话想说。

感想

前面说的都是Android N,现在终于开始可以感慨下。实际上,从Android L开始,Google就已经开始反省自己过分开放的策略。原本后台任务满天飞的系统,现在渐渐地被控制得有序起来。比如Android L发布的JobScheduler,Android M发布的Doze模式和APP Standby,Android N的Doze加强以及瘦身计划,无一不是在限制系统的后台任务数量以及计算强度。亡羊补牢,不知是否为时未晚。

同时,关于设计方面,Material Design推出已经接近两年,尽管有很多应用已经适配,但是包括微信、Facebook、Twitter在内的很多主流应用仍然在坚持使用自己的设计语言。诚然,这里有可以理解的风格统一之考虑,MD本身也有很多缺陷,但是我们很高兴能看到的是MD自身在不断的调整优化,越来越成为一个漂亮的并且优秀的设计,其层次感以及灵动性无处不撩拨着用户的神经。所以,是不是仍有很多人抱着2.3混乱局面或者4.0不那么优秀的Android Design来臆测Android的设计风格?也许用着iOS的诸位对于Android的印象依然是2.x时代那个臃肿的、落后的以及。。额,难看的形象。就像这样:

这是2011年左右,毕竟是5年前,那时iPhone上的应用也并不好看,不过还是比Android要强很多。但是现在Android应用已经长这样了(系统自带Google应用):

个人认为比Apple的设计。。。(为了防止引战)至少并不差吧。

其实,这里说了这么多,精通读心术的我也能想到大家心中的疑问:碎片化如此的Android市场,我们就算是适配了N的特性,大家也没有这么快用上,还是歇着吧。嗯,关于碎片化,首先,Android目前版本分布是酱紫的(来自Google官方,链接http://developer.android.com/about/dashboards/index.html)

也就是说,至少截止到今天(2016年4月22日),接近40%的人的设备已经是Android 5.0以及以上,按照如今厂商发货的速度,目测一两个月内比例就会过半。平心而论,针对这半数用户,有几家应用做到了完美支持?无论是UI还是具体功能,大部分应用应该都是在4.x,甚至2.x版本的基础上填坑,fix新版本上的crash,有几家用到了新的特性,新的feature呢?而大家以为占主流的2.3系统,实际上已经不足3%,是不是仍然有很多应用的target api仍然是4.x以下?

而且Google现在很鸡贼啊(这一点谢老大提醒),一年发布一个大版本,前年5.0,去年6.0,今年7.0,你说Google都到7.0了你还好意思连5.0都不上?这一点实际上对于解决碎片化是非常有帮助的。

面对占市场份额近7成的Android设备本身并不需要救助,一直都没有放弃发展,欣欣向荣。相反,需要救助的是我们在Android上的应用,低质量的应用实现已经威胁到了我们自身。不仅仅是要依靠产品经理,作为程序员,我们也要学会自救。

感谢!