news 2026/6/10 1:46:46

Android将应用添加到默认打开方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android将应用添加到默认打开方式

文章目录

  • 一、首先你需要先看到效果
  • 二、实现原理
    • 一、发送数据
    • 二、两种方式
    • 三、接收数据
  • 三、工具类

一、首先你需要先看到效果

就是将你的 activity 添加到打开方式,比如我这里有两个 activity,PdfViewerActivity 负责打开 pdf 文件,OfficeViewerActivity 负责打开 word,excel,ppt 文件

<activityandroid:name=".activity.PdfViewerActivity"android:exported="true"android:screenOrientation="portrait"><intent-filter><actionandroid:name="android.intent.action.VIEW"/><categoryandroid:name="android.intent.category.DEFAULT"/><categoryandroid:name="android.intent.category.BROWSABLE"/><dataandroid:mimeType="application/pdf"/></intent-filter></activity><activityandroid:name=".activity.OfficeViewerActivity"android:exported="true"android:screenOrientation="portrait"><intent-filter><actionandroid:name="android.intent.action.VIEW"/><categoryandroid:name="android.intent.category.DEFAULT"/><categoryandroid:name="android.intent.category.BROWSABLE"/><!-- Word --><dataandroid:mimeType="application/msword"/><dataandroid:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document"/><!-- Excel --><dataandroid:mimeType="application/vnd.ms-excel"/><dataandroid:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/><!-- PowerPoint --><dataandroid:mimeType="application/vnd.ms-powerpoint"/><dataandroid:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation"/></intent-filter></activity>

二、实现原理

一、发送数据

Manifest 配置完成后,如果调起了系统打开方式,系统会这样发送数据

