Android調(diào)用其他應(yīng)用打開各種附件(.doc/.pdf/.xls)
最近重構(gòu)掌上重郵的寫下載文件附件時(shí)遇到了一個(gè)問題:
附件下載完成后需要打開文件啊,不同文件打開怎么處理才優(yōu)雅呢?
成功操作后踩坑記錄整理于此
更新
Android N 版本適配
避雷
本文例子均為Kotlin編寫
思路
首先打開文件這種跳轉(zhuǎn)肯定是發(fā)intent出去,然后就是怎么傳遞文件信息和要打開的文件類型了
這里我發(fā)現(xiàn)了intent.setDataAndType(uri, type)
正文
那么整體的寫法就應(yīng)該是:
注意,這段代碼有倆坑惑畴,后文說明
if (file.exists()) {
try {
startActivity(Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setDataAndType(Uri.fromFile(file), FileTypeHelper.getMIMEType(file)))
} catch (e: Exception) {
e.printStackTrace()
}
}
這里關(guān)鍵的就是獲取type的方法台妆,我單獨(dú)寫了一個(gè)Helper來處理蛋褥,因?yàn)閠ype畢竟很多彼宠,而且需要多次判斷。
import java.io.File
/**
* Author: Hosigus
* Date: 2018/9/27 17:56
* Description: 打開對應(yīng)文件需要的type
*/
object FileTypeHelper {
private val MIME_TABLE = mapOf(".doc" to "application/msword",
".docx" to "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".xls" to "application/vnd.ms-excel",
".xlsx" to "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".pdf" to "application/pdf",
".pps" to "application/vnd.ms-powerpoint",
".ppt" to "application/vnd.ms-powerpoint",
".pptx" to "application/vnd.openxmlformats-officedocument.presentationml.presentation",
".z" to "application/x-compress",
".zip" to "application/x-zip-compressed")
/**
* 獲取文件類型
*/
fun getMIMEType(file: File): String {
var type = "*/*"
val fName = file.name
val dotIndex = fName.lastIndexOf(".")
if (dotIndex < 0) {
return type
}
val end = fName.substring(dotIndex, fName.length).toLowerCase()
if (end.isBlank()) return type
type = MIME_TABLE[end] ?: return type
return type
}
}
有一堆映射诗力,查找起來就特別快特別方便了凰浮,再加上一些小判斷,保證傳出去的type不會(huì)有問題苇本,畢竟要保證打開文件出錯(cuò)不是在我的應(yīng)用內(nèi)嘛 (笑
看起來一切都很完美了袜茧,那么實(shí)際跑起來又如何?
android.os.FileUriExposedException
運(yùn)行在Android 7.0的系統(tǒng)上時(shí)瓣窄,會(huì)發(fā)現(xiàn)這個(gè)報(bào)錯(cuò)笛厦,Google 官方解釋:
對于面向 Android 7.0 的應(yīng)用,Android 框架執(zhí)行的 StrictMode API 政策禁止在應(yīng)用外部公開 file:// URI俺夕,即當(dāng)把targetSdkVersion指定成24及之上并且在API>=24的設(shè)備上運(yùn)行時(shí)裳凸,如果一項(xiàng)包含文件 URI 的 intent 離開應(yīng)用(如分享),則應(yīng)用出現(xiàn)故障劝贸,并出現(xiàn) FileUriExposedException 異常姨谷。
也就是說,Uri.fromFile(file)
這個(gè)方法在高版本不適用映九,正確的解決方案是通過FileProvider
來進(jìn)行操作梦湘,更具體的可以看下這篇博客.
總之,編寫了ContentProvider
相關(guān)配置后件甥,原代碼如下:
if (file.exists()) {
try {
startActivity(Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setDataAndType(file.uri, FileTypeHelper.getMIMEType(file)))
} catch (e: Exception) {
e.printStackTrace()
}
}
val File.uri: Uri
get() = if (Build.VERSION.SDK_INT >= 24) {
FileProvider.getUriForFile(context, authority, this)
} else {
Uri.fromFile(this)
}
運(yùn)行之后發(fā)現(xiàn)成功跳轉(zhuǎn)捌议,但是!跳轉(zhuǎn)目標(biāo)應(yīng)用卻無法打開文件引有,查看后發(fā)現(xiàn)的確不是文件損壞瓣颅,用文件管理器是能打開的。那么原因肯定在于代碼錯(cuò)誤譬正。
分析發(fā)現(xiàn)宫补,不僅僅在Provider
提供公開文件僻孝,還需要在Flag
里提供可讀權(quán)限,最終代碼如下:
if (file.exists()) {
try {
startActivity(Intent(Intent.ACTION_VIEW)
.addFlags(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
} else {
Intent.FLAG_ACTIVITY_NEW_TASK
})
.setDataAndType(file.uri, FileTypeHelper.getMIMEType(file)))
} catch (e: Exception) {
e.printStackTrace()
}
}
val File.uri: Uri
get() = if (Build.VERSION.SDK_INT >= 24) {
FileProvider.getUriForFile(context, authority, this)
} else {
Uri.fromFile(this)
}
至此守谓,本篇博客結(jié)束,所以這個(gè)Android版本適配問題您单,還是蠻考驗(yàn)人經(jīng)歷的斋荞。