Android 8.0 升级笔记(适配图片、通知栏、ContentProvider、Receiver)

Android 8.0 升级笔记

前言

Google 在2017年就发布了Android 8.0,并且强制AppStore上得应用都要升级,国内得不晓得。为了防止出现之前升级6.0 得时候权限问题导致Crash这种情况得发生…这次很小心得去看了Google得升级意见,小伙伴们可以自行去看(https://developer.android.com/index.html)。
我大致记录以下有以下几点需要注意的:

  • 升级API版本号到26
  • 移除隐式意图接收器
  • 更新应用图标

正题

Android studio 3.0.1 下载

提前说下Google 建议使用Android studio 3.0 以上得版本,给一个地址需要得自己去下载吧。

官方(需要翻墙):https://developer.android.com/studio/index.html
国内:http://www.android-studio.org/

编译版本&相关库升级

找到你项目的build.gradle,主要关注以下几个,升级到26

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.2"
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 26
    }
}

升级相关的支持库
如果你不这么做studio会提示警告,但是也不会影响运行,如果要用库里最新的控件还是更新下吧。代码洁癖的不会想看到大段的黄色警告和红色波浪线…

 compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:cardview-v7:26.1.0'
    compile 'com.android.support:design:26.1.0'

我暂时就用到这些,有些项目里有用到Google Service的服务也要升级到 11.8.0

注意一点是studio 3.0以下的版本会提示找不到 “26.1.0”的库,而且你在Sdk Manager也找不到,需要再project的build.gradle加google的库,jCneter 里没有。

allprojects {
    repositories {
        jcenter()
        google()
    }
}

或者这样写

maven {
            url 'https://maven.google.com/'
            name 'Google'
        }

当然如果是Studio3.0 以上的版本IDE 会自动帮你加上!

应用隐式意图检查

Google为了优化(填以前的坑)系统性能,让应用无法再Manifast注册隐式意图了,因为Google认为这样做太消耗资源,特么的开发者写的APP消耗资源,用户觉得性能差,出了事要Google的Android系统背锅!他们不干了,要开始约束开发者行为。

举个例子:

一款社交图片APP,开发者希望再每次链接数据线的时候备份照片并清理缓存。

那么他需要写一个广播接收器,并再manifest中注册这个接收器,接收ACTION_POWER_CONNECTED 的广播。

<receiver android:name="com.xxx.PowerConnectReceiver">
            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
            </intent-filter>
        </receiver>

再Android 8.0 上Google说你不能这么干!这会在每次链接电源的时候去通知所有注册了这个广播的应用浪费系统资源!所以移除吧。

