Android版本适配(一)

2019-11-04  本文已影响0人  YX_亚亚呀

前面做项目用到了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 -->

适配需要用到的方法

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

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

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

当应用请求权限时,系统将向用户显示一个对话框。
当用户响应时,系统将调用应用的 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中静态的注册的无法收到。

上一篇下一篇

猜你喜欢

热点阅读