android 8.0适配

由于国内几家主流应用市场的联合公告,19年8月1日后更新的应用必须要适配到8.0,就开始了项目从5.0到8.0的适配 有错误的地方还请指正
在这里插入图片描述
参考:

6.0权限处理

6.0权限一点都不新鲜了,以前处理的时候直接使用的rxpermission,或者直接申请没有考虑复用性,考虑到项目用到的动态权限只有:定位、拍照、读写存储,就不集成第三方库了,自己做了点简单的封装,大家觉得哪里不妥,请回复指教

PermissionUtils主要判断要申请的权限是否已授予、权限是否授予后的处理(如全同意、部分同意、拒绝后点击不再询问)
PermissionUtils创建需要传入

  • activity上下文环境,用于申请权限,权限的申请统一由activity申请(包括fragment中申请权限)
  • PermissionListener 权限申请结果的回调,权限已授予、权限被拒绝、权限被拒绝且点击了不再询问
/**
 * Created by cys on 2018/12/17 0017.
 * 权限申请工具
 */
public class PermissionUtil {

    private BaseFragmentActivity mActivity;
    private PermissionListener mPermissionListener;

    public PermissionUtil(@NonNull BaseFragmentActivity activity, PermissionListener permissionListener) {
        this.mActivity = activity;
        this.mPermissionListener = permissionListener;
    }

    /**
     * 检查权限
     *
     * @param permissions 需要申请的权限
     * @param requestCode 请求码
     * @return
     */
    public void checkPermission(String[] permissions, int requestCode) {
        //>=6.0动态申请权限
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            mPermissionListener.onPermissionsGranted(requestCode);
        } else {
            ArrayList<String> unGrantPermissions = new ArrayList<>();
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(mActivity, permission) != PackageManager.PERMISSION_GRANTED) {
                    unGrantPermissions.add(permission);
                }
            }
            if (unGrantPermissions.size() > 0) {
                mActivity.onRequestPermissions(unGrantPermissions.toArray(new String[unGrantPermissions.size()]), requestCode);
            } else {
                mPermissionListener.onPermissionsGranted(requestCode);
            }
        }
    }

    /**
     * 请求结果回调
     *
     * @param requestCode  申请码
     * @param permissions  请求的权限列表
     * @param grantResults 请求权限列表的授权信息
     */
    public void onRequestResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        List<String> deniedPermission = new ArrayList<>();
        List<String> alwaysDeniedPermissions = new ArrayList<>();
        for (int i = 0; i < grantResults.length; i++) {
            //如果权限被拒绝,return
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                deniedPermission.add(permissions[i]);
                //>=6.0动态申请权限
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    //拒绝时点了不再询问
                    if (!mActivity.shouldShowRequestPermissionRationale(permissions[i])) {
                        alwaysDeniedPermissions.add(permissions[i]);
                    }
                }
            }
        }
        if (deniedPermission.size() == 0) {
            mPermissionListener.onPermissionsGranted(requestCode);
        } else {
            if (alwaysDeniedPermissions.size() > 0) {
                mPermissionListener.onPermissionsAlwaysDenied(requestCode, alwaysDeniedPermissions.toArray(new String[alwaysDeniedPermissions.size()]));
            } else {
                mPermissionListener.onPermissionsDenied(requestCode, deniedPermission.toArray(new String[deniedPermission.size()]));
            }
        }
    }
}

/**
 * Created by cys on 2018/12/18 0018.
 * 权限获取监听
 */
public interface PermissionListener {
    int PERMISSION_CAMERA_CODE = 100;//相机权限申请码
    int PERMISSION_STORAGE_CODE = 101;//读写存储权限申请码
    int PERMISSION_LOCATION_CODE = 102;//定位权限申请码

    /**
     * 请求权限成功
     *
     * @param requestCode 请求码
     */
    void onPermissionsGranted(int requestCode);

    /**
     * 请求权限失败
     *
     * @param requestCode 权限请求码
     * @param permissions 被拒绝的权限
     */
    void onPermissionsDenied(int requestCode, String[] permissions);

