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:地址,格式比较长,但按顺序填就行
我的经验:vCard里字段很多,但实际分享时,我建议只保留FN、TEL、EMAIL这三个核心字段。字段太多反而容易出编码问题,尤其是地址里的中文。

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。

我曾经踩过的坑:有一次我忘了加FLAG_GRANT_READ_URI_PERMISSION,结果分享到微信时,微信提示「无法读取文件」。折腾了半天才发现是权限问题。所以这个flag一定要加,而且要加在Intent上,不是加在URI上。

另外,MIME类型要设置成text/x-vcard,而不是text/plain。为什么?因为有些App(比如系统短信)会根据MIME类型来决定怎么处理这个文件。设成text/plain,对方可能直接看到一堆乱码文本。

22.3 生成分享二维码

二维码分享,说白了就是把联系人的信息编码成二维码,别人一扫就能存到通讯录里。这个功能在名片交换场景下特别实用。

生成二维码的核心逻辑分两步:

  1. 编码:把vCard字符串编码成二维码的bitmap
  2. 显示:把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级是个折中,既保证了一定的容错率,又不会让二维码太复杂。

核心要点:二维码的内容就是vCard字符串本身,不是图片链接,也不是联系人ID。这样即使对方手机没有网络,也能直接解析出联系人信息。

22.4 跨应用分享的兼容性处理

跨应用分享,说白了就是你的App把联系人发给别的App。但不同App对vCard的支持程度不一样,这就得做兼容性处理。

我整理了一个表格,列出常见App对vCard的支持情况:

目标App vCard支持 常见问题 我的建议
系统短信 ✅ 支持 中文乱码 vCard文件用UTF-8编码
微信 ✅ 支持 文件大小限制 单个联系人分享,不要批量
QQ ⚠️ 部分支持 字段解析不全 只保留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格式:联系人的标准交换格式 BEGIN/END标记 VERSION版本 FN/TEL/EMAIL ADR地址字段 Intent发送:ACTION_SEND FileProvider MIME: text/x-vcard 二维码生成:ZXing库 纠错等级M级 UTF-8编码 跨应用兼容性处理 微信/QQ/短信 降级为纯文本 ActivityNotFoundException 文件大小限制 核心原则:兼容优先,降级兜底

从这张图你可以看到,联系人分享其实就四个核心环节:vCard格式、Intent发送、二维码生成、跨应用兼容。每个环节都有各自的坑,但只要按我上面说的做,基本不会出大问题。

最后说一句:分享功能做得好不好,关键看「异常处理」做得到不到位。用户不会因为你分享成功而夸你,但一定会因为分享失败而骂你。所以,多花点时间在兼容性测试上,值。

公众号:蓝海资料掘金营,微信deep3321