# 安卓适配6.0-7.0

515 阅读5分钟

一、Android7.0

1、应用间共享文件

7.0中为了提高私有文件的安全性,面向7.0及更高版本的应用私有目录将被限制访问

问题:SharedPreferences闪退:私有文件的文件权限不在放权给所有的应用,在manifest里使用MODE_WORLD_READABLE或MODE_WORL_WRITEABLE进行的操作将触发SecurityException。应修改成MODE_PRIVATE
给其他应用传递file://URI这种URI类型,可能导致接收者无法访问该路径。因此,在7.0中尝试传递file://URI会触发FileUriExposedException。

申明Provider

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

创建resource->xml->file

在res创建xml文件,再xml文件下创建filepaths
内部存储、外部存储、公共存储目录
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    //代表Context.getFilesDir()所指向的目录
    <files-path
        name=""
        path=""></files-path>
    //代表Context.getCacheDir()所指向的目录
    <cache-path
        name=""
        path=""></cache-path>
    //代表Context.getExternalCacheDir()所指向的目录    
    <external-cache-path
        name=""
        path=""></external-cache-path>
    //代表Context.getExternalFilesDir()所指向的目录    
    <external-files-path
        name=""
        path=""></external-files-path>
    //代表Environment.getExternalStorageDirectory()所指向的目录
    <external-path
        name=""
        path=""></external-path>
</paths>

代码

File imgFile = new File("照片存放目录");
Uri uri;
//根据当前系统版本决定使用哪个api,N是Android7.0的代号
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//第一个参数是上下文,第二个参数来自清单文件,必须完全一样,第三个参数为上面创建的照片file
    uri = FileProvider.getUriForFile(this, "{包名}.fileprovider", imgFile);
} else {
    //Android7.0还用原先的api
    uri = Uri.fromFile(imgFile);
}

2、广播

Android Nougat (7.0) 中,Google 移除了三项隐式广播(Implict Broadcast)监听网络状态(CONNECTIVITY_ACTION), 监听手机拍照(ACTION_NEW_PICTURE),监听录像(ACTION_NEW_VIDEO),而在 Android O 中除了这里列出的,其余所有的隐式广播都被移除了。请充分利用 Android 7.0 以迁移到JobScheduler和相关的API(官方:https://github.com/googlesamples/android-JobScheduler)

3、APK signature scheme v2

1.只勾选V1签名就是传统方案签署,但是在 Android 7.0 上不会使用V2安全的验证方式。
2.只勾选V2签名7.0以下会显示未安装,Android 7.0 上则会使用了V2安全的验证方式。
3.同时勾选V1和V2则所有版本都没问题。

4、Popupwindow位置不正确

处理PopupWindow在Android 7.x中兼容性问题的示例

PopupWindow 中的 showAsDropDown(View anchor) 用于在指定锚点View下方显示 PopupWindow,在Android 7.0 (api<=23) 以前是没什么问题的,但是在Android 7.x系统上,会在某些情况下出现兼容问题:

如果指定 PopupWindow 的高度为 MATCH_PARENT,调用 showAsDropDown(View anchor) 时,在 7.0 之前,会在锚点 anchor 下边缘到屏幕底部之间显示 PopupWindow;而在 7.0、7.1 系统上的 PopupWindow 会占据整个屏幕(除状态栏之外)。

如果指定 PopupWindow 的高度为 WRAP_CONTENT, 调用 showAsDropDown(View anchor) 时,便不会出现兼容性的问题。

如果指定 PopupWindow 的高度为自定义的值height,调用 showAsDropDown(View anchor)时, 如果 height > 锚点 anchor 下边缘与屏幕底部的距离, 则还是会出现7.0、7.1上显示异常的问题;否则,不会出现该问题。可以看出,情况1和2是情况3的特例。

// Android 7.x中,PopupWindow高度为match_parent时,会出现兼容性问题,需要处理兼容性
if (Build.VERSION.SDK_INT >= 24) { 
    // 记录anchor在屏幕中的位置
    int[] location = new int[2]; 
    anchor.getLocationOnScreen(location);
    int offsetY = location[1] + anchor.getHeight();
    // Android 7.1中,PopupWindow高度为 match_parent 时,会占据整个屏幕
    if (Build.VERSION.SDK_INT == 25) { 
        // 故而需要在 Android 7.1上再做特殊处理
        // 获取屏幕高度
        int screenHeight = ScreenUtils.getScreenHeight(context); 
        // 重新设置 PopupWindow 的高度
        popupWindow.setHeight(screenHeight - offsetY); 
    }
    popupWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, 0, offsetY);
} else {
    popupWindow.showAsDropDown(anchor);
}