    /**
     * 请求权限失败 并勾选了不再提示
     *
     * @param requestCode 权限请求码
     * @param permissions 被拒绝的权限
     */
    void onPermissionsAlwaysDenied(int requestCode, String[] permissions);
}

在BaseActivity

  • 添加两个方法用于调用PermissionUtil申请、检查权限
  • onRequestPermissions方法是最终的真实申请权限方法
  • 并实现PermissionListener接口
/**
 * 回调给指定的监听器
 *
 * @param permissions        申请的权限列表
 * @param requestCode        请求码
 * @param permissionListener 指定的监听器
 */
public void requestPermission(String[] permissions, int requestCode, PermissionListener permissionListener) {
    mPermissionUtil = new PermissionUtil(this, permissionListener);
    mPermissionUtil.checkPermission(permissions, requestCode);
}
/**
 * 默认权限监听是当前activity
 *
 * @param permissions 申请的权限列表
 * @param requestCode 请求码
 */
public void requestPermission(String[] permissions, int requestCode) {
    mPermissionUtil = new PermissionUtil(this, this);
    mPermissionUtil.checkPermission(permissions, requestCode);
}

/**
 * 申请权限回调
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (mPermissionUtil != null) {
        mPermissionUtil.onRequestResult(requestCode, permissions, grantResults);
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

public void onRequestPermissions(String[] permissions, int requestCode) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        requestPermissions(permissions, requestCode);
    }
}
@Override
public void onPermissionsGranted(int requestCode) {
}
@Override
public void onPermissionsDenied(int requestCode, String[] permissions) {
}
@Override
public void onPermissionsAlwaysDenied(int requestCode, String[] permissions) {
    if (showPermissionDialog == null) {
        showPermissionDialog = new ShowPermissionDialog(mContext);
    }
    showPermissionDialog.show(permissions);
}

使用方法

  • activity中申请权限直接调用即可,传入的PermissionListener即为自己,做一些比较通用的操作,如(权限被拒绝并选中不再询问提示此功能需要开启相关权限对话框,点击对话框文字“去设置”跳转到应用设置页面)
  • fragment、viewholder中申请权限需要实现PermissionListener接口 因为权限被授予后下一步的操作要在当前fragment或viewholder中处理

示例:
在MainActivity中

//先申请权限
requestPermission(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION},PERMISSION_LOCATION_CODE);

//若权限被拒、且点击不再询问 调用BaseActivity方法,提示用户授予相关权限
 @Override
 public void onPermissionsAlwaysDenied(int requestCode,String[] permissions) {
     if (requestCode == PermissionListener.PERMISSION_LOCATION_CODE) {
         super.onPermissionsAlwaysDenied(requestCode,permissions);
     }
 }
 //若权限被用户授予,定位
 @Override
 public void onPermissionsGranted(int requestCode) {
     if (requestCode == PermissionListener.PERMISSION_LOCATION_CODE) {
         initLocation();
     }
 }

在ViewHolder中

  • ViewHolder实现PermissionListener接口
  • 申请读写存储权限 由activity调用PermissionUtils检查权限,最终还是通过activity调用onRequestPermissions申请
((BaseFragmentActivity) mContext).requestPermission(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE}
        , PermissionListener.PERMISSION_STORAGE_CODE, this);
  • 申请权限回调
//权限被授予 保存图片
@Override
public void onPermissionsGranted(int requestCode) {
    if (requestCode == PermissionListener.PERMISSION_STORAGE_CODE) {
        saveImage();
    }
}
@Override
public void onPermissionsDenied(int requestCode, String[] permissions) {
}
//权限被拒 且点击不再询问提示需要开启相关权限 仍然回调给activity进行处理
@Override
public void onPermissionsAlwaysDenied(int requestCode, String[] permissions) {
    ((BaseFragmentActivity) mContext).onPermissionsAlwaysDenied(requestCode, permissions);
}

fragment中申请权限也类似,在BaseFragment做了统一的实现

/**
 * 回调给指定的监听器
 *
 * @param permissions        申请的权限列表
 * @param requestCode        请求码
 * @param permissionListener 指定的监听器
 */
