美文网首页
把targetSdkVersion设置为26(Android 8

把targetSdkVersion设置为26(Android 8

作者: Dengszzzzz | 来源:发表于2019-07-10 10:47 被阅读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中声明。

    <!--
    解释:
    android:authorities="${applicationId}.fileProvider" 就是 app的包名.fileProvider
    android:exported="false" 为false,不允许被另一个Application的组件启动
    android:grantUriPermissions="true" 为true,才能获取临时共享权限。
    -->
    <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/provider_paths" />
    </provider>
    

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

    <?xml version="1.0" encoding="utf-8"?>
    <!--
    对应的关系如下:
    <external-path/>           Environment.getExternalStorageDirectory()
    <files-path/>              getFilesDir()
    <cache-path/>              getCacheDir()
    <external-cache-path/>     getExternalCacheDir()
    <external-files-path/>     getExternalFilesDir()
    <root-path/>               这个应该是Android设备的根目录
    path="xxx"表示你要共享的文件夹
    -->
    <!--
    下面这句代表的目录即为:Environment.getExternalStorageDirectory()/com.sz.dzh.dandroidsummary/download/
    -->
    <paths>
    <external-path name="download_apk_path" path="com.sz.dzh.dandroidsummary/download/"/>
    </paths>
    

    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 中 声明权限
    //<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    
    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

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