新手的第一个Android项目该如何选择targetSdkVersion

vusn4784 7年前
   <p>新手的第一个 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三者之间的关系。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/015aa3659a76af2295c3ee3428b4b1e0.jpg"></p>    <h2>一、认识targetSdkVersion</h2>    <p>targetSdkVersion 即目标软件开发版本,在创建每一个Android项目的时候都需要选择 targetSdkVersion 和 minSdkVersion ,一个 targetSdkVersion 的属性值表示创建的Android项目使用哪个API版本,一个API版本使用一个整型数字表示,API的全称是 <strong>Application Programming Interface</strong> ,即应用程序编程接口,API 19对应的编程接口和API 23定义的编程接口存在差别,因为使用整型数字表示 targetSdkVersion 的属性值,容易记忆和便于比较它们之间的大小,高版本API编程接口可以兼容低版本API编程接口,反之则不行。</p>    <p>需要区别 <strong>Android 7.0</strong> 和 <strong>API 24</strong> 之间的关系,Android 7.0定义的手机系统的版本,该系统的版本对外开放的应用程序接口被定义为API 24,如果开发者想要在开发的APP中使用Android 7.0系统提供的功能,这些功能包括:多窗口支持、通知显示变更、JIT/AOT编译、快速的应用安装路径等等,那么新手在创建的Android项目的时候就需要选择API 24的应用程序接口。</p>    <p>不知道开发者也没有注意到,为什么API 19开发的Android项目可以在Android 7.0系统的手机上运行,反之Android 4.x系统的手机可以运行API 24开发的Android项目,它们运行的效果是否一样呢?如果是Android 7.0系统运行API 24开发的应用程序,效果又是怎么样?这就让TeachCourse联想到了API 23开发的应用程序在 <strong>低于Android 6.0系统</strong> 上运行时,如果权限被禁用后,会提示如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/68166182b1fd417494f17b8f08c85557.png"></p>    <p>但是,如果API 23开发的应用程序在 <strong>高于或等于Android 6.0系统</strong> 的手机上运行时需要自己定义打印上述 Toast ,下面是完整的运行时权限申请的代码:</p>    <pre>  <code class="language-java">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;              }          }      }</code></pre>    <p>如果需要显示类似于上面图片显示的 Toast 提示,开发者需要在 onRequestPermissionResult 回调方法中,打印拍照权限被禁用,请在权限管理修改这句话,在使用API 23应用程序编程接口开发的APP中都可以反复复制上面代码,仅将 CAMERA 替换成定义的权限参数,将 alterImageNew() 替换成拥有权限后要执行的代码。</p>    <p>举这个例子的目的,想要说明为什么在API 23版本开发的APP在 <strong>低于Android 6.0系统</strong> 不会执行上述代码的原因。</p>    <p>高版本的API定义了一些的编程接口,但是通常不需要开发者考虑是否 <strong>高版本API</strong> 开发的APP在 <strong>低版本的Android系统</strong> 的兼容性问题, <strong>API</strong> 和 <strong>Android系统</strong> 是两个不一样的概念, <strong>API</strong> 属于应用层的东西, <strong>Android系统</strong> 属于底层的东西,开发者想要显示底层的演示效果,就需要使用API来完成,如果想要不同的演示效果,又需要考虑选择哪个版本的API,也就是文章的标题提到的:如何选择 targetSdkVersion 属性值的问题。</p>    <h2>二、如何选择targetSdkVersion</h2>    <p>一个Android系统,对外提供一套API,如何选择 targetSdkVersion 取决于应用程序需要实现的功能,如果你的应用程序使用API 7就可以实现的功能,可以不用考虑使用API 24,使用低版本API的其中一个好处,可以让更多的Android系统运行的效果保持一致,即兼容性更好,打个比方:API 7开发的APP可能兼容98%以上的Android手机,而API 24开发的APP可能兼容仅有60%,所谓的不兼容并不是无法正常运行,而是在不同Android系统的手机运行的效果差异比较大,会让用户感觉难以接受;使用低版本API的其中一个不足,显示的效果比较OUT,提供的可用的接口或类比较少,本来一句代码可以完成的功能(封装的类或接口),需要自己花一天琢磨写很多的代码,也就是有高版本API的其中一个原因,提供更多的或封装好的应用程序接口让开发者使用。</p>    <p>同时,高版本API会针对低版本存在的问题进行改进和完善,摈弃一下不用的类或接口,新增一些方法或属性,如果你使用的方法是在某个API被另一个方法代替的话,你可能就得在代码中区分APP是运行在哪个版本的Android系统,一个很典型的例子: WebChromeClient 的 onShowFileChooser() 方法和 openFileChooser() 方法,如果你的 targetSdkVersion <strong>小于19</strong> ,在处理WebView上传表单的数据的时候就需要重写 openFileChooser 方法;如果你的 targetSdkVersion <strong>大于或等于19</strong> ,你就必须同时重写 onShowFileChooser 和 openFileChooser 两个方法, openFileChooser 方法在 <strong>Android 4.0以下</strong> 系统被回调,另一则在 <strong>Android 4.0以上</strong> 系统被回调。这是高低版本API摈弃或新增一些类和方法时需要注意的其中一个问题。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/896799bed9bbb066f5af963f26470597.png"></p>    <p>了解并学习Android 4.x、Android 5.x、Android 6.x或Android 7.x系统的特性,重点掌握不同系统同一个功能的实现方式,即 <strong>行为变更</strong> ,特点:变更的行为在当前系统或更高系统版本中被支持,一个简单的例子:Android 7.0系统其中的一个行为变更是 <strong>权限更改</strong> ,尝试传递 file:// URI 的方式写入本地文件或读取本地文件,使用API 24开发的APP将会触发  FileUriExposedException 异常,请求拍照并保存到本地的正确的写法,如下:</p>    <pre>  <code class="language-java">/**       * 调用系统相机       */      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();          }      }</code></pre>    <p>想要查看完整的例子,文章后面附上源码,可以下载测试。本来已经代码可以实现的拍照功能,API 24开发的APP在Android 7.0以上系统需要 <strong>至少四个步骤:</strong></p>    <ol>     <li>在AndroidManifest.xml文件中定义一个FileProvider</li>     <li>指定写入或读取本地文件的目录</li>     <li>生成完整的URI</li>     <li>请求授予URI权限</li>     <li>实现手机拍照并保存本地功能</li>    </ol>    <h2>三、关于minSdkVersion和compileSdkVersion</h2>    <p>minSdkVersion 定义应用程序支持的最低API版本,最低版本设置为API 11,目标版本设置为API 24,那么应用程序调用使用API 14提供的方法时,Android Studio或Eclipse开发工具将提醒开发者引用一个未定义的方法,使用该方法需要将 minSdkVersion 设置为API 14以上,如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3bebf85c03ea1af8dde1ab5daa02133f.png"></p>    <p>继续在上述代码,造成的结果 <strong>大于或等于Android 4.0</strong> 的系统可以正常执行, <strong>小于Android 4.0</strong> 的系统将在运行时尝试访问不可用的 API 时发生崩溃。</p>    <p>compileSdkVersion 定义应用程序编译选择哪个Android SDK版本,通常 compileSDKVersion 属性值被设置为最新的API版本,例如:25,改变 compileSDKVersion 的属性值不会影响Android系统运行行为,比如说,将属性值设置为25, targetSdkVersion 属性值为23,代码如下:</p>    <pre>  <code class="language-java">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"      }</code></pre>    <p>开发的应用程序在Android 7.0系统运行,不会以Android 7.0新增的行为运行,决定Android系统行为的仍然是 targetSDKVersion ,那么 compileSDKVersion 有什么用呢?选择最新的API版本,在编译的时候检查代码的错误和警告,提示开发者修改和优化,因为通常在Android项目中会引入第三方的支持库,支持库使用了 <strong>23.1.1</strong> 版本, compileSdkVersion 的属性值至少为 <strong>23.0.0</strong> ,新版本的支持库的发布紧跟着对应的Android系统平台,能够更好的兼容。</p>    <h2>四、targetSdkVersion、minSdkVersion和CompileSdkVersion之间的关系</h2>    <p>记住一点:Android系统平台的行为变更,只有 targetSdkVersion 的属性值被设置为大于或等于该系统平台的API版本时,才会生效; compileSdkVersion 属于Android编译项目时其中的一项配置,主要区别是 compileSDKVersion 在不会被打包的APK文件中, targetSdkVersion 和 minSdkVersion 将被打包到APK文件中,具体可以解压APK文件后,查看AndroidManifest.xml文件,如下图:</p>    <p><img src="https://simg.open-open.com/show/739b672b9c079a9c357406dca8669560.png"></p>    <p>你会发现在使用Android Studio开发工具时,手动修改 AndroidManifest.xml 的属性值,使用Gradle构建后,将会被忽略,最终生成的是 build.gradle 配置文件指定的属性值。它们之间的关系如下:</p>    <p>minSdkVersion &lt;= targetSdkVersion &lt;= compileSdkVersion</p>    <p> </p>    <p> </p>    <p>来自:http://www.androidchina.net/6598.html</p>    <p> </p>