关于 Android N 那些你不知道的事儿

时间:2022-05-05
本文章向大家介绍关于 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.15 alpha 版本发布之前修复并合入主线,辜负了 Google 的一片苦心。

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

假如我是产品经理

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

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

默认多窗口支持

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

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

在 Android N 中,竟然直接支持了 multi-window!虽然这个特性并不惊艳,在 iOS 和三星的机型中早已支持,甚至在 Android M 中,也可以预埋了这个特性,并可以通过某些特殊方法开启。然而,和 iOS 应用需要特殊声明才能支持多窗口的特性不同,Android N竟然默认支持了多窗口。这意味着任何一个应用,无论 target-api 是否是 Android N,都支持从最近任务中长按应用标题栏进入多窗口模式。这里是个 Demo。

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

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

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

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

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

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

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

强化通知

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

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

如今,这一点已经被 Google 迎头赶上,并且体验绝不亚于 iOS,甚至好很多。

当然,如果一次来了多条消息并且都不是一个会话中,快速回复也是毫无压力:

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

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

同时,这里需要同时提醒 PM 和开发同学的是:如果真的需要在通知上设置自定义控件,请调用 DecoratedCustomViewStyle()。它会让你的自定义控件在通知栏显得更加和谐。Sample:

Notification noti = new Notification.Builder()
    .setSmallIcon(R.drawable.ic_stat_player)
    .setLargeIcon(albumArtBitmap))
    .setCustomContentView(contentView);
    .setStyle(new Notification.DecoratedCustomViewStyle())
    .build();

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

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

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

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

  • 应用可以通过PowerManager.isIgnoringBatteryOptimizations(java.lang.String)接口查询到自己是不是在白名单内,如果不在,则通过ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS这个动作引导用户进入系统设置添加自己为白名单。
  • 应用使用 GCM 服务。即使是 Doze 模式,GCM 依然可以有效运行。但是,中国大陆的 Google 服务。。。你懂得。当然了,根据各种小道消息,这个服务很可能近期会可用。各位可以通过这个链接 (https://developers.google.com/cloud-messaging/ ) 让你的应用支持 GCM。毕竟即使 Google 不入华,让歪果仁们用的时候也要保证你的应用的可用性。

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

一目了然。

瘦身计划

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

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

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

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

public static final int MY_BACKGROUND_JOB = 0;
...
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)
      .setRequiresCharging(true)
      .build();
  js.schedule(job);
}

『何必这样,我写一个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。也就是酱紫的:

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

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

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

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

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

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

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

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

此外还需要注意两点:

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

其他应该注意的事项

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

禁止 Native 动态链接系统库

这一点 Android Developer 没有讲,至少暂时没有讲

还记得之前我说的微信升级到 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 应用已经长这样了(系统自带应用):

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

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

也就是说,至少截止到今天,接近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 上的应用,低质量的应用实现已经威胁到了我们自身。不仅仅是要依靠产品经理,作为程序员,我们也要学会自救。

感谢!


本文系腾讯Bugly独家内容,转载请在文章开头显眼处注明作者和出处“腾讯Bugly(http://bugly.qq.com)”