新手的第一个Android项目该如何选择targetSdkVer
摘要:
新手的第一个Android项目或许没有认真考虑过如何选择targetSdkVersion的问题,也或许还有一部分像TeachCourse一样的开发者,积累一些工作经验后才回头来思考这个问题。那么该如何选择一个targetSdkVersion的属性值?一个属性值为23的targetSdkVersion表示什么含义?那么API 24和Android 7.0又是什么关系?为什么API 19开发的Android项目可以在Android 7.0系统的手机上运行,反之Android 4.x系统的手机可以运行API 24开发的Android项目?最后,正确理解compileSdkVersion、minSdkVersion和targetSdkVersion三者之间的关系。
应用程序接口和Android系统关系一、认识targetSdkVersion
targetSdkVersion
即目标软件开发版本,在创建每一个Android项目的时候都需要选择targetSdkVersion
和minSdkVersion
,一个targetSdkVersion
的属性值表示创建的Android项目使用哪个API版本,一个API版本使用一个整型数字表示,API的全称是Application Programming Interface,即应用程序编程接口,API 19对应的编程接口和API 23定义的编程接口存在差别,因为使用整型数字表示targetSdkVersion
的属性值,容易记忆和便于比较它们之间的大小,高版本API编程接口可以兼容低版本API编程接口,反之则不行。
需要区别Android 7.0和API 24之间的关系,Android 7.0定义的手机系统的版本,该系统的版本对外开放的应用程序接口被定义为API 24,如果开发者想要在开发的APP中使用Android 7.0系统提供的功能,这些功能包括:多窗口支持、通知显示变更、JIT/AOT编译、快速的应用安装路径等等,那么新手在创建的Android项目的时候就需要选择API 24的应用程序接口。
不知道开发者也没有注意到,为什么API 19开发的Android项目可以在Android 7.0系统的手机上运行,反之Android 4.x系统的手机可以运行API 24开发的Android项目,它们运行的效果是否一样呢?如果是Android 7.0系统运行API 24开发的应用程序,效果又是怎么样?这就让TeachCourse联想到了API 23开发的应用程序在低于Android 6.0系统上运行时,如果权限被禁用后,会提示如下图:
运行时权限但是,如果API 23开发的应用程序在高于或等于Android 6.0系统的手机上运行时需要自己定义打印上述Toast
,下面是完整的运行时权限申请的代码:
private static final int REQUEST_CODE_PERMISSION=0x112;
private void initPermission() {
int flag= ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if(PackageManager.PERMISSION_GRANTED!=flag){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_PERMISSION);
}else{
alterImageNew();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (REQUEST_CODE_PERMISSION==requestCode){
switch (grantResults[0]){
case PackageManager.PERMISSION_DENIED:
boolean isSecondRequest=ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA);
if(isSecondRequest)
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},REQUEST_CODE_PERMISSION);
else
Toast.makeText(this, "拍照权限被禁用,请在权限管理修改", Toast.LENGTH_SHORT).show();
break;
case PackageManager.PERMISSION_GRANTED:
alterImageNew();
break;
}
}
}
如果需要显示类似于上面图片显示的Toast
提示,开发者需要在onRequestPermissionResult
回调方法中,打印拍照权限被禁用,请在权限管理修改这句话,在使用API 23应用程序编程接口开发的APP中都可以反复复制上面代码,仅将CAMERA
替换成定义的权限参数,将alterImageNew()
替换成拥有权限后要执行的代码。
举这个例子的目的,想要说明为什么在API 23版本开发的APP在低于Android 6.0系统不会执行上述代码的原因。
高版本的API定义了一些的编程接口,但是通常不需要开发者考虑是否高版本API开发的APP在低版本的Android系统的兼容性问题,API和Android系统是两个不一样的概念,API属于应用层的东西,Android系统属于底层的东西,开发者想要显示底层的演示效果,就需要使用API来完成,如果想要不同的演示效果,又需要考虑选择哪个版本的API,也就是文章的标题提到的:如何选择targetSdkVersion
属性值的问题。
二、如何选择targetSdkVersion
一个Android系统,对外提供一套API,如何选择targetSdkVersion
取决于应用程序需要实现的功能,如果你的应用程序使用API 7就可以实现的功能,可以不用考虑使用API 24,使用低版本API的其中一个好处,可以让更多的Android系统运行的效果保持一致,即兼容性更好,打个比方:API 7开发的APP可能兼容98%以上的Android手机,而API 24开发的APP可能兼容仅有60%,所谓的不兼容并不是无法正常运行,而是在不同Android系统的手机运行的效果差异比较大,会让用户感觉难以接受;使用低版本API的其中一个不足,显示的效果比较OUT,提供的可用的接口或类比较少,本来一句代码可以完成的功能(封装的类或接口),需要自己花一天琢磨写很多的代码,也就是有高版本API的其中一个原因,提供更多的或封装好的应用程序接口让开发者使用。
同时,高版本API会针对低版本存在的问题进行改进和完善,摈弃一下不用的类或接口,新增一些方法或属性,如果你使用的方法是在某个API被另一个方法代替的话,你可能就得在代码中区分APP是运行在哪个版本的Android系统,一个很典型的例子:WebChromeClient
的onShowFileChooser()
方法和openFileChooser()
方法,如果你的targetSdkVersion
小于19,在处理WebView上传表单的数据的时候就需要重写openFileChooser
方法;如果你的targetSdkVersion
大于或等于19,你就必须同时重写onShowFileChooser
和openFileChooser
两个方法,openFileChooser
方法在Android 4.0以下系统被回调,另一则在Android 4.0以上系统被回调。这是高低版本API摈弃或新增一些类和方法时需要注意的其中一个问题。
了解并学习Android 4.x、Android 5.x、Android 6.x或Android 7.x系统的特性,重点掌握不同系统同一个功能的实现方式,即行为变更,特点:变更的行为在当前系统或更高系统版本中被支持,一个简单的例子:Android 7.0系统其中的一个行为变更是权限更改,尝试传递file:// URI
的方式写入本地文件或读取本地文件,使用API 24开发的APP将会触发 FileUriExposedException
异常,请求拍照并保存到本地的正确的写法,如下:
/**
* 调用系统相机
*/
private void goToTakePhoto() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File file = new File(mSDCardPath, System.currentTimeMillis() + ".jpg");
/**Android 7.0以上的方式**/
Uri contentUri = getUriForFile(this, getString(R.string.install_apk_path), file);
grantUriPermission(getPackageName(), contentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
/**Android 7.0以前的方式**/
// intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(intent, RESULT_CAPTURE_IMAGE);
} else {
Toast.makeText(this, "sdcard不存在", Toast.LENGTH_SHORT).show();
}
}
想要查看完整的例子,文章后面附上源码,可以下载测试。本来已经代码可以实现的拍照功能,API 24开发的APP在Android 7.0以上系统需要至少四个步骤:
- 在AndroidManifest.xml文件中定义一个FileProvider
- 指定写入或读取本地文件的目录
- 生成完整的URI
- 请求授予URI权限
- 实现手机拍照并保存本地功能
三、关于minSdkVersion和compileSdkVersion
minSdkVersion
定义应用程序支持的最低API版本,最低版本设置为API 11,目标版本设置为API 24,那么应用程序调用使用API 14提供的方法时,Android Studio或Eclipse开发工具将提醒开发者引用一个未定义的方法,使用该方法需要将minSdkVersion
设置为API 14以上,如下图:
继续在上述代码,造成的结果大于或等于Android 4.0的系统可以正常执行,小于Android 4.0的系统将在运行时尝试访问不可用的 API 时发生崩溃。
compileSdkVersion
定义应用程序编译选择哪个Android SDK版本,通常compileSDKVersion
属性值被设置为最新的API版本,例如:25,改变compileSDKVersion
的属性值不会影响Android系统运行行为,比如说,将属性值设置为25,targetSdkVersion
属性值为23,代码如下:
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "cn.teachcourse.demos"
minSdkVersion 11
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
开发的应用程序在Android 7.0系统运行,不会以Android 7.0新增的行为运行,决定Android系统行为的仍然是targetSDKVersion
,那么compileSDKVersion
有什么用呢?选择最新的API版本,在编译的时候检查代码的错误和警告,提示开发者修改和优化,因为通常在Android项目中会引入第三方的支持库,支持库使用了23.1.1版本,compileSdkVersion
的属性值至少为23.0.0,新版本的支持库的发布紧跟着对应的Android系统平台,能够更好的兼容。
四、targetSdkVersion、minSdkVersion和CompileSdkVersion之间的关系
记住一点:Android系统平台的行为变更,只有targetSdkVersion
的属性值被设置为大于或等于该系统平台的API版本时,才会生效;compileSdkVersion
属于Android编译项目时其中的一项配置,主要区别是compileSDKVersion
在不会被打包的APK文件中,targetSdkVersion
和minSdkVersion
将被打包到APK文件中,具体可以解压APK文件后,查看AndroidManifest.xml文件,如下图:
你会发现在使用Android Studio开发工具时,手动修改AndroidManifest.xml
的属性值,使用Gradle构建后,将会被忽略,最终生成的是build.gradle
配置文件指定的属性值。它们之间的关系如下:
minSdkVersion <= targetSdkVersion <= compileSdkVersion
参考资料:
https://developer.android.com/guide/topics/manifest/uses-sdk-element.html