当然也不是所有的意图都被过滤掉了,官网也说了有些意图不会受这条约束(https://developer.android.com/guide/components/broadcast-exceptions.html),我从官网摘录了下来,访问不了外网的可以搜以下你的应用有下面这些隐式意图的话就不用做8.0的适配,相反你就需要做适配

ACTION_LOCKED_BOOT_COMPLETED, ACTION_BOOT_COMPLETED
由于这些广播只能在第一次启动时只发送一次,因此许多应用程序需要接收此广播来安排作业,警报等等。

ACTION_USER_INITIALIZE, "android.intent.action.USER_ADDED", "android.intent.action.USER_REMOVED"
这些广播受到特权的保护,因此大多数普通应用程序无法收到它们。

"android.intent.action.TIME_SET", ACTION_TIMEZONE_CHANGED, ACTION_NEXT_ALARM_CLOCK_CHANGED
时钟应用程序可能需要接收这些广播,以在时间,时区或警报发生更改时更新警报。

ACTION_LOCALE_CHANGED
只有在语言环境发生变化时才会发送,这种情况并不常见。应用程序可能需要在区域设置更改时更新其数据。

ACTION_USB_ACCESSORY_ATTACHED, ACTION_USB_ACCESSORY_DETACHED, ACTION_USB_DEVICE_ATTACHED, ACTION_USB_DEVICE_DETACHED
如果一个应用程序需要了解这些USB相关事件,目前还没有一个好的选择来注册广播。

ACTION_CONNECTION_STATE_CHANGED, ACTION_CONNECTION_STATE_CHANGED, ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED
如果应用程序接收到这些蓝牙事件的广播,用户体验不太可能受到影响。

ACTION_CARRIER_CONFIG_CHANGED, TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED, "TelephonyIntents.SECRET_CODE_ACTION"
OEM电话应用程序可能需要接收这些广播。

LOGIN_ACCOUNTS_CHANGED_ACTION
某些应用程序需要知道登录帐户的更改,以便他们可以为新帐户和更改的帐户设置预定的操作。

ACTION_PACKAGE_DATA_CLEARED
只有在用户从“设置”中明确清除其数据时才会发送,因此广播接收器不太可能会显着影响用户体验。

ACTION_PACKAGE_FULLY_REMOVED
有些应用程序可能需要更新其存储的数据时,另一个包被删除; 对于这些应用程序,没有好的选择注册这个广播。

注意:其他与包相关的广播(例如ACTION_PACKAGE_REPLACED)不受新限制的限制。这些广播已经足够普遍,对于免除这些广播有潜在的性能影响。

ACTION_NEW_OUTGOING_CALL
针对拨打电话的用户采取行动的应用程序需要接收此广播。

ACTION_DEVICE_OWNER_CHANGED
这个广播不经常发送; 一些应用程序需要接收它,以便他们知道设备的安全状态已经改变。

ACTION_EVENT_REMINDER
由日历提供程序发送,以便将事件提醒发送到日历应用程序。由于日历提供程序不知道日历应用程序是什么,这个广播必须是隐含的。

ACTION_MEDIA_MOUNTED, ACTION_MEDIA_CHECKING, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_EJECT, ACTION_MEDIA_UNMOUNTABLE, ACTION_MEDIA_REMOVED, ACTION_MEDIA_BAD_REMOVAL
这些广播是由于用户与设备的物理交互(安装或移除存储卷)或作为引导初始化的一部分(因为可用卷被挂载)而发送的,因此它们不是常见的并且通常在用户的控制之下。

SMS_RECEIVED_ACTION, WAP_PUSH_RECEIVED_ACTION
这些广播是由SMS收件人应用程序依靠。

注:需要签名权限的广播不受此限制所限

那么想实现上面那个功能该怎么办呢?我就是想在应用中接收连接电源的广播要怎么做呢?

  • 让用户再Setting 中自己打开(基本不可取)
  • 在应用启动的时候动态的注册广播

    Context.registerReceiver()
  • 使用JobScheduler

简单一句介绍,5.0 之后系统会干掉轮询等保活机制,JobScheduler系统不会杀。

有兴趣的哥们自己去研究下,不难理解。

显示意图 & 隐式意图

举个例子,如果忘记了显示\隐式意图的可以看下。

显式意图

显式意图:调用Intent.setComponent()或Intent.setClass()方法明确指定了组件名的Intent为显式意图,显式意图明确指定了Intent应该传递给哪个组件。

Intent intent = new Intent(context,AnotherActivity.class);//第二个参数是待启动Activity的类的反射  
intent.putExtra("name",name);//可通过意图向另一个Activity传递数据  
startActivity(intent);//跳转到另一个Activity  
隐式意图

隐式意图:没有明确指定组件名的Intent为隐式意图。 Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URI和数据类型)找到最合适的组件来处理这个意图。

<intent-filter>  
   <action android:name="android.intent.yinsiyitu.action"/>  
   <category android:name="android.intent.category.DEFAULT"/>  
   <data android:mimeType="application/person"/>  
   <data android:scheme="jianren" android:host="www.ggl.com"/>  
</intent-filter>  

或者在Activity中写

Intent intent = new Intent();  
intent.setAction("android:intent.yinsiyitu.action");  
intent.addCategory(Intent.CATEGORY_DEFAULT);  
//intent.setData(Uri.parse(jianren://www.ggl.com));//会清除前面所有set的type  
//intent.setType("application/person");//会清除前面所有的set的data  
//这是setData和setType两全的方法,另外如果上面的Activity定义了host,则这里一定也要指定  
intent.setDataAndType(Uri.parse("jianren://www.ggl.com"),"application/person");  
//如果上面的Activity没有定义host,则Uri.parse("jianren:");至少要写到冒号,不可以只写Uri.parse("jianren")  
startActivity(intent);  

adative图片适配

这里写图片描述
官网上的图,我就放这装个逼,看起来比较屌 哈哈!

https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive.html

上面是官网的文档,去看下吧,我觉得我讲的不会有这个细了,我大致说下流程。

1.准备background、foreground icon