二、Android6.0

1、运行时权限

需注意表中每个危险权限都属于一个权限组,我们在运行时权限处理时使用权限名,用户一旦同意授权了,那么该权限组中其他权限也将同时被授权

申请权限

checkSelfPermission、requestPermissions
//例子:
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
       ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                            Manifest.permission.READ_PHONE_STATE);
            ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.READ_PHONE_STATE,
                            Manifest.permission.CAMERA},RESULT_PHONE_STATE);
}

EasyPermissions框架
dependencies {
    // For developers using AndroidX in their applications
    implementation 'pub.devrel:easypermissions:3.0.0'
    // For developers using the Android Support Library
    implementation 'pub.devrel:easypermissions:2.0.1'
}
权限组名 权限 权限名
android.permission-group.CAMERA(相机) android.permission.CAMERA 允许程序访问摄像头进行拍照
android.permission-group.CONTACTS(联系人) android.permission.READ_CONTACTS 允许程序访问联系人通讯录信息
android.permission.WRITE_CONTACTS 写入联系人,但不可读取
android.permission.GET_ACCOUNTS 允许程序访问账户Gmail列表
android.permission-group.PHONE(电话) android.permission.READ_PHONE_STATE 允许程序访问电话状态
android.permission.CALL_PHONE 允许程序从非系统拨号器里拨打电话
android.permission.READ_CALL_LOG 读取通话记录
android.permission.WRITE_CALL_LOG 允许程序写入(但是不能读)用户的联系人数据
com.android.voicemail.permission.ADD_VOICEMAIL 允许一个应用程序添加语音邮件系统
android.permission.USE_SIP 允许程序使用SIP视频服务
android.permission.PROCESS_OUTGOING_CALLS 允许程序监视,修改或放弃播出电话
GroupPermissionsandroid.permission-group.CALENDAR(日历) android.permission.READ_CALENDAR 允许程序读取用户的日程信息
android.permission.WRITE_CALENDAR 允许程序写入日程,但不可读取
android.permission-group.LOCATION(定位) android.permission.ACCESS_FINE_LOCATION 允许程序通过GPS芯片接收卫星的定位信息
android.permission.ACCESS_COARSE_LOCATION 允许程序通过WiFi或移动基站的方式获取用户错略的经纬度信息
android.permission-group.STORAGE(存储) android.permission.READ_EXTERNAL_STORAGE 程序可以读取设备外部存储空间(内置SDcard和外置SDCard)的文件,如果您的App已经添加
android.permission.WRITE_EXTERNAL_STORAGE 允许程序写入外部存储,如SD卡上写文件
android.permission-group.SENSORS(传感器) android.permission.BODY_SENSORS 传感器
android.permission-group.SMS(短信) android.permission.SEND_SMS 允许程序发送短信
android.permission.RECEIVE_SMS 允许程序接收短信
android.permission.READ_SMS 允许程序读取短信内容
android.permission.RECEIVE_WAP_PUSH 允许程序接收WAP PUSH信息
android.permission.RECEIVE_MMS 允许程序接收彩信
android.permission.READ_CELL_BROADCASTS
android.permission-group.MICROPHONE(麦克风) android.permission.RECORD_AUDIO 麦克风

2、取消支持 Apache HTTP 客户端

Android 6.0 版移除了对 Apache HTTP 客户端的支持。如果您的应用使用该客户端,并以 Android 2.3(API 级别 9)或更高版本为目标平台,请改用 HttpURLConnection 类。此 API 效率更高,因为它可以通过透明压缩和响应缓存减少网络使用,并可最大限度降低耗电量。要继续使用 Apache HTTP API,您必须先在 build.gradle 文件中声明以下编译时依赖项:
android {
    useLibrary 'org.apache.http.legacy'
}