做Android开发,总有一天会碰到这样一个需求:微信或QQ对话框里传过来一个文件,用户点开文件,再按右上角“其他应用打开”,列表里能出现自己的App;点进去之后,App还要把文件接收到私有目录里,继续做解析、预览或上传。这里绕不开两个核心关键词: intent-filter 和 文件路径 。真正把这个需求跑通之后我才发现,最折磨人的不是让App出现在列表里,而是拿到那个 content:// URI 之后,怎么安全地把文件落成本地文件。
这篇文章我会从清单配置、FileProvider、ContentResolver 处理、Activity 复用,再到微信/QQ 实测中踩过的坑,完整过一遍。如果你正在做接收外部文件的功能,可以直接照着往下抄。
1. 先搞明白匹配机制:微信/QQ的“打开方式”是怎么选应用的
1.1 一个文件两种入口:ACTION_VIEW 与 ACTION_SEND
用户从微信、QQ里把文件打开时,系统实际投递的隐式 Intent 并不总是一样的。最常见的两个 Action 需要分清:
-
ACTION_VIEW:表示“查看文件”。用户点文件预览、再点“其他应用打开”,大多数时候走的是这个。 -
ACTION_SEND/ACTION_SEND_MULTIPLE:表示“分享文件”。文件消息底部选“发送给朋友”,或者从系统相册点分享,走的是这个。
很多项目只给接收文件的那个 Activity 配了 ACTION_VIEW ,结果QQ里走“分享”入口时,列表找不到自己的应用。我习惯的做法是三个 Action 全部声明,文件接收页面通吃。用户通过哪个入口进来的都能处理,大不了在后面根据 Intent 的 Action 区分一下。
1.2 隐式 Intent 匹配三要素:Action、Category、Data
Android 的系统分发器在决定“哪些应用可以出现在打开列表”时,会拿隐式 Intent 去和所有 App 的 <intent-filter> 匹配,核心看三样东西:Action 是否一致、Category 是否一致、Data(scheme + host + path + mimeType)是否匹配。
- Action:要能匹配
VIEW/SEND/SEND_MULTIPLE。 - Category:必须包含
android.intent.category.DEFAULT,否则隐式 Intent 不会把 Activity 当成候选。少数会加BROWSABLE,但接收文件时通常不需要。 - Data:微信/QQ 和系统文件选择器传出来的基本都是
content://URI,MIME 类型按文件实际类型自动识别。想做到“什么文件都能收”,就直接写scheme="content"+mimeType="*/*",再顺带兼容一下文件时代的file://。
另外有一个容易被忽略的点:接收外部 App 的 Intent,Activity 必须设置 android:exported="true" 。Android 12 之后如果带 Intent-Filter 的 Activity 没有显式声明 exported,会直接在安装时崩溃。这个我后面会再说。
2. 清单文件配置:把 Activity 变成一个“文件接收器”
2.1 manifest 里必须写的 Activity 和 Intent-Filter
以 FileReceiveActivity 为例,完整声明如下:
<activity
android:name=".activity.FileReceiveActivity"
android:exported="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@style/Theme.FileReceive">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mime
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_33462167/article/details/165913047