用人话说就是让UI给图,把logi拆成ICON和背景色 两张图

2.创建/res/mipmap-anydpi/ic_launcher.xml

<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Logo背景色 -->
    <background android:drawable="@color/ic_background"/>
    <!--Logo 图标-->
    <foreground android:drawable="@mipmap/ic_foreground_layer"/>
</adaptive-icon>

注:如果不做这个适配的话8.0 的设备上你的App 图标可能是很丑的一个被圆形包住方块
这里写图片描述

问UI小姐姐要了图,改完之后的。

这里写图片描述

其他一些API变动

官网上摘录下来的。

变化 摘要 其他参考资料
隐私性 Android 8.0 不支持使用 net.dns1、net.dns2、net.dns3 或 net.dns4 系统属性。 行为变更:隐私性
实行了可写且可执行的代码段 对于原生库,Android 8.0 实行的规则是:数据不应可执行,代码不应可写。 行为变更:原生库
ELF 标头和节验证 动态链接器对 ELF 标头和节头中的更多值进行检查,如果值无效则失败。 行为变更:原生库
通知 以 SDK 的 Android 8.0 版本为目标平台的应用必须实现一个或多个通知渠道,以便向用户发布通知。 API 概览:通知
List.sort() 方法 该方法的实现不得再调用 Collections.sort(),否则应用将因堆栈溢出而引发异常。 行为变更:集合的处理
Collections.sort() 方法 在列表实现中,Collections.sort() 现在会引发 ConcurrentModificationException 行为变更:集合的处理

适配后发现的一些问题

上次适配了图片和部分权限后,又发现了一些需要适配的地方.

通知权限

主要就是多了一个通道的概念,Channel ,简单的就是下面这样用 先创建一个channel,在向这个channel中发送通知,相同通道的通知可以归类,可以做到按照时间、按照用户群、按照年龄等创建通道,分类接收数据。不这么做的话收不到通知。

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if(notificationManager != null){
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                //创建channel
                NotificationChannel notificationChannel = new NotificationChannel("channel01","channel01_name"
                        ,NotificationManager.IMPORTANCE_DEFAULT);
                notificationChannel.enableLights(true);//是否允许桌面小红点, 8.0 上无效?
                notificationChannel.setLightColor(Color.RED);//桌面红点颜色
                notificationChannel.setSound(null,null);
                Toast.makeText(this,"创建",Toast.LENGTH_SHORT).show();
                notificationManager.createNotificationChannel(notificationChannel);
            }

            int notifyId = 1;
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"channel01");
            builder.setContentTitle("This is Content Title~");//设置内容信息
            builder.setContentInfo("This is Content Info~");//8.0 上无效

            builder.setOngoing(true);//禁止滑动删除
            builder.setSmallIcon(R.mipmap.ic_launcher);//设置左上角小图标
            builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_16_18));//右侧图片
            builder.setDefaults(Notification.DEFAULT_SOUND);//设置默认声音
            builder.setVibrate(new long[]{100,200,100,500,500});//设置震动 {间隔,时长,间隔,时长...}
            builder.setLights(Color.BLUE,1000,5000);//设置呼吸灯颜色 亮时间 暗时间 有些机型不支持
            builder.setChannelId("channel01");//设置通道,8.0 以上有用
            notificationManager.notify(notifyId,builder.build());

参考:Android O(8.0)通知栏适配

ContentProvider

Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在针对 Android 8.0 的应用中的行为方式。现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider。使用相关权限定义一个有效的 ContentProvider 可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。

官方的话,现象就是你registerContentProvider 或者 notifyChange 的时候如果没有注册明确的authorities的话那么他就会抛一个SecurityException 的异常Failed to find provider null for user 0; expected to find a valid ContentProvider for this authority。主要也是防止其他APP监听本APP的数据。

解决办法就是在AndroidManifest里去注册一下Providor,并且记住在NotifyChange的时候URI一定要传 content://authority/ authority别传null

这里写图片描述

借鉴 Android 8.0 java.lang.SecurityException: Failed to find provider for user 0 产生原因以及解决方法。

总结

比较重要的几个就是移除隐式意图,更换app logo图,升级相关库;如果有用到API 在上面有说明改变的也要注意以下,目前来看还没有遇到别的坑。发现来再来补上!

猜你喜欢

转载自blog.csdn.net/moxiouhao/article/details/79209054