public void requestPermission(String[] permissions, int requestCode, PermissionListener permissionListener) {
    ((BaseFragmentActivity) getActivity()).requestPermission(permissions, requestCode, permissionListener);
}
/**
 * 默认权限监听是当前activity
 *
 * @param permissions 申请的权限列表
 * @param requestCode 请求码
 */
public void requestPermission(String[] permissions, int requestCode) {
    ((BaseFragmentActivity) getActivity()).requestPermission(permissions, requestCode);
}
@Override
public void onPermissionsGranted(int requestCode) {
}
@Override
public void onPermissionsDenied(int requestCode, String[] permissions) {
}
@Override
public void onPermissionsAlwaysDenied(int requestCode, String[] permissions) {
    ((BaseFragmentActivity) getActivity()).onPermissionsAlwaysDenied(requestCode, permissions);
}

在fragment中调用requestPermission 三个参数的方法,permissionListener传入this即可

ShowPermissionDialog中可以做提示文字的操作,判断相关权限append相关权限的字符如Manifest.permission.ACCESS_FINE_LOCATION 对应“定位” Manifest.permission.ACCESS_COARSE_LOCATION 同样对应“定位”,有相同字符后不再添加,可视需求而定

7.0的适配

  1. 应用之间共享文件
    主要见于拍照和应用内升级跳转到安装页
    参见鸿洋大神博客:
    https://blog.csdn.net/lmj623565791/article/details/72859156
    应用内升级新版本时需要添加 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    不然总提示解析安装包错误,8.0的应用安装适配见下面
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
Uri fileUri;
if (Build.VERSION.SDK_INT >= 24) {
    fileUri = FileProvider.getUriForFile(activity, "com.sign.asyoulike.fileprovider",file);//添加这一句表示对目标应用临时授权该Uri所代表的文件
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
    fileUri = Uri.fromFile(file);
}
intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
activity.startActivity(intent);
  1. SharedPreferences适配 这个项目中均采用MODE_PRIVATE
	SharedPreferences read = getSharedPreferences(RELEASE_POOL_DATA, MODE_WORLD_READABLE);
	//MODE_WORLD_READABLE :7.0以后不能使用这个获取,会闪退,修改成MODE_PRIVATE

8.0适配

  1. 应用内升级,需要允许安装未知来源权限权限<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

  2. 透明主题的Activity不能设置屏幕方向,只有不透明的全屏Activity可以自主设置界面方向 这个项目中也没有遇到

	<item name="android:windowIsTranslucent">true</item>
	
	android:screenOrientation="portrait"
  1. 创建通知时需要传入 channelId,否则通知将不会显示
//判断>=8.0就创建通知渠道 指定channelId channelName(会展示给用户) importance(重要等级如仅有提示音、弹框并且有提示音)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
    NotificationManager notificationManager = (NotificationManager) getSystemService(
            NOTIFICATION_SERVICE);
    notificationManager.createNotificationChannel(channel);
}

创建通知时传入通知渠道即可

NotificationCompat.Builder builder = new NotificationCompat.Builder(activity, ConstantValue.CHANNERL_ID_UPDATE);

引发推送收不到的相关问题:

  • android8.0小米推送收不到

小米推送相关适配文档,传入客户端定义的channelId channelName即可
详见:https://dev.mi.com/console/doc/detail?pId=1278

另外小米推送6.0以上要求读写存储权限或者读取手机状态权限其一
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 极光推送是可以收到的,但是channelName指定为“Notification” 提示方式是“仅有提示音”

这个可能极光会陆续开放接口 目前使用版本:jpush3.1.8
在这里插入图片描述
而且极光推送通知栏的小图标颜色不支持修改只能先这样了 下个版本可能会支持
在这里插入图片描述

  • 华为推送,没发现有什么问题
  • 魅族推送 由于没有魅族android8.0的手机 目前没有测到 不知道是否有问题 找到手机的话后续会补上
  1. 隐式广播
    项目中用到的地方只有监听网络状态变化的系统广播,原来是在manifest静态注册的,由于此广播在7.0已经被移除(见下图),而在8.0上除了被豁免的隐式广播除外,都收不到了,就改成在应用主页动态注册广播了
    在这里插入图片描述
    2019 你好 新的一年 要更努力
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值