https://juejin.im/post/5aae7200f265da23a3350317
彻底弄清support支持库,以及v4 v7重复依赖问题深究
一般在Android Studio的项目配置build.gradle里,有3个SDKversion,即:compileSdkVersion、minSdkVersion和targetSDKVersion,这些version指的并不是我们使用的SDK的版本,而是我们使用的Android平台(Android Platform)的版本,即API Level,是一个整数,指的是我们使用的框架(Framework)的版本,也就是SDK中platforms文件夹下各个level中的android.jar的版本,API Level就是android.jar(Framework开发包、一套接口)的代号。一般Android系统版本表示为Android2.3.3,在系统内部会记录它的API level,不同的Android系统版本对应不同的API Level(最后有详细介绍),但是有时候Android系统版本更新了,其对应的API Level并不一定更新,所以他们是多对一的关系,Android系统版本是面向用户,API Level是面向开发者。
compileSDKVersion
各个其中compileSDKVersion是AS编译APK使用的API Level,一般修改了compileSDKVersion不会改变运行时的行为,可能会出现一些编译的警告和错误, compileSDKVersion并不会被包含到apk中,它只是纯粹在编译时候使用。一般推荐使用最新的版本进行编译,避免使用废弃的API,同时Support Library 也需要相应的编译SDK版本来支持,我们经常使用的Support Library的依赖语句形式如下:compile 'com.android.support:support-v4:24.2.1
,其格式是compile jar文件组(group/命名空间):jar文件名(name):jar文件版本(version)
,这里jar文件版本需要和compileSDKVersion保持一致。
关于Support Library参考:http://www.jianshu.com/p/f5f9a4fd22e8
简单总结一下:Support Library提供了一些低版本API Level中不存在的API接口,如App配置为minSdkVersion=9,targetSdkVersion=23,在程序里使用了android 3.0 (API level 11)提供的ActionBar类,使用compileSdkVersion=23成功编译出apk,App在android 3.0以及以上的版本上正常运行,但在API 9的版本上回crash,因为找不到ActionBar类,因为旧版本上没有新版本里新增的类。为了避免使用了最新功能开发的app只能在最新系统的设备上运行的尴尬,官方把新版系统framework中新增加的接口提出来放到了Android Support Library(支持包)中,开发者在遇到上面的情况时,就可以使用支持包中具有同样功能的ActionBar类,这个支持包会打包进apk里,这样使用了新版本系统上功能的App也可以向后兼容以前的老系统版本设备了。
Support V4和V7是使用最广泛的2个包,其实是包的集合,v4在后期被拆分为几个子包,v7一开始就是不同的子包,这样可以减少apk的体积,另外在编译的时候使用代码混淆ProGuard,除了可以混淆源代码外,还可以移除依赖支持库中没有使用到的类,达到apk瘦身的效果。
7/22/2017 7:58:00 AM
App编译时用的android sdk(android.jar)不会打包进我们的apk中。因为apk编码是使用android.jar中的接口就是android设备里系统框架层(framework)对外提供的接口。
minSdkVersion
minSdkVersion表示应用兼容的最低API Level,如果手机的Android系统的API level低于该值,则该手机无法安装此apk。API level在选择时应该兼顾市场上大部分的手机Android系统版本。原则上,如果我们在编写程序时使用了最低API Level里没有的API接口,那么程序就会报错,我们通过使用Support Library来提供最低API Level里没有的API接口,但注意,这些Support Library本身也使用了minSdkVersion,那么我们工程里的minSdkVersion版本不能低于Support Library使用的minSdkVersion版本。例如我们用了3个库,他们的minSdkVersion分别是4、7、9,那么我们工程的minSdkVersion至少应该是9,否则编译通不过。如果minSdkVersion没有设置的话,默认为1。
targetSdkVersion
targetSdkVersion,谷歌给出的解释是:
targetSdkVersion is the main way Android provides forward compatibility,Specifies the API Level on which the application is designed to run. In some cases. As Android evolves with each new version, some behaviors and even appearances might change. However, if the API level of the platform is higher than the version declared by your app's targetSdkVersion, the system may enable compatibility behaviors to ensure that your app continues to work the way you expect.
If not set, the default value equals that given to minSdkVersion
targetSdkVersion是Android系统提供前向兼容的主要手段,用来保证以前老的app即使运行在新的Android系统上,它的行为还是和以前兼容。因为Android系统是不断更新的,有些API的行为在新系统上发生了变化,那么对于那些以前的apk,即使它安装在了最新的Android系统上,调用的最新Android系统的API接口,只要apk的targetSdkVersion不变,那么它的行为依然保持不变,依然执行原来的行为。这就保证了系统对以前老的app应用的前向兼容性。
例如,在 Android 4.4 (API 19)以后,AlarmManager 的 set() 和 setRepeat() 这两个 API 的行为发生了变化。在 Android 4.4 以前,这两个 API 设置的都是精确的时间,系统能保证在 API 设置的时间点上唤醒 Alarm。因为省电原因 Android 4.4 系统实现了 AlarmManager 的对齐唤醒,这两个 API 设置唤醒的时间,系统都对待成不精确的时间,系统只能保证在你设置的时间点之后某个时间唤醒。
这时,虽然 API 没有任何变化,但是实际上 API 的行为却发生了变化,如果老的 APK 中使用了此 API,并且在应用中的行为非常依赖 AlarmManager 在精确的时间唤醒,例如闹钟应用。如果 Android 系统不能保证兼容,老的 APK 安装在新的系统上,就会出现问题。
Android 系统是怎么保证这
种兼容性的呢?这时候 targetSdkVersion 就起作用了。APK 在调用系统 AlarmManager 的 set() 或者 setRepeat() 的时候,系统首先会查一下调用的 APK 的 targetSdkVersion 信息,如果小于 19,就还是按照老的行为,即精确设置唤醒时间,否者执行新的行为。
我们来看一下 Android 4.4 上 AlarmManger 的一部分源代码:
private final boolean mAlwaysExact;
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
}
看到这里,首选获取应用的 targetSdkVersion,判断是否是小于 Build.VERSION_CODES.KITKAT (即 API Level 19),来设置 mAlwaysExact 变量,表示是否使用精确时间模式。
public static final long WINDOW_EXACT = 0;
public static final long WINDOW_HEURISTIC = -1;
private long legacyExactLength() {
return (mAlwaysExact ? WINDOW_EXACT : WINDOW_HEURISTIC);
}
public void set(int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null);
}
这里看到,直接影响到 set() 方法给 setImpl() 传入不同的参数,从而影响到了 set() 的执行行为。具体的实现在 AlarmManagerService.java
,这里就不往下深究了。
看到这里,发现其实 Android 的 targetSdkVersion 并没有什么特别的,系统使用它也非常直接,甚至很“粗糙”。仅仅是用过下面的 API 来获取 targetSdkVersion,来判断是否执行哪种行为:
getApplicationInfo().targetSdkVersion;
所以,我们可以猜测到,如果 Android 系统升级,发生这种兼容行为的变化时,一般都会在原来的保存新旧两种逻辑,并通过 if-else 方法来判断执行哪种逻辑。果然,在源码中搜索,我们会发现不少类似 getApplicationInfo().targetSdkVersion < Buid.XXXX
这样的代码,相对于浩瀚的 Android 源码量来说,这些还是相对较少了。其实原则上,这种会导致兼容性问题的修改还是越少越好,所以每次发布新的 Android 版本的时候,Android 开发者网站都会列出做了哪些改变,在这里,开发者需要特别注意。
最后,这3者之间的关系,理想上,在稳定状态下三者的关系应该更像这样:
minSdkVersion (lowest possible) <= targetSdkVersion == compileSdkVersion (latest SDK)
用较低的 minSdkVersion 来覆盖最大的人群,用最新的 SDK 设置 target 和 compile 来获得最好的外观和行为。#BuildBetterApps
AS默认的targetSdkVersion和compileSdkVersion都是使用的最新的API Level。
另外,minSdkVersion 和 targetSdkVersion 与 compileSdkVersion 的另一个不同之处是它们会被包含进最终的 APK 文件中,如果你查看生成的 AndroidManifest.xml 文件,你会看到类似下面这样的标签:
如果你在 manifest 文件中手工设置,你会发现 Gradle 在构建时会忽略它们(尽管其它构建系统可能会明确依赖它们)。
What is API Level?
API Level is an integer value that uniquely identifies the framework API revision offered by a version of the Android platform.
The Android platform provides a framework API that applications can use to interact with the underlying Android system. The framework API consists of:
- A core set of packages and classes
- A set of XML elements and attributes for declaring a manifest file
- A set of XML elements and attributes for declaring and accessing resources
- A set of Intents
- A set of permissions that applications can request, as well as permission enforcements included in the system
Each successive version of the Android platform can include updates to the Android application framework API that it delivers.
Updates to the framework API are designed so that the new API remains compatible with earlier versions of the API. That is, most changes in the API are additive and introduce new or replacement functionality. As parts of the API are upgraded, the older replaced parts are deprecated but are not removed, so that existing applications can still use them. In a very small number of cases, parts of the API may be modified or removed, although typically such changes are only needed to ensure API robustness and application or system security. All other API parts from earlier revisions are carried forward without modification.
The framework API that an Android platform delivers is specified using an integer identifier called "API Level". Each Android platform version supports exactly one API Level, although support is implicit for all earlier API Levels (down to API Level 1). The initial release of the Android platform provided API Level 1 and subsequent releases have incremented the API Level.
The API Level identifier serves a key role in ensuring the best possible experience for users and application developers:
- It lets the Android platform describe the maximum framework API revision that it supports
- It lets applications describe the framework API revision that they require
- It lets the system negotiate the installation of applications on the user's device, such that version-incompatible applications are not installed.
Each Android platform version stores its API Level identifier internally, in the Android system itself.
参考:
Android targetSdkVersion 原理
如何选择 compileSdkVersion, minSdkVersion 和 targetSdkVersion
Android中build target,minSdkVersion,targetSdkVersion,maxSdkVersion概念区分
https://developer.android.com/guide/topics/manifest/uses-sdk-element.html