安卓版本适配信息汇总,最后更新于2019-1-24 18:11:25,持续收集中
-
1:安卓4.4 API-19
- 1.1:应用支持全屏模式,也就是常说的 沉浸式 ,这一块踩坑存在bug
- 1.2:虚拟按键可隐藏,既然沉浸式底部导航栏跟着支持了,那就顺路把相关控制API也暴露给开发者
- 1.3:为了加强
WebView
的功能,Google引入了Chromium
内核。但仍然还是存在 编辑的bug
-
2:安卓5.0 API-21
-
2.1:ART androidRunTime取代 Dalvik 成为平台默认设置,ART采用预先编译技术,改进了垃圾回收机制与调试支持
-
2.2:
Material Design
设计规范,以及Appcompat-V7
RecyclerView-V7
CardView-V7
等框架,在UI上突出的变化还有SharedElementAnimation
elevation
等。 -
2.3:提升用户隐私的安全性,弃用
activityManager.getRecentTask()
方法。对于向后兼容性,此方法仍会返回它的一小部分数据,包括应用自己的任务和可能的一些其他非敏感任务(如首页)。如果你的应用使用此方法检索它自己的任务,则改用getAppTasks
检索信息 -
2.4:禁用隐式意图启动服务,运行时会直接抛出
IllegalArgumentException
异常Intent intent = new Intent();过时禁用 intent.setAction("com.example.myapplication"); startService(intent); 只能使用显式意图 Intent intent = new Intent("com.example.myapplication");
-
2.5:
Notification
模块不支持设置了rgb通道的smallIcon
,此bug 在后续版本已经修复,但是为了系统兼容性,建议开发者在安卓5.0及以上设备上更换smallIcon以达到版本一致性 -
2.6:系统 默认会阻止混合内容和第三方 Cookie。要允许混合内容请调用
setMixedContentMode()
,要允许第三方 Cookie,请调用setAcceptThirdPartyCookies()
。 -
2.7:现在可以智能地选择要绘制的 HTML 文档部分。这个新的默认行为有助于减少内存占用和提升性能。如果您要一次渲染整个文档,可通过调用
enableSlowWholeDocumentDraw()
停用此优化。 -
2.8:自定义权限必须保证唯一,使用不同秘钥签名的应用不能使用相同的自定义权限。如果用户设备上存在一个应用使用了相同的自定义权限,那么新的应用将无法安装
-
2.9:移除了对锁定屏幕小部件的支持
-
2.10:新增
Intent.resolveActivity(*)
对发起的意图进行设备匹配
-
-
3:安卓6.0 API-23
-
3.1:移除了
Apache
的http
支持包,如有需要手动引入。android { useLibrary 'org.apache.http.legacy' }
-
3.2:废弃:
FloatMath
,Notification.setLastEventInfo()
-
3.3:移除了对设备本地硬件标识符的编程访问权。
WifiInfo.getMacAddress()
方法和BluetoothAdapter.getAddress()
方法现在会返回常量值02:00:00:00:00:00
。如果需要通过蓝牙和wifi扫描访问外部设备的硬件标识符,应用必须拥有ACCESS_FINE_LOCATION
或ACCESS_COARSE_LOCATION
权限。 -
3.4:音频管理器变更:不再支持通过
AudioManager
类直接设置音量或将特定音频流静音。setStreamSolo()方法弃用,改为requestAudioFocus()
方法。setStreamMute() 方法弃用,改为adjustStreamVolume()
并传入方向值ADJUST_MUTE/ ADJUST_UNMUTE
。 -
3.5:APK 验证更为严格。如果在清单中声明的文件在 APK 中并不存在,该 APK 将被视为已损坏。移除任何内容后必须重新签署 APK。
-
3.6:运行时权限管理,谷歌借鉴了苹果 和国内厂商的优化经验,对应用的权限不再一刀切,而是把相关权限分成了三组
- 特殊权限 每次冷启动APP都会重置状态,不建议应用申请
权限名称 权限说明 SYSTEM_ALERT_WINDOW
设置悬浮窗,TYPE_SYSTEM_ALERT已失效 WRITE_SETTINGS
修改系统设置 - 敏感权限 运行时申请,如果没有适配会直接抛出异常,许可状态会保存
权限组 权限名称 介绍 CALENDAR android.permission.READ_CALENDAR
读取系统日历 android.permission.WRITE_CALENDAR
写入系统日历 CAMERA android.permission.CAMERA
相机权限 CONTACTS android.permission.READ_CONTACTS
读取联系人 android.permission.WRITE_CONTACTS
写入联系人 android.permission.GET_ACCOUNTS
读取账号 LOCATION android.permission.ACCESS_FINE_LOCATION
获取精准位置 android.permission.ACCESS_COARSE_LOCATION
获取大概位置 MICROPHONE android.permission.RECORD_AUDIO
录音 PHONE android.permission.READ_PHONE_STATE
获取手机信息 android.permission.CALL_PHONE
打电话,不建议使用 android.permission.READ_CALL_LOG
读取通话记录 android.permission.WRITE_CALL_LOG
写入通话记录 android.permission.PROCESS_OUTGOING_CALLS
监听、控制、取消呼出电话的权限 android.permission.USE_SIP
使用sip(会话发起协议)网络电话 android.permission.ADD_VOICEMAIL
添加系统中的语音邮件 SENSORS android.permission.BODY_SENSOR
使用生命体征相关传感器 SMS android.permission.SEND_SMS
发送短信 android.permission.RECEIVE_SMS
接收短信 android.permission.READ_SMS
读取短信 android.permission.RECEIVE_WAP_PUSH
接收WapPush信息 android.permission.RECEIVE_MMS
接收彩信 android.permission.READ_CELL_BROADCASTS
不存在的权限 STORAGE android.permission.READ_EXTERNAL_STORAGE
读取存储 android.permission.WRITE_EXTERNAL_STORAGE
写入存储 运行时权限共9组,同一组下如果有一个权限被允许,其它权限会自动被允许。
开发时应减少申请权限的次数,以避免交互被打断。对于敏感权限可用intent来代替,让其它应用去处理
-
-
4:安卓7.0 API-24
-
4.1:应用分屏,支持java8
-
4.2:
Notification.Builder
通知消息可以通过setShowWhen()
设置定时显示 -
4.3:删除了三个常用隐式广播 :
CONNECTIVITY_ACTION
,ACTION_NEW_PICTURE
,ACTION_NEW_VIDEO
。因为这些广播可能会一次唤醒多个应用的后台进程,消耗内存和电池 -
4.4:优化了
SurfaceView
,使其在视频播放和3D方面表现更优于TextureView
。 -
4.5:APK signature scheme v2,android 7.0 新引入应用签名方案,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。
-
4.6:
StrictMode API
政策禁止在您的应用外部公开file:// URI
。如果启动一个包含文件 URI 的intent
会抛出FileUriExposedException
异常。受此影响的有拍照图片选择,下载apk安装等功能。要在应用间共享文件,您应发送一项content:// URI
,并授予 URI 临时访问权限。拍照intent示例:
Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 添加这一句表示对目标应用临时授权该Uri所代表的文件 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } 将拍照结果保存至photo_file的Uri中,不保留在相册中 intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); activity.startActivityForResult(intentCamera, requestCode);
安装APK intent如下
Intent installIntent = new Intent(Intent.ACTION_VIEW)); installIntent.addCategory(Intent.CATEGORY_DEFAULT); installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { String auth = null; PackageManager manager = context.getPackageManager(); PackageInfo packageInfo = manager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS); for (ProviderInfo provider : packageInfo.providers) { if ("FileProvider".equals(provider.name) && provider.authority.endsWith(".update.file_provider")) { auth = provider.authority; break; } } Uri uriForFile = FileProvider.getUriForFile(context, auth, apkFile); installIntent.setDataAndType(uriForFile, "application/vnd.android.package-archive"); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { installIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); } context.startActivity(installIntent);
-
4.7:加密库 Crypto废弃 将密码作为随机数生成器的种子换算出密钥 key这种做法已经被认定为是不安全的。相关的 Crypto provider 和 SHA1PRNG 算法同时废弃掉了,并计划在后续的 SDK 中完全移除相关的库,如下是官方给出的解决方案:
给出字符串的密码 String password = "password"; 密钥的比特位数,注意这里是比特位数 AES 支持 128、192 和 256 比特长度的密钥 int keyLength = 256; 盐值的字节数组长度,注意这里是字节数组的长度 其长度值需要和最终输出的密钥字节数组长度一致 由于这里密钥的长度是 256 比特,则最终密钥将以 256/8 = 32 位长度的字节数组存在 所以盐值的字节数组长度也应该是 32 int saltLength = 32; byte[] salt; 先获取一个随机的盐值 你需要将此次生成的盐值保存到磁盘上下次再从字符串换算密钥时传入 如果盐值不一致将导致换算的密钥值不同 保存密钥的逻辑官方并没写,需要自行实现 SecureRandom random = new SecureRandom(); byte[] salt = new byte[saltLength]; random.nextBytes(salt); 将密码明文、盐值等使用新的方法换算密钥 int iterationCount = 1000; KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,iterationCount, keyLength); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 到这里你就能拿到一个安全的密钥了 byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); SecretKey key = new SecretKeySpec(keyBytes, "AES");
-
-
5:安卓8.0 API-26 (8.1 API-27)
- 5.1:roundedIcon 自适应图标,在manifest文件中额外提供圆形应用icon
<application android:name=".App" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" ......
- 5.2:PHONE权限组新增两个权限
-
ANSWER_PHONE_CALLS
:允许接听呼入电话。在应用中使用acceptRingingCall()
函数处理呼入电话。 -
READ_PHONE_NUMBERS
:允许应用读取设备中存储的电话号码。
-
- 5.3:安卓8.0中,Notification的通知渠道。用户可以根据渠道来屏蔽一些不想要的通知
void setNotifyChannel(NotificationManager manager, NotificationCompat.Builder builder, String channeId, String channelName) { if (TextUtils.isEmpty(channeId)||TextUtils.isEmpty(channelName)){ L.e("NotifyCompat: ".concat("安卓8.0的通知兼容库中 channeId 与 channelName 不能为empty")); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //第三个参数设置通知的优先级别 NotificationChannel channel = new NotificationChannel(channeId, channelName, NotificationManager.IMPORTANCE_DEFAULT); channel.canBypassDnd();//是否可以绕过请勿打扰模式 channel.canShowBadge();//是否可以显示icon角标 channel.enableLights(true);//是否显示通知闪灯 channel.enableVibration(true);//收到小时时震动提示 channel.setBypassDnd(true);//设置绕过免打扰 channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_SECRET); channel.setLightColor(Color.RED);//设置闪光灯颜色 channel.getAudioAttributes();//获取设置铃声设置 channel.setVibrationPattern(new long[]{100, 200, 100});//设置震动模式 channel.shouldShowLights();//是否会闪光 if (manager != null) { manager.createNotificationChannel(channel); //manager 与channel关联 } if (builder != null) { builder.setChannelId(channeId);//Notification 与channel关联 } } }
- 5.4:新增普通权限:
REQUEST_INSTALL_PACKAGES
安装未知来源应用,也可以使用canRequestPackageInstalls()
查询是否有此权限,如果没有的话使用Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES
这个action将用户引导至安装未知应用权限界面去授权。private void installAPK(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls(); if (hasInstallPermission) { //安装应用 } else { //跳转至“安装未知应用”权限界面,引导用户开启权限 Uri selfPackageUri = Uri.parse("package:" + this.getPackageName()); Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri); startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP); } }else { //安装应用 } }
- 5.5:SharedPreferences不能使用
MODE_WORLD_READABLE
,请修改为MODE_PRIVATE
- 5.6:新的广播接收器限制导致静态广播无法正常接收,应使用动态广播代替静态广播
- 5.7:
Only fullscreen opaque activities can request orientation
,在Android 8.0上非全屏透明页面不允许设置方向(8.1以上谷歌就修复去掉了这个限制)解决方案1:在manifest中去掉此Activity的
screenOrientation
属性
解决方案2:在此Activity的style里设置<item name="android:windowIsTranslucent">false</item>
解决方案3:使用Dialog/DialogFragment/Popwindow
代替此Activity。
- 5.1:roundedIcon 自适应图标,在manifest文件中额外提供圆形应用icon
-
6:安卓9.0 API-28
- 6.1:限制明文流量的网络请求,非加密的流量请求会被系统禁止掉
解决方案:在xml目录新建文件network_security_config.xml
在manifest清单文件application节点配置<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
android:networkSecurityConfig="@xml/network_security_config"
- 6.2:
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
安卓9.0对canvas#clipPath()中的Region.Op做了限制只能使用INTERSECT或者DIFFERENCE。if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { canvas.clipPath(mPath); } else { canvas.clipPath(mPath, Region.Op.REPLACE); }
- 6.1:限制明文流量的网络请求,非加密的流量请求会被系统禁止掉
网友评论