美文网首页
Android版本适配(一)

Android版本适配(一)

作者: YX_亚亚呀 | 来源:发表于2019-11-04 14:21 被阅读0次

    前面做项目用到了Notification涉及到Android版本适配问题,然后总结了下Android6.0—9.0的变化及其适配。下面介绍Android6.0的适配。

    Android6.0适配

    在6.0之前的版本,我们使用某项权限只需要在Manifest中声明就可以使用了,但是在6.0之后,某些权限(例如:通讯录、位置、相机)不仅需要在Manifest中声明,还要在app运行的时候动态地申请并被用户允许才能正常使用,这类权限称为危险权限(Dangerous Permission)。与其对应的是正常权限(Normal Permissions),正常权限(例如:蓝牙,网络)只需要在清单文件声明即可。

    注意:只有危险权限需要动态申请。危险权限很多被分为9组,不需要一个个申请,对于同一组内的权限,只要一个被同意,其他的都会被同意。

    <!-- 危险权限 start -->
    <!--PHONE-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
    <uses-permission android:name="android.permission.USE_SIP"/>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <!--CALENDAR-->
    <uses-permission android:name="android.permission.READ_CALENDAR"/>
    <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
    <!--CAMERA-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <!--CONTACTS-->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <!--LOCATION-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <!--MICROPHONE-->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!--SENSORS-->
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <!--SMS-->
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
    <uses-permission android:name="android.permission.RECEIVE_MMS"/>
    <!--STORAGE-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 危险权限 Permissions end -->
    

    适配需要用到的方法

    • ContextCompat.checkSelfPermission

    检查应用是否具有某个危险权限。
    如果应用具有此权限,方法将返回 PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。
    如果应用不具有此权限,返回PackageManager.PERMISSION_DENIED,且应用必须明确向用户要求权限。

    • ActivityCompat.requestPermissions

    应用可以通过这个方法动态申请权限,调用后会弹出一个对话框提示用户授权所申请的权限。

    • ActivityCompat.shouldShowRequestPermissionRationale

    如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
    如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 false。
    如果设备规范禁止应用具有该权限,此方法也会返回 false。

    • onRequestPermissionsResult

    当应用请求权限时,系统将向用户显示一个对话框。
    当用户响应时,系统将调用应用的 onRequestPermissionsResult() 方法,向其传递用户响应,处理对应的场景

    适配流程 (下面以相机权限为例说明,分为以下几个步骤)

    1. 在Manifest中声明所需权限:

       <uses-permission android:name="android.permission.CAMERA"/>
      
    2. 调用ContextCompat.checkSelfPermission()检查权限:

       //checkSelfPermission()方法接受两个参数。  
       //第一个参数为Context对象,  
       //第二个参数为需要进行检查的权限,类型为String。  
       //返回值是一个int常量,返回PackageManager.PERMISSION_GRANTED表示权限已经被授予,  
       //返回PackageManager.PERMISSION_DENIED表示权限未被授予。
         
       if(ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)
              != PackageManager.PERMISSION_GRANTED) {
               //权限未授予,调用requestPermission()申请权限
       }else{
                //权限已授予
           }      
      
    3. 如果权限未被授予,调用ActivityCompat.requestPermissions()申请权限:

       //requestPermissions()接受三个参数,  
       //第一个参数是Context对象,  
       //第二个参数是一个String数组,可以同时申请多个权限,  
       //第三个参数是请求码。
      
       ActivityCompat.requestPermissions(this,
                  new String[]{Manifest.permission.CAMERA}, MY_REQUEST_CODE);  
      
    4. 重写Activity的onRequestPermissionsResult()处理申请回调:

       //requestCode是请求码,在这里就是上面的MY_REQUEST_CODE,  
       //permissions就是申请的权限,  
       //grantResults是请求的结果,数组大小与permissions对应。  
      
       @Override
       public void onRequestPermissionsResult(int requestCode,
       String permissions[], int[] grantResults) {
           switch (requestCode) {
               case MY_REQUEST_CODE: {
      
                   if (grantResults.length > 0 &&
                       grantResults[0] == PackageManager.PERMISSION_GRANTED) {
      
                       //用户允许该权限
      
                   } else {
      
                       //用户拒绝该权限
      
               }
               return;
               }
           }
       }  
      

    Android7.0适配

    (1)Android7.0新增了多窗口支持,用户可以一次在屏幕上打开两个应用。

    分屏模式具有以下几个特点:
    1. 分屏模式不会更改 Activity 生命周期
    2. 在分屏模式模式下,只有一个Activity会获得焦点,其他Activity处于Paused状态
    3. 在分屏模式下调整窗口大小会回调onConfigurationChanged
    4. 如果根Activity允许多窗口模式,那么与它在同一个栈种的Activity都被允许多窗口模式(即使没有设置)。
    5. 某些系统 UI 自定义选项将被禁用;例如,在非全屏模式中,应用无法隐藏状态栏。
    6. 系统将忽略对android:screenOrientation 属性所作的更改。
    适配流程
    1. 如果布局外层是ScrollView、RecycleView、ListView的界面,那么在分屏模式下是比较正常,可以尝试让这些界面支持分屏模式。

    2. 如果是固定宽高的界面,设置android:minimalHeight、android:minimalWidth来设置分屏模式下的最小宽高。

      <activity android:name=".MyActivity">
      <layout
      android:minimalHeight="450dp"
      android:minimalWidth="300dp" />
      </activity>

    3. 如果不想适配分屏模式,可以在Manifest的activity标签下设置属性android:resizeableActivity="false"

    (2)Android7.0应用间共享文件被限制

    在7.0版本之前,我们可以通过File://这一类Uri访问其他应用的私有文件或者让其他应用访问自己的私有文件。

    在Android 7.0系统上,Android框架强制执行了StrictMode API政策,禁止向应用外公开file://URI,如果尝试传递 file:// Uri来访问其他应用的私有文件会触发 FileUriExposedException异常,如调用系统相机拍照、裁切照片、打开APK安装界面等。 Android7.0如果要在应用间共享文件,可以发送content://URI类型的Uri,并授予URI临时访问权限,进行此授权的最简单方式是使用FileProvider类。

    适配流程 (下面以打开下载完的APK的实例说明,分为以下几个步骤)
    1. 导入v4包,在主module的build.gradle下的dependencies节点下添加依赖,版本号视情况而定。

       implementation 'com.android.support:support-v4:27.1.1'  
      
    2. 在 res/xml 目录下新建一个 xml 文件,用于存放应用需要共享的文件目录

       <?xml version="1.0" encoding="utf-8"?>
       <paths xmlns:android="http://schemas.android.com/apk/res/android">
       <files-path
           name="captured_media"
           path="captured_media/" />
       <external-path
           name="data"
           path="Android" />
       <cache-path
           name="cache"
           path="appCache" />
       <external-path
           name="external"
           path="" />
       </paths> 
      

      name是自定义的一个别名,path是这个共享的目录,这里的path值表示共享外部私有目录file/images下的文件。如果是输入的是"."则表示共享外部私有目录下的file/目录下的所有文件。

      其中<paths>元素有以下几个子节点:

      <files-path>:内部存储空间应用私有目录下的 files/ 目录,等同于 Context.getFilesDir() 所获取的目录路径

      <cache-path>:内部存储空间应用私有目录下的 cache/ 目录,同于 Context.getCacheDir() 所获取的目录路径;

      <external-path>:外部存储空间根目录,等同于 Environment.getExternalStorageDirectory() 所获取的目录路径;

      <external-files-path>外部存储空间应用私有目录下的 files/ 目录,等同于 Context.getExternalFilesDir(null) 所获取的目录路径;

      <external-cache-path>:外部存储空间应用私有目录下的 cache/ 目录,等同于 Context.getExternalCacheDir();

    3. 在AndroidManifest中声明FileProvide

      <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${applicationId}.FileProvider"
      android:exported="false"
      android:grantUriPermissions="true">
      
      <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_paths"/>
      </provider> 
      

      exported为false,grantUriPermissions表示授予URI临时访问权限。
      authorities的名字可自定义,一般为包名+FileProvide,resource就是刚刚新建的共享文件。

    4. 通过FileProvider,打开下载完的APK

       public static Intent getOpenFileIntent(Context context, DownloadResponse downloadResponse) {
           File file = new File(downloadResponse.getParentPath(), downloadResponse.getFileName());
           if (!file.exists()) {
               return null;
           }
           Intent intent = new Intent();
           intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
           intent.setAction(Intent.ACTION_VIEW);
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
               intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
               Uri contentUri = FileProvider.getUriForFile(context, "com.demo.lizejun.provider", file);
               intent.setDataAndType(contentUri, downloadResponse.getMimeType());
           } else {
               intent.setDataAndType(Uri.fromFile(file), downloadResponse.getMimeType());
           }
           if (!(context instanceof Activity)) {
               intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
           }
           return intent;
       }  
      

    (3)Android7.0关闭了三项系统广播:网络状态变更广播、拍照广播及录像广播。

    网络状态变更广播、拍照广播及录像广播
    只有在通过 动态注册 的方式才能收到网络变化的广播,
    在AndroidManifest.xml中静态的注册的无法收到。

    相关文章

      网友评论

          本文标题:Android版本适配(一)

          本文链接:https://www.haomeiwen.com/subject/sicdbctx.html