news 2026/8/6 15:21:41

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/8/6 15:17:12

拒绝低效办公,5个超实用职场必备的免费软件!

每天坐在工位上&#xff0c;最让人崩溃的不是活儿多&#xff0c;而是工具不给力。今天不讲废话&#xff0c;直接给大家整理了5个真正实用又免费的职场必备软件工具。这些软件工具我亲测好用&#xff01;1. 万彩办公大师&#xff1a;一个顶60个的“百宝箱”很多人的电脑里塞满了…

作者头像 李华
网站建设 2026/8/4 20:54:08

掌握提示工程:CoT思维链技术详解,零样本学习替代数据标注

提示工程是设计与大模型交互的核心技能&#xff0c;特别是Chain-of-Thought(CoT)技术通过引导模型展示推理过程&#xff0c;能显著降低对标注数据的依赖。无需微调&#xff0c;即可通过零样本/少样本提示让基座模型表现出接近微调模型的性能。文章系统介绍了提示工程流程&#…

作者头像 李华
网站建设 2026/8/6 4:53:17

Jmeter命令行压测 生成HTML测试报告

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快通常Jmeter的 GUI 模式仅用于调试&#xff0c;在实际的压测项目中&#xff0c;为了让压测机有更好的性能&#xff0c;多用 Jmeter 命令行来进行压测。同时&#xff…

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

离线部署kubernetes v1.34.3

离线部署kubernetes v1.34.3 镜像用的在线下载的方式&#xff0c;如果需要完全离线&#xff0c;搭建私有镜像仓库&#xff0c;把需要的镜像上传到镜像仓库。 环境信息 virtualbox 虚拟机os: ubuntu 24.04.3kernel: 6.8.0-90-generic网卡1: 10.0.2.15/fd17:625c:f037:2:a00:27f…

作者头像 李华
网站建设 2026/8/7 0:01:06

铜及铜合金的特性、应用与检测

铜及铜合金的特性铜及铜合金是一种以纯铜为基础&#xff0c;添加其他元素形成的合金材料。纯铜本身具有独特的紫红色外观&#xff0c;而铜合金则会因添加的元素种类和比例不同&#xff0c;呈现出不同的性能和外观特征。1.物理性质铜的导电和导热性能极为出色&#xff0c;其电导…

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

Python 爬虫实战:Scrapy 管道(Pipeline)数据持久化

前言 Scrapy 框架中&#xff0c;爬虫解析出的 Item 数据最终需落地存储&#xff0c;而管道&#xff08;Pipeline&#xff09;是实现数据持久化的核心组件。相较于直接在爬虫文件中处理数据存储&#xff0c;Pipeline 具备模块化、可扩展、支持多管道协同处理的优势&#xff0c;…

作者头像 李华