把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中声明。

<!--
解释:
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
//www.greatytc.com/p/47fcd7873f39
8.0通知栏不显示问题、apk更新安装
//www.greatytc.com/p/ca88fcbbf6c5
https://www.cnblogs.com/nesger/p/9483582.html
NotificaitionChannel适配填坑指南
//www.greatytc.com/p/99bc32cd8ad6

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352