news 2025/12/26 6:54:44

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进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/18 19:26:55

SGMICRO圣邦微 SGM2017-3.3XN5/TR SOT23-5 线性稳压器(LDO)

特性 工作输入电压范围:2.5V至5.5V 固定输出电压为2.8V和3.3V 输出电流:300mA 电流:77微A(TYR) 低压差:在300mA时为300mV(典型值)低噪声:30uVrms(典型值)(10Hz至100kHz)高PSRR:在1kHz时典型值为73dB 电流限制与热保护 使用小型封装陶瓷电容实现稳定运行关断供电电流:0.01uA(典型…

作者头像 李华
网站建设 2025/12/18 19:26:42

SGMICRO圣邦微 SGM2019-1.3YN5G/TR SOT-153 线性稳压器(LDO)

特性 工作输入电压范围:2.5V至5.5V 固定输出电压: 1.2V,1.5V,1.8V,2.5V,2.6V,2.8V,2.85V,3.0V,3.3V可调输出电压范围:1.2V至5.0V输出电压精度:25C时士2.5% 低输出噪声:30pVRMS(典型值) 低压差电压:在300mA时为270mV(典型值) 高PSRR:在1kHz时典型值为74dB 关断电流:0.01uA(典型值…

作者头像 李华
网站建设 2025/12/18 19:26:38

SGMICRO圣邦微 SGM2019-1.5YC5G/TR SC70-5 线性稳压器(LDO)

特性工作输入电压范围&#xff1a;2.5V至5.5V固定输出电压&#xff1a;1.2V、1.5V、1.8V、2.5V、2.6V、2.8V、2.85V、3.0V、3.3V可调输出电压范围&#xff1a;1.2V至5.0V输出电压精度&#xff1a;25C时为2.5%低输出噪声&#xff1a;30μV_RMS&#xff08;典型值&#xff09;低压…

作者头像 李华
网站建设 2025/12/18 19:26:31

Python 爬虫实战:User-Agent 随机切换防封禁

前言 在网络爬虫的开发与应用过程中&#xff0c;反爬机制是绕不开的核心问题。其中&#xff0c;基于请求头中 User-Agent 字段的校验是网站最基础也是最常用的反爬手段之一。固定的 User-Agent 会被服务器快速识别为爬虫程序&#xff0c;进而触发 IP 封禁、请求限制等反爬措施…

作者头像 李华
网站建设 2025/12/18 19:26:29

一、地理探测器:是什么?

Geo Detector是 用Excel编制的地理探测器软件, 可从以下网址免费下载:http://www.geodetector.org/。地理探测器方法简介地理探测器&#xff08;Geographical Detector&#xff09;是一种用于识别空间分异特征及其驱动因素的统计分析方法&#xff0c;最早由王劲峰等学者提出&am…

作者头像 李华