- Thanks for saving me. - Don’t waster it. Don’t waste your life.
全文思维导图
问题产生的背景
Android 是一个不断更新演进的系统,犹记得自己最初一部手机是在百脑汇购买的Samsung I9001,Android2.3系统 —— GINGERBREAD,现在几乎听不到这个词了。
在版本迭代的过程中,Google会不断引入新的接口、并且为App增加新的限制。一方面是由于硬件和软件技术更新,为用户&开发者提供了更强大的能力,比如陀螺仪、人脸识别等;另一方面是不断演进和强化的安全隐私需求。
对于开发者而言,自然希望自己的App的受众范围越大越好,不仅是当前市面上已有的系统版本,甚至对于未来将要推出的版本也一并可以兼容。由此便引出了以下三个问题,这三个问题是开发者必须解决的,如何解决呢?借助Google提供的能力来完成。
- 开发者写的代码是基于哪个版本的API —— 对应的是compileSdkVersion
- 开发者提供的apk最低可以运行在什么版本的系统 —— 对应的是minSdkVersion
- 开发者提供的apk是以哪个系统版本作为目标来开发的 —— 对应的是targetSdkVersion
解决系统版本适配和限制的问题
接下来我们逐个分析以上三个需求。
开发者写的代码是基于哪个版本的API:compileSdkVersion
首先作为开发者,在编写代码时一定会调用系统API,而不同系统版本提供的API是有区别的,比如有的接口在某一版本之前一直是private
的,直到某一版本时被开发成public
。开发者如何告诉编译器自己写的代码是基于哪个版本呢?这时就要用到compileSdkVersion
了。
使用compileSdkVersion,最直接的目的是在编译过程中使用指定版本的SDK进行编译,在此之前,在编写代码的时候,如果调用了高于compileSdkVersion的API,编译器会给出出错提示。需要注意的是compileSdkVersion只存在于编译前的阶段,在编译的生成物apk里是看不到任何与其相关信息的。
另一个限制是,compileSdkVersion需要与Support Library Version首位匹配。比如你用的Support Library是23.1.1
版本的,那么compileSdkVersion一定要是23
。
对compileSdkVersion的使用建议是,总使用当前最新的compileSdkVersion,一方面可以调用最新的系统接口,防止未来某一时刻老接口被废弃后不得不修改代码,变被动为主动。另一方面能够享受到最新SDK带来的编译速度提升。
开发者提供的apk最低可以运行在什么版本的系统:minSdkVersion
“能够运行在市面上所有的系统版本上”是开发者一个美好的愿望,但实际上,随着Google不断推陈出新,老版本在市场上的占有量越来越小。可以参考Google的Dashboard,它统计的规则是一周内访问Google Play的系统版本分布,可以看到4.4及更早版本加起来已经不到10%。
当市面上老版本占有量趋近于无的时候,我们就不必为适配老版本写专门的代码了,这样能够减少适配工作,从而把开发者宝贵的精力投入到更有价值的新功能实现上。
在项目中我们用minSdkVersion来说明自己的app最低可以运行在什么版本的系统上。在Google Play、vivo应用商店、游戏中心等应用分发系统中会检测本机版本,若低于应用声明的minSdkVersion就会提示用户,甚至直接拒绝安装,因为即使装上也无法使用。
在边写代码过程中,你可以使用任何不低于compileSdkVersion的API,但是当你使用的API高于minSdkVersion时,就必须在代码里显示地进行版本判断,否则编译器/Lint工具会提示你。
另一个限制是,主工程的minSdkVerison不低于其所有依赖库的版本。举例说明:如果项目里同时使用了如下依赖
- support(min=4)
- glide(min=7)
- google play(min=9)
那么主工程的minSdkVersion就应当为9。该限制可以违背,需要人为保证安全,设置方法为在AndroidManifest里声明tools:overrideLibrary。这样做是有风险的,还是以上例说明,你不能在4的系统版本上调用glide接口,否则一定会报错。必须在代码逻辑里面保证这一点。
对minSdkVersion的使用建议是,开发过程中若使用到了高于minSdkVersion的API,一定要进设备版本判断;其次在声明这个属性时,参考你应用的目标用户群体系统版本分布,这是一个权衡的过程,你希望应用的覆盖面更广,还是应用的特性更新。
开发者提供的apk是以哪个系统版本作为目标来开发的:targetSdkVersion
到了本文要介绍的三个SdkVersion
中最有趣的部分——targetSdkVersion。考虑这样一种需求,开发者基于5.0的API开发(即compileSdkVersion=5.0),当app发布时候,Google已经发布了Android 6.0,此时当然希望原App不需要任何改动就可以运行在6.0系统的手机上,通常是Google在6.0的SDK里做了这个兼容。
拿6.0(API=23)的权限限制举例,Google希望在6.0开始控制App滥用权限的情况,列出了一些必须动态申请的敏感权限。这对手机用户而言是百利而无一害的事,而对开发者而言,如果不能及时修改代码,就会面临App不可用甚至无法上架Google Play的情况下。假如你作为一名应用开发者,日常有1000w的活跃用户,而由于手机系统升级,这1000w用户无法使用之前的App了;而你又恰好在度假中,无法及时修改更新,这将是一个灾难。
针对上面的具体例子,可以通过在gradle文件里声明targetSdkVersion=21
来告诉操作系统,App的代码是面向Android 5.0系统编写的,没有考虑动态权限的问题。这样的APK即使被安装在系统版本为6.0的手机上,也会依照之前的行为来运行,即在安装时申请所有必须权限,而非运行时动态申请。
AOSP里有很多
getApplicationInfo().targetSdkVersion < Buid.XXXX
样式的代码,就是用来判断targetSdkVersion
的
使用targetSdkVersion还有一个好处,你可以使用更高版本的API,且不需要关注行为变更。
也许你会觉得,作为一名顽固恋旧的开发者,我就不要升级targetSdkVersion,Google好像也拿我没有办法。事实上并非如此,如果targetSdkVersion过低的话,Google、手机厂商都有权利拒绝上架你的App。
对开发者选择targetSdkVersion的建议是,尽量保持最新的版本,以防应用市场把你的应用下架。升级targetSdkVersion后一定要进行针对性的测试。
学以致用
了解这几个版本的含义之后,就要看看如何应用了。通常我们的项目是用gradle管理依赖的,我们需要在根project中声明这几个版本。
Gradle配置
1 | android { |
android
标签下的配置只会在编译期间生效,defaultConfig
标签下的设置会打入最终的Apk中。
版本高低关系
通常的版本关系是min<=target<=compile,最理想的状态则是min<=target==compile,其中compile时刻保持最新。
targetSdkVersion适配指南
从Android 6.0开始,每一个版本升级,Google都会发布适配指南,将其整理成思维导图。