網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Android 6
運(yùn)行時(shí)權(quán)限動(dòng)態(tài)申請(qǐng),這里推薦郭霖的開源庫(kù):https://github.com/guolindev/PermissionX
Android 7
在Android 7.0系統(tǒng)上,禁止向你的應(yīng)用外公開 file:// URI,如果一項(xiàng)包含文件 file:// URI類型的Intent離開你的應(yīng)用,應(yīng)用失敗,并出現(xiàn) FileUriExposedException異常,如調(diào)用系統(tǒng)相機(jī)拍照。若要在應(yīng)用間共享文件,可以發(fā)送 content:// URI類型的Uri,并授予URI 臨時(shí)訪問權(quán)限,使用FileProvider類。
使用FileProvider的大致步驟如下:
1.在res下創(chuàng)建xml目錄,在此目錄下創(chuàng)建file_paths.xml文件,內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?> <paths> <!-- 內(nèi)部存儲(chǔ),等同于Context.getFilesDir,路徑:/data/data/包名/files目錄--> <files-path name="DocDir" path="/" /> <!-- 內(nèi)部存儲(chǔ),等同于Context.getCacheDir,路徑:/data/data/包名/cache目錄--> <cache-path name="CacheDocDir" path="/" /> <!--外部存儲(chǔ),等同于Context.getExternalFilesDir,路徑:/storage/sdcard/Android/data/包名/files--> <external-files-path name="ExtDocDir" path="/" /> <!--外部存儲(chǔ),等同于Context.getExternalCacheDir 路徑:/storage/sdcard/Android/data/包名/cache--> <external-cache-path name="ExtCacheDir" path="/" /> </paths>
2.在manifest中注冊(cè)provider
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.unclexing.exploreapp.fileprovider" android:exported="false" android:grantUriPermissions="true"> <!--exported:要為false,為true則會(huì)報(bào)安全異常。grantUriPermissions為true,表示授予URI臨時(shí)訪問權(quán)限--> <meta-data android:name="androidx.core.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
3.使用FileProvider
val file = File(
getExternalFilesDir(null),
"/temp/" + System.currentTimeMillis() + ".jpg"
)
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}
//通過FileProvider創(chuàng)建一個(gè)content類型的Uri
val imageUri = FileProvider.getUriForFile(
this,
"com.unclexing.exploreapp.fileprovider", file
)
val intent = Intent()
//表示對(duì)目標(biāo)應(yīng)用臨時(shí)授權(quán)該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 引入了通知渠道,允許為要顯示的每種通知類型創(chuàng)建用戶可自定義的渠道,用戶界面將通知渠道稱之為通知類別。
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")
notificationManager.createNotificationChannelGroup(group)
//channelId唯一
val channelId = "channel1"
val notificationChannel = NotificationChannel(
channelId,
"promote information",
NotificationManager.IMPORTANCE_DEFAULT
)
//將渠道添加進(jìn)組,必須先創(chuàng)建組才能添加
notificationChannel.group = groupId
notificationManager.createNotificationChannel(notificationChannel)
//創(chuàng)建通知
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()
notificationManager.notify(1, notification)
}
}
Android 8.0以后不允許后臺(tái)應(yīng)用啟動(dòng)后臺(tái)服務(wù),需要通過startForegroundService()指定為前臺(tái)服務(wù),應(yīng)用有五秒的時(shí)間來調(diào)用該 Service 的 startForeground() 方法以顯示可見通知。 如果應(yīng)用在此時(shí)間限制內(nèi)未調(diào)用startForeground(),則系統(tǒng)將停止 Service 并聲明此應(yīng)用為 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() {
super.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)
manager.createNotificationChannel(channel)
val notification = Notification.Builder(this, "channelId")
.build()
startForeground(1, notification)
}
}
override fun onDestroy() {
super.onDestroy()
stopForeground(true)
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}
別忘了在manifest添加權(quán)限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
Android 9
在Android 9中的網(wǎng)絡(luò)請(qǐng)求中,不允許使用http請(qǐng)求,要求使用https。
解決方案:
在 res 下新建一個(gè)xml目錄,然后創(chuàng)建一個(gè)名為:network_config.xml 文件
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
然后在Mainfiests appliction標(biāo)簽下配置該屬性
android:networkSecurityConfig="@xml/network_config"
這是一種簡(jiǎn)單粗暴的方法,為了安全靈活,我們可以指定http域名,部分域名時(shí)使用http
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">csdn.example.com</domain> </domain-config> </network-security-config>
Android 10
定位權(quán)限
用戶可以更好地控制應(yīng)用何時(shí)可以訪問設(shè)備位置,當(dāng)在Android 10上運(yùn)行的應(yīng)用程序請(qǐng)求位置訪問時(shí),會(huì)通過對(duì)話框的形式給用戶進(jìn)行授權(quán)提示,此時(shí)有兩種位置訪問權(quán)限:在使用中(僅限前臺(tái))或始終(前臺(tái)和后臺(tái))
新增權(quán)限 ACCESS_BACKGROUND_LOCATION
如果你的應(yīng)用針對(duì) Android 10并且需要在后臺(tái)運(yùn)行時(shí)訪問用戶的位置,則必須在應(yīng)用的清單文件中聲明新權(quán)限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
分區(qū)存儲(chǔ)
在Android 10之前的版本上,我們?cè)谧鑫募牟僮鲿r(shí)都會(huì)申請(qǐng)存儲(chǔ)空間的讀寫權(quán)限。但是這些權(quán)限完全被濫用,造成的問題就是手機(jī)的存儲(chǔ)空間中充斥著大量不明作用的文件,并且應(yīng)用卸載后它也沒有刪除掉。為了解決這個(gè)問題,Android 10 中引入了分區(qū)存儲(chǔ)的概念,通過添加外部存儲(chǔ)訪問限制來實(shí)現(xiàn)更好的文件管理。但是應(yīng)用得不徹底,因?yàn)槲覀兛梢栽贏ndroidManifest.xml中添加android:requestLegacyExternalStorage="true"來請(qǐng)求使用舊的存儲(chǔ)模式,以此來做簡(jiǎn)單粗暴地適配。但是我不推薦這樣做,因?yàn)锳ndroid 11強(qiáng)制執(zhí)行分區(qū)存儲(chǔ)機(jī)制,此配置已經(jīng)將會(huì)失效,所以還得老老實(shí)實(shí)地做下適配,直接看下面Android 11的適配吧。
Android 11
無需存儲(chǔ)權(quán)限即可訪問的有兩種,一是App自身的內(nèi)部存儲(chǔ),一是App自身的自帶外部存儲(chǔ)。
對(duì)于存儲(chǔ)作用域訪問的區(qū)別就體現(xiàn)在如何訪問除此之外的目錄內(nèi)的文件。
強(qiáng)制執(zhí)行分區(qū)存儲(chǔ)
共享存儲(chǔ)空間存放的是圖片、視頻、音頻等文件,這些資源是公用的,所有App都能夠訪問它們。共享存儲(chǔ)空間里存放著圖片、視頻、音頻、下載的文件,App獲取或者插入文件的時(shí)候怎么區(qū)分這些類型呢?這個(gè)時(shí)候就需要MediaStore。比如想要查詢共享存儲(chǔ)空間里的圖片文件:
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null,
null,
null,
null
)
MediaStore.Images.Media.EXTERNAL_CONTENT_URI 意思是指定查詢文件的類型是圖片,并構(gòu)造成Uri對(duì)象,Uri實(shí)現(xiàn)了Parcelable,能夠在進(jìn)程間傳遞。
既然不能通過路徑直接訪問文件,那么如何通過Uri訪問文件呢?Uri可以通過MediaStore或SAF獲取。但是,需要注意的是:雖然也可以通過文件路徑直接構(gòu)造Uri,但是此種方式構(gòu)造的Uri是沒有權(quán)限訪問文件的。
現(xiàn)在我們來讀取/sdcard/目錄下的一個(gè)文本文件NewTextFile.txt,由于它不屬于共享存儲(chǔ)空間的文件,是屬于其它目錄的,因此不能通過MediaStore獲取,只能通過SAF獲取。
private fun openSAF() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
//指定選擇文本類型的文件
intent.type = "text/plain"
startActivityForResult(intent, 1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
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構(gòu)造InputStream即可讀取文件。
下面,我們?cè)賮韺懭雰?nèi)容到該文件中,還是需要通過SAF拿到Uri,拿到Uri后構(gòu)造輸出流。
private fun writeForUri(uri: Uri) {
try {
val outputStream = contentResolver.openOutputStream(uri)
val content = "my name is Uncle Xing"
outputStream?.write(content.toByteArray())
outputStream?.flush()
outputStream?.close()
} catch (e: Exception) {
Log.d(tag, "Exception:${e.message}")
}
}
SAF好處是:系統(tǒng)提供了文件選擇器,調(diào)用者只需指定要讀寫的文件類型,比如文本類型、圖片類型、視頻類型等,選擇器就會(huì)過濾出相應(yīng)文件以供選擇,使用簡(jiǎn)單。
位置權(quán)限
Android 10請(qǐng)求ACCESS_FINE_LOCATION或 ACCESS_COARSE_LOCATION表示在前臺(tái)時(shí)擁有訪問設(shè)備位置信息的權(quán)限。在請(qǐng)求彈框還能看到始終允許,Android 11中,取消了始終允許選項(xiàng),默認(rèn)不會(huì)授予后臺(tái)訪問設(shè)備位置信息的權(quán)限。Android 11將后臺(tái)獲取設(shè)備位置信息抽離了出來,通過ACCESS_BACKGROUND_LOCATION權(quán)限后臺(tái)訪問設(shè)備位置信息的權(quán)限,需要注意的一點(diǎn)是,請(qǐng)求ACCESS_BACKGROUND_LOCATION的同時(shí)不能請(qǐng)求其它權(quán)限,否則系統(tǒng)會(huì)拋出異常。官方給出的建議是先請(qǐng)求前臺(tái)位置信息訪問權(quán)限,再請(qǐng)求后臺(tái)位置信息訪問權(quán)限。
原文鏈接:https://blog.csdn.net/qq_45485851/article/details/122417968
相關(guān)推薦
- 2022-03-07 Android顯示系統(tǒng)SurfaceFlinger分析_Android
- 2022-10-24 C語(yǔ)言詳解分析進(jìn)程控制中進(jìn)程終止的實(shí)現(xiàn)_C 語(yǔ)言
- 2023-04-14 python使用pyodbc連接sqlserver_python
- 2022-11-01 AndroidView與Compose框架交互實(shí)現(xiàn)介紹_Android
- 2023-10-15 element-ui里el-progress:進(jìn)度條問題的解決Invalid prop: custo
- 2023-03-22 PyTorch中torch.tensor()和torch.to_tensor()的區(qū)別_python
- 2022-06-11 shell編程中for循環(huán)語(yǔ)句的實(shí)現(xiàn)過程及案例_linux shell
- 2022-05-12 android okHttp網(wǎng)絡(luò)請(qǐng)求封裝
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支