Android各版本兼容性适配
Android 6
Android 7
在Android 7.0系统上,禁⽌向你的应⽤外公开 file:// URI,如果⼀项包含⽂件 file:// URI类型的Intent离开你的应⽤,应⽤失败,并出现FileUriExposedException异常,如调⽤系统相机拍照。若要在应⽤间共享⽂件,可以发送 content:// URI类型的Uri,并授予URI 临时访问权限,使⽤FileProvider类。
使⽤FileProvider的⼤致步骤如下:
1.在res下创建xml⽬录,在此⽬录下创建l⽂件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!-- 内部存储,等同于FilesDir,路径:/data/data/包名/files⽬录-->
<files-path
name="DocDir"
path="/"/>
<!-- 内部存储,等同于CacheDir,路径:/data/data/包名/cache⽬录-->
document有安卓版吗<cache-path
name="CacheDocDir"
path="/"/>
<!--外部存储,等同于ExternalFilesDir,路径:/storage/sdcard/Android/data/包名/files-->
<external-files-path
name="ExtDocDir"
path="/"/>
<!--外部存储,等同于ExternalCacheDir 路径:/storage/sdcard/Android/data/包名/cache-->
<external-cache-path
name="ExtCacheDir"
path="/"/>
</paths>
2.在manifest中注册provider
<provider
name="t.FileProvider"
authorities="ploreapp.fileprovider"
exported="false"
grantUriPermissions="true">
<!--exported:要为false,为true则会报安全异常。grantUriPermissions为true,表⽰授予URI临时访问权限-->
<meta-data
name="FILE_PROVIDER_PATHS"
resource="@xml/file_paths"/>
</provider>
3.使⽤FileProvider
val file =File(
getExternalFilesDir(null),
"/temp/"+ System.currentTimeMillis()+".jpg"
)
if(!ists()){
file.parentFile.mkdirs()
}
//通过FileProvider创建⼀个content类型的Uri
val imageUri = UriForFile(
this,
"ploreapp.fileprovider", file
)
val intent =Intent()
//表⽰对⽬标应⽤临时授权该Uri所代表的⽂件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.action = MediaStore.ACTION_IMAGE_CAPTURE
//将拍摄的照⽚保存到特定的URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivity(intent)
Android 8
Android 8.0 引⼊了通知渠道,允许为要显⽰的每种通知类型创建⽤户可⾃定义的渠道,⽤户界⾯将通知渠道称之为通知类别。
private fun createNotification(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE)as NotificationManager
//如果要分组,groupId要唯⼀
val groupId ="group1"
val group =NotificationChannelGroup(groupId,"advertisement")
//channelId唯⼀
val channelId ="channel1"
val notificationChannel =NotificationChannel(
channelId,
"promote information",
NotificationManager.IMPORTANCE_DEFAULT
)
//将渠道添加进组,必须先创建组才能添加
/
/创建通知
val notification = Notification.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))
.setContentTitle("A new notice")
.setContentText("Likes and follows")
.setAutoCancel(true)
.build()
}
}
Android 8.0以后不允许后台应⽤启动后台服务,需要通过startForegroundService()指定为前台服务,应⽤有五秒的时间来调⽤该Service 的 startForeground() ⽅法以显⽰可见通知。 如果应⽤在此时间限制内未调⽤startForeground(),则系统将停⽌ Service 并声明此应⽤为 ANR。
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val intent =Intent(this, UncleService::class.java)
startForegroundService(intent)
}
class UncleService :Service(){
override fun onCreate(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val manager =getSystemService(NOTIFICATION_SERVICE)as NotificationManager
val channel =
NotificationChannel("channelId","channelName", NotificationManager.IMPORTANCE_HIGH)
val notification = Notification.Builder(this,"channelId")
.build()
startForeground(1, notification)
}
}
override fun onDestroy(){
stopForeground(true)
}
override fun onBind(p0: Intent?): IBinder?{
return null
}
}
别忘了在manifest添加权限
<uses-permission name="android.permission.FOREGROUND_SERVICE"/>
Android 9
在Android 9中的⽹络请求中,不允许使⽤http请求,要求使⽤https。
解决⽅案:
在 res 下新建⼀个xml⽬录,然后创建⼀个名为:l ⽂件
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
然后在Mainfiests appliction标签下配置该属性
android:networkSecurityConfig="@xml/network_config"
这是⼀种简单粗暴的⽅法,为了安全灵活,我们可以指定http域名,部分域名时使⽤http
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true"&le</domain>
</domain-config>
</network-security-config>
Android 10
定位权限
⽤户可以更好地控制应⽤何时可以访问设备位置,当在Android 10上运⾏的应⽤程序请求位置访问时,会通过对话框的形式给⽤户进⾏授权提⽰,此时有两种位置访问权限:在使⽤中(仅限前台)或始终(前台和后台)
新增权限 ACCESS_BACKGROUND_LOCATION
如果你的应⽤针对 Android 10并且需要在后台运⾏时访问⽤户的位置,则必须在应⽤的清单⽂件中声明新权限
<uses-permission name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission name="android.permission.ACCESS_FINE_LOCATION"/>
分区存储
在Android 10之前的版本上,我们在做⽂件的操作时都会申请存储空间的读写权限。但是这些权限完全被滥⽤,造成的问题就是⼿机的存储空间中充斥着⼤量不明作⽤的⽂件,并且应⽤卸载后它也没有删除掉。为了解决这个问题,Android 10 中引⼊了分区存储的概念,通过添加外部存储访问限制来实现更好的⽂件管理。但是应⽤得不彻底,因为我们可以在l中添加
android:requestLegacyExternalStorage="true"来请求使⽤旧的存储模式,以此来做简单粗暴地适配。但是我不推荐这样做,因为Android 11强制执⾏分区存储机制,此配置已经将会失效,所以还得⽼⽼实实地做下适配,直接看下⾯Android 11的适配吧。
Android 11
⽆需存储权限即可访问的有两种,⼀是App⾃⾝的内部存储,⼀是App⾃⾝的⾃带外部存储。
对于存储作⽤域访问的区别就体现在如何访问除此之外的⽬录内的⽂件。
强制执⾏分区存储
共享存储空间存放的是图⽚、视频、⾳频等⽂件,这些资源是公⽤的,所有App都能够访问它们。共享存储空间⾥存放着图⽚、视频、⾳频、下载的⽂件,App获取或者插⼊⽂件的时候怎么区分这些类型呢?这个时候就需要MediaStore。⽐如想要查询共享存储空间⾥的图⽚⽂件:
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null,
null,
null,
null
)
MediaStore.Images.Media.EXTERNAL_CONTENT_URI 意思是指定查询⽂件的类型是图⽚,并构造成Uri对象,Uri实现了Parcelable,能够在进程间传递。
既然不能通过路径直接访问⽂件,那么如何通过Uri访问⽂件呢?Uri可以通过MediaStore或SAF获取。但是,需要注意的是:虽然也可以通过⽂件路径直接构造Uri,但是此种⽅式构造的Uri是没有权限访问⽂件的。
现在我们来读取/sdcard/⽬录下的⼀个⽂本⽂件,由于它不属于共享存储空间的⽂件,是属于其它⽬录的,因此不能通过MediaStore获取,只能通过SAF获取。
private fun openSAF(){
val intent =Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
//指定选择⽂本类型的⽂件
startActivityForResult(intent,1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int,data: Intent?){
if(requestCode ==1&&data!=null){
val uri =data.data
startRead(uri!!)
}
}
private fun startRead(uri: Uri){
try{
val inputStream = contentResolver.openInputStream(uri)
val readContent =ByteArray(1024)
var len: Int
do{
len = inputStream!!.read(readContent)
if(len !=-1){
Log.d(tag,"file content:${String(readContent).substring(0, len)}")
}
}while(len !=-1)
}catch(e: Exception){
Log.d(tag,"Exception:${e.message}")
}
}
由此可以看出,属于"其它⽬录"下的⽂件,需要通过SAF访问,SAF返回Uri,通过Uri构造InputStream即可读取⽂件。
下⾯,我们再来写⼊内容到该⽂件中,还是需要通过SAF拿到Uri,拿到Uri后构造输出流。
private fun writeForUri(uri: Uri){
try{
val outputStream = contentResolver.openOutputStream(uri)
val content ="my name is Uncle Xing"
outputStream?.ByteArray())
outputStream?.flush()
outputStream?.close()
}catch(e: Exception){
Log.d(tag,"Exception:${e.message}")
}
}
SAF好处是:系统提供了⽂件选择器,调⽤者只需指定要读写的⽂件类型,⽐如⽂本类型、图⽚类型、视频类型等,选择器就会过滤出相应⽂件以供选择,使⽤简单。
单次授权
从 Android 11 开始,每当应⽤请求与位置信息、麦克风或摄像头相关的权限时,⾯向⽤户的权限对话框会包含仅限这⼀次选项。如果⽤户在对话框中选择此选项,系统会向应⽤授予临时的单次授权。
权限对话框的三个选择是:拒绝,本次运⾏允许,仅在使⽤中允许。
位置权限
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论