把targetSdkVersion设置为26(Android 8.0)需要注意的地方

前言

由于项目要上应用宝,必须设置targetSdk>=26,所以把以前项目的targetSdk=22的改成了26,要开始处理Android 6.0的动态权限,7.0的FileProvider,8.0的通知栏和安装未知应用的权限。对老项目修改最麻烦的是,你得一个一个找哪些地方用到了危险权限,也可以直接设为26后,关闭所有权限,对app每个功能都点点,奔溃就是缺少权限了。在项目中,我是使用RxPermission管理权限。

6.0 Android权限

6.0动态申请危险权限,先在AndroidManifest.xml里声明,再在代码里申请。危险权限分9组,分别是联系人、通话、日历、相机、传感器、定位、存储、音视频/录音、短信权限。

动态权限请求和处理流程
//1.判断是否已经授权
ContextCompat.checkSelfPermission(context,permissions[i]) != PackageManager.PERMISSION_GRANTED)

//2.请求权限
ActivityCompat.requestPermissions(context,permissions,mRequestCode);

//3.请求权限的回调  grantResults值为0允许,1拒绝
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)

//4.这个方法的理解是,是否需要告诉用户请求权限的原因,那么用户直接拒绝时,最好给用户一个提示说明该权限用来做什么的,所以返回true。而用户允许了权限 or 点了不再询问,此时没必要再提示用户请求权限的原因,所以返回false。所以在回调中,如果获取权限失败,合理的做法是判断是否需要给用户一个提示说明。
ActivityCompat.shouldShowRequestPermissionRationale(context,permissions[i])
RxPermission简单用法
new RxPermissions(this).request(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
                .subscribe(granted -> {
                    if (granted) {//允许
                        
                    } else { //拒绝
                        ToastUtils.showToast("为了功能正常使用,请您开启存储权限");
                    }
                });

7.0 FileProvider

7.0以上的系统,应用间共享文件要通过FileProvider,如果还像以前那样用 [file://URI](file://uri/) 就会触发 FileUriExposeException。其实这个在引用第三方库的时候就看到了,比如腾讯的Bugly。可以全局搜索Uri.fromFile(xxx)定位到要修改的地方,比如app更新下载后的安装、拍照、通知图库更新等地方可能用到。
使用流程
使用流程很简单,分3步。
1)在AndroidMainfest中声明。





2)在res文件夹下,创建xml文件夹,在里面创建xml文件,我创建的是 provider_paths.xml文件。







3)代码中使用FileProvider,判断是否>=24(也就是Android 7.0),大于调用FileProvider。这里直接放出拉起系统安装页面的代码,关键就是FileProvider.getUriForFile(...)这个方法。

    /**
     * 判断是否在7.0以上,7.0以上要用FileProvider
     */
    private void installApp() {
        File file = new File(DownloadIntentService.downLoadPath);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri apkUri;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            //参数2就是AndroidManifest.xml中provider的authorities
            apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileProvider", file);
            //临时授权读该Uri代表的文件的权限,不然安装的时候会出现“解析软件包出现问题”。
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }else{
            apkUri = Uri.fromFile(file);
        }
        //注意别设置成setFlags(...)了,不然前面的addFlags就清掉了。
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        startActivity(intent);
    }

8.0 Andorid的通知栏和未知应用安装权限

NotificationChannel

targetSdk >= 26 时,系统不会默认添加Channel,反之低版本则会默认添加,所以要自己创建Channel。当创建Channel后,以前在Builder设置的如震动、声音等可能都会无效,要在Channel设置,我这里只是简单的显示通知栏,所以没有设置震动、声音等。

private  final String NOTIFICATION_CHANNEL_ID = "com.sz.dzh.dandroidsummary";
private  final String NOTIFICATION_CHANNEL_NAME = "apk_download_channel";
private NotificationManager mNotifyManager;  //通知管理类
private Notification mNotification;
private int downloadId = 101;

/**
 * 创建通知栏
 * targetSdk >= 26 时,系统不会默认添加Channel,反之低版本则会默认添加;
 * @param remoteViews
 */
private void createNotification(RemoteViews remoteViews){
    //1.创建通知通道
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        //参数:通道id、名字、优先级。
        NotificationChannel notifyChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
                NOTIFICATION_CHANNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT);
        notifyChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        mNotifyManager.createNotificationChannel(notifyChannel);
    }
    //2.创建Builder对象
    Notification.Builder builder;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        builder =  new Notification.Builder(this, NOTIFICATION_CHANNEL_ID);
    } else {
        builder =  new Notification.Builder(this);
    }
    builder.setContent(remoteViews)
            .setTicker("正在下载")
            .setSmallIcon(R.mipmap.ic_launcher);
    //3.将Builder对象转变成普通的notification
    mNotification = builder.build();
    mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotifyManager.notify(downloadId, mNotification);
}
未知应用安装权限

在Android8.0之前,未知应用安装权限是默认开启的。在Android8.0之后,未知应用安装权限默认关闭,且权限入口隐藏。未知应用就是没有在对应的应用市场上线的应用。所以在调安装方法前,要先询问是否已开启了未知应用安装的权限,没开启,引导用户去打开。

//记得在AndroidManifest.xml 中 声明权限
//

private final int REQUEST_CODE_APP_INSTALL = 312;
    /**
     * 因为涉及权限申请,所以把安装app的流程放到Activity里来。
     * 也可以考虑在Service中动态获取权限
     * 此处Service下载完成后,通过EventBus通知activity可以开始安装
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onDownloadSuccess(EventBean eventBean) {
       if(eventBean.getCode() == Constant.EventCode.DOWNLOAD_APK_SUCCESS){
           //8.0以上,判断未知应用安装权限是否开启,没开启引导用户去设置
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
               boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
               if (!hasInstallPermission) {
                   Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
                   startActivityForResult(intent,REQUEST_CODE_APP_INSTALL);
                   return;
               }
           }
           installApp();
       }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == REQUEST_CODE_APP_INSTALL){
            //回调再查一次是否开启权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
                if (hasInstallPermission) {
                    installApp();
                }else{
                    ToastUtils.showToast("若要安装,请允许未知应用安装权限");
                }
            }
        }
    }

    /**
     * 判断是否在7.0以上,7.0以上要用FileProvider
     */
    private void installApp() {
        File file = new File(DownloadIntentService.downLoadPath);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri apkUri;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            //参数2就是AndroidManifest.xml中provider的authorities
            apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileProvider", file);
            //临时授权读该Uri代表的文件的权限,不然安装的时候会出现“解析软件包出现问题”。
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }else{
            apkUri = Uri.fromFile(file);
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        startActivity(intent);
    }

遇到的问题

线上app的targetSdk是22,在把targetSdk改为26且做了上述修改后,我想试一下app更新下载,就把versionCode改低了,然后下载正常且也拉起了安装页面,但点击安装后,页面就显示了五个字——“应用未安装”,(一加手机测试)除此之外没有没有任何解释。。。


原因:低版本的targetSdkVersion不能覆盖高版本的targetSdkVersion。

参考

FileProvider
https://www.jianshu.com/p/47fcd7873f39
8.0通知栏不显示问题、apk更新安装
https://www.jianshu.com/p/ca88fcbbf6c5
https://www.cnblogs.com/nesger/p/9483582.html
NotificaitionChannel适配填坑指南
https://www.jianshu.com/p/99bc32cd8ad6

你可能感兴趣的:(把targetSdkVersion设置为26(Android 8.0)需要注意的地方)