Intent{action=ACTION_VIEWdata=content://xxx/xxx//代表文件的 uritype=application/pdf//代表文件类型}

二、两种方式

自己伪装成系统系统打开方式发送数据

// 把 File 转成 content:// Uri(和系统行为一致)valuri=FileProvider.getUriForFile(activity,"${activity.packageName}.fileprovider",file)// 构造 ACTION_VIEW Intent(系统打开方式标准格式)valintent=Intent(Intent.ACTION_VIEW).apply{setDataAndType(uri,"application/pdf")addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)}// 发起跳转activity.startActivity(intent)

有什么区别:

Intent{action=android.intent.action.VIEWdata=content://your.package.fileprovider/...type=application/pdf}
Intent{action=android.intent.action.VIEWdata=content://com.android.providers.downloads.documents/document/1234type=application/pdf}

可以这样判断:

uri.authority=="${context.packageName}.fileprovider"

三、接收数据

在相应的页面接收数据:

valuri:Uri=intent.data?:returnvalinputStream=contentResolver.openInputStream(uri)

如果你必须要使用文件真实路径而不用 uri,可通过 uri 复制文件到一个目录得到:

funcopyUriToCache(context:Context,uri:Uri):File{valfileName=getFileName(context,uri)?:"temp_file"valdestFile=File(context.cacheDir,fileName)context.contentResolver.openInputStream(uri)?.use{input->destFile.outputStream().use{output->input.copyTo(output)}}returndestFile}

获取文件名:

fungetFileName(context:Context,uri:Uri):String?{valcursor=context.contentResolver.query(uri,arrayOf(OpenableColumns.DISPLAY_NAME),null,null,null)cursor?.use{if(it.moveToFirst()){returnit.getString(0)}}returnnull}

三、工具类

// 获取传入的文件路径和文件名// 优先从 extra 获取(应用内调用,就是我们常用的 activity 之间跳转传参)varfilePath=intent.getStringExtra(EXTRA_PDF_FILE_PATH)?:""varfileName=intent.getStringExtra(EXTRA_PDF_FILE_NAME)?:""// 如果 extra 中没有文件路径,尝试从 Intent.data URI 获取(系统打开方式调用)if(filePath.isEmpty()&&intent.data!=null){filePath=UriFileResolver.getFilePathFromUri(this,intent.data!!)if(fileName.isEmpty()){// 从文件路径中提取文件名fileName=File(filePath).name}}
/** * Uri 文件路径解析工具 * * 设计原则: * - 不根据系统版本做假设 * - 能直接获取真实路径就直接用 * - 获取不到再复制到 App 私有缓存目录 * * 适用于: * - 系统“打开方式” * - 第三方文件管理器 * - 应用内 FileProvider */objectUriFileResolver{/** * 从 Uri 获取一个可用的文件路径 * * @return 文件路径,失败返回空字符串 */fungetFilePathFromUri(context:Context,uri:Uri):String{returnwhen(uri.scheme){ContentResolver.SCHEME_FILE->{uri.path?:""}ContentResolver.SCHEME_CONTENT->{try{// 1️⃣ 自家 FileProvider,直接还原真实路径(零拷贝)if(isOwnFileProvider(context,uri)){resolveFromFileProvider(context,uri)?.let{returnit}}// 2️⃣ 尝试通过 MediaStore 获取真实路径(不做版本假设)valmediaPath=getFilePathFromMediaStore(context,uri)if(mediaPath.isNotEmpty()){returnmediaPath}// 3️⃣ 拿不到路径,复制到缓存目录兜底copyUriToTempFile(context,uri)}catch(e:Exception){""}}else->""}}// ================= FileProvider =================privatefunisOwnFileProvider(context:Context,uri:Uri):Boolean{returnuri.authority=="${context.packageName}.fileprovider"}/** * 解析自家 FileProvider Uri * * content://authority/path_name/relative_path */privatefunresolveFromFileProvider(context:Context,uri:Uri):String?{valsegments=uri.pathSegmentsif(segments.isEmpty())returnnullvalroot=segments[0]valrelativePath=if(segments.size>1)segments.subList(1,segments.size).joinToString(File.separator)else""valbaseDir=when(root){"files"->context.filesDir"cache"->context.cacheDir"external_files"->context.getExternalFilesDir(null)"external_cache"->context.externalCacheDirelse->null}?:returnnullreturnif(relativePath.isNotEmpty()){File(baseDir,relativePath).absolutePath}else{baseDir.absolutePath}}// ================= MediaStore =================/** * 尝试从 MediaStore 查询真实文件路径 * * 注意: * - 高版本系统上不保证一定成功 * - 能成功就直接用,失败交给兜底方案 */privatefungetFilePathFromMediaStore(context:Context,uri:Uri):String{varcursor:Cursor?=nullreturntry{cursor=context.contentResolver.query(uri,arrayOf(MediaStore.Files.FileColumns.DATA),null,null,null)if(cursor!=null&&cursor.moveToFirst()){valindex=cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)if(index>=0)cursor.getString(index)?:""else""}else{""}}catch(e:Exception){""}finally{cursor?.close()}}// ================= Copy =================/** * 将 Uri 指向的文件复制到 App 缓存目录 */privatefuncopyUriToTempFile(context:Context,uri:Uri):String{returntry{valtempDir=File(context.cacheDir,"temp_files")if(!tempDir.exists()){tempDir.mkdirs()}varfileName=getFileNameFromUri(context,uri)if(fileName.isEmpty()){fileName="temp_${System.currentTimeMillis()}"}valtempFile=File(tempDir,fileName)if(tempFile.exists()){returntempFile.absolutePath}context.contentResolver.openInputStream(uri)?.use{input->tempFile.outputStream().use{output->input.copyTo(output)}}tempFile.absolutePath}catch(e:Exception){""}}// ================= File name =================privatefungetFileNameFromUri(context:Context,uri:Uri):String{varfileName=""try{context.contentResolver.query(uri,null,null,null,null)?.use{cursor->if(cursor.moveToFirst()){valindex=cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME)if(index>=0){fileName=cursor.getString(index)?:""}}}if(fileName.isEmpty()){uri.path?.let{fileName=it.substringAfterLast('/')}}}catch(e:Exception){}returnfileName}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 8:21:02

重负荷设备选直线导轨?HIWIN授权商海威机电有合适推荐吗?

最近和几个做重型设备的朋友聊天&#xff0c;他们都在吐槽&#xff1a;重负荷场景下选导轨真难——要么负载不够容易坏&#xff0c;要么刚性不足影响精度&#xff0c;要么安装麻烦老出问题。其实选对产品和服务商&#xff0c;这些问题都能解决。作为HIWIN正式授权专属经销商&am…

作者头像 李华
网站建设 2026/6/8 23:20:57

Xiaomi mimo大模型API接入Claude code

昨天听闻小米的大模型出来之后&#xff0c;今天就马不停蹄的试了一下&#xff0c;确实在代码方面算是国内top级别&#xff0c;最主要的是目前还是免费开源的。这也意味着我们编程的路上又多了一个得力助手。希望免费的福利还能更长一些。 Xiaomi MiMo 开放平台 首先mimo官网上…

作者头像 李华
网站建设 2026/6/9 22:27:48

多语种网站设计常见误区:项目管理者必须关注的本地化细节

多语种网站设计的本地化盲区&#xff1a;技术之外的文化洞察 多语种网站设计常陷误区&#xff0c;忽视本地化细节将影响全球布局。作为全球多语言网站供应商&#xff0c;易营宝助力企业官网建站实现响应式SEO优化与外贸独立站Yandex推广&#xff0c;打通全球流量生态。许多企业…

作者头像 李华
网站建设 2026/6/9 19:44:10

如何在JMeter中调用Python代码N种方法?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在性能测试领域&#xff0c;JMeter已经成为测试专业人士的首选工具&#xff0c;用于模拟用户行为、测量响应时间、评估系统性能。而现在大部分接口都会涉及到验签…

作者头像 李华
网站建设 2026/6/8 22:30:31

如何使用Jmeter进行性能测试?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快今日分享&#xff1a;如何使用 jmeter 进行性能测试&#xff0c;就拿一个具体项目为例进行讲解。一、目录结构说明&#xff1a;确认转让接口的入参依赖于&#xff…

作者头像 李华