22、联系人分享:分享联系人vCard、使用Intent发送、生成分享二维码、跨应用分享
联系人分享这个功能,说实话,在Android开发里属于那种「看起来简单,做起来坑不少」的模块。我最早做联系人分享时,以为就是调个Intent发个文本完事。结果呢?用户反馈说「分享出去的联系人,对方手机打开是乱码」——嗯,从那以后我再也不敢小看这个功能了。
今天我们就来聊聊,怎么把联系人「体面地」分享出去。包括vCard格式、Intent发送、二维码生成,以及跨应用分享的那些坑。
22.1 vCard:联系人的「通用语言」
vCard,全称是Virtual Card,说白了就是联系人的标准交换格式。你想想看,如果每个App都用自己定义的格式存联系人,那分享出去对方根本读不了。vCard就是来解决这个问题的。
一个标准的vCard文件长什么样?我直接给你看代码:
BEGIN:VCARD
VERSION:3.0
FN:张三
N:张;三;;;
TEL;TYPE=CELL:13800138000
EMAIL:zhangsan@example.com
ADR;TYPE=HOME:;;北京市朝阳区某某路1号;;;
END:VCARD
这里有几个关键点:
- BEGIN/END:vCard的起止标记,必须成对出现
- VERSION:版本号,目前主流是3.0和4.0。我个人习惯用3.0,兼容性最好
- FN:显示名称,就是你在通讯录里看到的名字
- N:结构化姓名,格式是「姓;名;中间名;前缀;后缀」
- TEL:电话号码,可以加TYPE指定类型(CELL、HOME、WORK等)
- EMAIL:邮箱地址
- ADR:地址,格式比较长,但按顺序填就行
22.2 用Intent发送vCard
生成vCard字符串之后,怎么发给别的App?最直接的方式就是Intent。我直接上代码:
// 生成vCard字符串
String vCardString = buildVCard(contact);
// 创建临时文件
File vcfFile = new File(context.getCacheDir(), "contact.vcf");
FileOutputStream fos = new FileOutputStream(vcfFile);
fos.write(vCardString.getBytes("UTF-8"));
fos.close();
// 构建Intent
Uri uri = FileProvider.getUriForFile(context,
context.getPackageName() + ".fileprovider", vcfFile);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/x-vcard");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 启动选择器
startActivity(Intent.createChooser(intent, "分享联系人"));
这里有个坑,我必须要提醒你:不要直接用file:// URI。Android 7.0以后,直接暴露文件路径会抛FileUriExposedException。必须用FileProvider来生成content:// URI。
另外,MIME类型要设置成text/x-vcard,而不是text/plain。为什么?因为有些App(比如系统短信)会根据MIME类型来决定怎么处理这个文件。设成text/plain,对方可能直接看到一堆乱码文本。
22.3 生成分享二维码
二维码分享,说白了就是把联系人的信息编码成二维码,别人一扫就能存到通讯录里。这个功能在名片交换场景下特别实用。
生成二维码的核心逻辑分两步:
- 编码:把vCard字符串编码成二维码的bitmap
- 显示:把bitmap展示到ImageView上
我一般用ZXing库来生成二维码。代码是这样的:
// 依赖:implementation 'com.google.zxing:core:3.5.1'
public Bitmap generateVCardQrCode(String vCardString, int size) {
try {
Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix matrix = new QRCodeWriter().encode(
vCardString,
BarcodeFormat.QR_CODE,
size, size,
hints
);
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565);
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
bitmap.setPixel(x, y,
matrix.get(x, y) ? Color.BLACK : Color.WHITE);
}
}
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
这里有个细节:纠错等级。我选的是M级(中等),为什么?因为二维码如果太密,手机扫的时候容易失败。L级纠错太弱,H级又会让二维码太密。M级是个折中,既保证了一定的容错率,又不会让二维码太复杂。
22.4 跨应用分享的兼容性处理
跨应用分享,说白了就是你的App把联系人发给别的App。但不同App对vCard的支持程度不一样,这就得做兼容性处理。
我整理了一个表格,列出常见App对vCard的支持情况:
| 目标App | vCard支持 | 常见问题 | 我的建议 |
|---|---|---|---|
| 系统短信 | ✅ 支持 | 中文乱码 | vCard文件用UTF-8编码 |
| 微信 | ✅ 支持 | 文件大小限制 | 单个联系人分享,不要批量 |
| ⚠️ 部分支持 | 字段解析不全 | 只保留FN和TEL字段 | |
| 邮件客户端 | ✅ 支持 | 附件名乱码 | 文件名用英文,如contact.vcf |
| 蓝牙 | ⚠️ 部分支持 | 传输失败 | 文件不要超过1MB |
你看,不同App的问题都不一样。我个人的处理方式是:先发vCard文件,如果对方App不支持,再降级成纯文本。降级代码大概是这样:
// 尝试发送vCard
try {
sendVCardIntent(contact);
} catch (ActivityNotFoundException e) {
// 没有App能处理vCard,降级为文本
sendTextIntent(contact);
}
private void sendTextIntent(Contact contact) {
String text = "姓名:" + contact.getName() + "\n"
+ "电话:" + contact.getPhone() + "\n"
+ "邮箱:" + contact.getEmail();
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(intent, "分享联系人"));
}
这个降级策略,说白了就是「有vCard用vCard,没有就用文本」。虽然文本格式不如vCard规范,但至少用户能拿到信息。
22.5 知识体系总览
我把整个联系人分享的知识点画成了一张图,方便你理解整体结构:
从这张图你可以看到,联系人分享其实就四个核心环节:vCard格式、Intent发送、二维码生成、跨应用兼容。每个环节都有各自的坑,但只要按我上面说的做,基本不会出大问题。
公众号:蓝海资料掘金营,微信deep3321