跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaWeChatjava

Android 原生分享功能实现详解

Android 原生分享功能通过 Intent 机制实现,支持文本、图片、音频、视频等多种类型分享。基于 Intent.ACTION_SEND 的实现方案,涵盖 ShareUtil 工具类封装、Activity 调用逻辑及布局配置。针对 Android 7.0+ 系统限制,补充了 FileProvider 配置及存储权限申请说明,确保在微信、QQ 等主流应用间稳定分享文件,同时规避了 FileUriExposedException 安全异常,提供了一套完整的原生分享集成指南。

XiaoPingzi发布于 2025/2/7更新于 2026/7/2544 浏览
Android 原生分享功能实现详解

Android 原生分享功能实现详解

在 Android 开发中,利用系统原生的 Intent 机制调用其他应用进行分享是一种轻量且兼容性较好的方案。相比第三方 SDK(如 ShareSDK、友盟),原生方式无需引入额外依赖,但需要处理不同应用的特定组件名称及权限配置。

一、核心原理与准备

1.1 权限配置

在 AndroidManifest.xml 中声明必要的权限,包括网络访问和外部存储读写权限(针对 Android 6.0+ 还需动态申请):

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

1.2 FileProvider 配置

从 Android 7.0 (API 24) 开始,直接使用 Uri.fromFile() 会抛出 FileUriExposedException。必须使用 FileProvider 生成内容 URI。

在 res/xml/file_paths.xml 中添加配置:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external" path="." />
    <files-path name="files" path="." />
</paths>

在 AndroidManifest.xml 的 Application 节点下注册 Provider:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

二、ShareUtil 工具类封装

创建一个 ShareUtil.java 类来统一管理分享逻辑,支持文本、图片、音频、视频等多种类型。

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;
import java.io.File;

public class ShareUtil {
    private Context context;

    public ShareUtil(Context context) {
        this.context = context;
    }

    /**
     * 分享文字
     */
    public void shareText(String packageName, String className, String content, String title, String subject) {
        if (!stringCheck(packageName) && !stringCheck(className)) {
            // 通用分享
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setType("text/plain");
            intent.putExtra(Intent.EXTRA_TEXT, content);
            if (!TextUtils.isEmpty(title)) intent.putExtra(Intent.EXTRA_TITLE, title);
            if (!TextUtils.isEmpty(subject)) intent.putExtra(Intent.EXTRA_SUBJECT, subject);
            context.startActivity(Intent.createChooser(intent, "分享到:"));
            return;
        }

        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType("text/plain");
        
        if (stringCheck(className) && stringCheck(packageName)) {
            ComponentName componentName = new ComponentName(packageName, className);
            intent.setComponent(componentName);
        } else if (stringCheck(packageName)) {
            intent.setPackage(packageName);
        }

        intent.putExtra(Intent.EXTRA_TEXT, content);
        if (!TextUtils.isEmpty(title)) intent.putExtra(Intent.EXTRA_TITLE, title);
        if (!TextUtils.isEmpty(subject)) intent.putExtra(Intent.EXTRA_SUBJECT, subject);
        
        context.startActivity(Intent.createChooser(intent, "分享到:"));
    }

    /**
     * 分享图片
     */
    public void shareImg(String packageName, String className, File file) {
        if (!file.exists()) {
            Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show();
            return;
        }
        Uri uri = Uri.fromFile(file); // 实际生产环境建议使用 FileProvider.getUriForFile()
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType("image/*");
        
        if (stringCheck(packageName) && stringCheck(className)) {
            intent.setComponent(new ComponentName(packageName, className));
        } else if (stringCheck(packageName)) {
            intent.setPackage(packageName);
        }
        
        intent.putExtra(Intent.EXTRA_STREAM, uri);
        context.startActivity(Intent.createChooser(intent, "分享到:"));
    }

    /**
     * 分享音频
     */
    public void shareAudio(String packageName, String className, File file) {
        if (!file.exists()) {
            Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show();
            return;
        }
        Uri uri = Uri.fromFile(file);
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType("audio/*");
        
        if (stringCheck(packageName) && stringCheck(className)) {
            intent.setComponent(new ComponentName(packageName, className));
        } else if (stringCheck(packageName)) {
            intent.setPackage(packageName);
        }
        
        intent.putExtra(Intent.EXTRA_STREAM, uri);
        context.startActivity(Intent.createChooser(intent, "分享到:"));
    }

    /**
     * 分享视频
     */
    public void shareVideo(String packageName, String className, File file) {
        setIntent("video/*", packageName, className, file);
    }

    private void setIntent(String type, String packageName, String className, File file) {
        if (!file.exists()) {
            Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show();
            return;
        }
        Uri uri = Uri.fromFile(file);
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType(type);
        
        if (stringCheck(packageName) && stringCheck(className)) {
            intent.setComponent(new ComponentName(packageName, className));
        } else if (stringCheck(packageName)) {
            intent.setPackage(packageName);
        }
        
        intent.putExtra(Intent.EXTRA_STREAM, uri);
        context.startActivity(Intent.createChooser(intent, "分享到:"));
    }

    /**
     * 检查是否安装应用
     */
    public boolean checkInstall(String packageName) {
        try {
            context.getPackageManager().getPackageInfo(packageName, 0);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private static boolean stringCheck(String str) {
        return str != null && !TextUtils.isEmpty(str);
    }
}

三、Activity 调用示例

在 MainActivity.java 中初始化工具类并绑定按钮事件。

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.io.File;

public class MainActivity extends Activity implements View.OnClickListener {
    Button btnQQ, btnWX, btnMore;
    Button btnWxFriendText, btnQQFriendText;
    Button btnWxFriendImg, btnQQFriendImg;
    Button btnWxFriendAudio, btnQQFriendAudio;
    Button btnWxFriendVideo, btnQQFriendVideo;
    Button btn_wxCircle_img;
    
    ShareUtil shareUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        initViews();
        shareUtil = new ShareUtil(this);
    }

    private void initViews() {
        btnQQ = findViewById(R.id.btn_qq);
        btnWX = findViewById(R.id.btn_wx);
        btnMore = findViewById(R.id.btn_more);
        btnWxFriendText = findViewById(R.id.btn_wxFriend);
        btnQQFriendText = findViewById(R.id.btn_qqFriend);
        btnWxFriendImg = findViewById(R.id.btn_wxFriend_img);
        btnQQFriendImg = findViewById(R.id.btn_qqFriend_img);
        btnWxFriendAudio = findViewById(R.id.btn_wxFriend_audio);
        btnQQFriendAudio = findViewById(R.id.btn_qqFriend_audio);
        btnWxFriendVideo = findViewById(R.id.btn_wxFriend_video);
        btnQQFriendVideo = findViewById(R.id.btn_qqFriend_video);
        btn_wxCircle_img = findViewById(R.id.btn_wxCircle_img);

        btnQQ.setOnClickListener(this);
        btnWX.setOnClickListener(this);
        btnMore.setOnClickListener(this);
        btnWxFriendText.setOnClickListener(this);
        btnQQFriendText.setOnClickListener(this);
        btnWxFriendImg.setOnClickListener(this);
        btnQQFriendImg.setOnClickListener(this);
        btnWxFriendAudio.setOnClickListener(this);
        btnQQFriendAudio.setOnClickListener(this);
        btnWxFriendVideo.setOnClickListener(this);
        btnQQFriendVideo.setOnClickListener(this);
        btn_wxCircle_img.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        String testImgPath = "/storage/emulated/legacy/display-client/picture/my.png";
        File fileImage = new File(testImgPath);
        
        switch (v.getId()) {
            case R.id.btn_qq:
                shareUtil.shareText("com.tencent.mobileqq", null, "这是一条分享信息", "分享标题", "分享主题");
                break;
            case R.id.btn_wx:
                shareUtil.shareText("com.tencent.mm", null, "这是一条分享信息", "分享标题", "分享主题");
                break;
            case R.id.btn_more:
                shareUtil.shareText(null, null, "这是一条分享信息", "分享标题", "分享主题");
                break;
            case R.id.btn_wxFriend:
                if (shareUtil.checkInstall("com.tencent.mm")) {
                    shareUtil.shareText("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI", "http://www.example.com/", "分享标题", "分享主题");
                }
                break;
            case R.id.btn_qqFriend:
                if (shareUtil.checkInstall("com.tencent.mobileqq")) {
                    shareUtil.shareText("com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity", "http://www.example.com/", "分享标题", "分享主题");
                }
                break;
            case R.id.btn_wxFriend_img:
                shareUtil.shareImg("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI", fileImage);
                break;
            case R.id.btn_qqFriend_img:
                shareUtil.shareImg("com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity", fileImage);
                break;
            case R.id.btn_wxFriend_audio:
                shareUtil.shareAudio("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI", fileImage);
                break;
            case R.id.btn_qqFriend_audio:
                shareUtil.shareAudio("com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity", fileImage);
                break;
            case R.id.btn_wxFriend_video:
                shareUtil.shareVideo("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI", fileImage);
                break;
            case R.id.btn_qqFriend_video:
                shareUtil.shareVideo("com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity", fileImage);
                break;
            case R.id.btn_wxCircle_img:
                shareUtil.shareImgToWXCircle("狗狗图片", "com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI", fileImage);
                break;
        }
    }
}

四、布局文件 activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <Button
        android:id="@+id/btn_qq"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="QQ" />

    <Button
        android:id="@+id/btn_wx"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="微信"
        android:layout_below="@+id/btn_qq" />

    <Button
        android:id="@+id/btn_more"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="更多"
        android:layout_below="@+id/btn_wx" />

    <Button
        android:id="@+id/btn_wxFriend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_more"
        android:text="微信好友文字" />

    <Button
        android:id="@+id/btn_qqFriend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_wxFriend"
        android:text="QQ 好友文字" />

    <Button
        android:id="@+id/btn_wxFriend_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_more"
        android:layout_toRightOf="@+id/btn_wxFriend"
        android:text="微信好友图片" />

    <Button
        android:id="@+id/btn_qqFriend_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/btn_qqFriend"
        android:layout_below="@+id/btn_wxFriend"
        android:text="QQ 好友图片" />

    <Button
        android:id="@+id/btn_wxFriend_audio"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_more"
        android:layout_toRightOf="@+id/btn_wxFriend_img"
        android:text="微信好友音频" />

    <Button
        android:id="@+id/btn_qqFriend_audio"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/btn_qqFriend_img"
        android:layout_below="@+id/btn_wxFriend"
        android:text="QQ 好友音频" />

    <Button
        android:id="@+id/btn_wxFriend_video"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_qqFriend"
        android:text="微信好友视频" />

    <Button
        android:id="@+id/btn_qqFriend_video"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_wxFriend_video"
        android:text="QQ 好友视频" />

    <Button
        android:id="@+id/btn_wxCircle_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_wxFriend_video"
        android:layout_toRightOf="@+id/btn_wxFriend_img"
        android:text="微信朋友圈" />

</RelativeLayout>

五、注意事项

  1. 组件名变更:微信和 QQ 的内部组件名称(如 ShareImgUI)可能会随版本更新而变化,建议通过反射或官方文档确认最新组件名。
  2. 权限适配:Android 6.0 及以上需要在运行时动态请求存储权限。
  3. URI 安全:正式项目中请务必使用 FileProvider 替代 Uri.fromFile(),避免崩溃。
  4. 朋友圈限制:微信朋友圈分享通常仅支持图片和文字,部分旧版本可能不支持单独分享图片或纯文本到朋友圈。

目录

  1. Android 原生分享功能实现详解
  2. 一、核心原理与准备
  3. 1.1 权限配置
  4. 1.2 FileProvider 配置
  5. 二、ShareUtil 工具类封装
  6. 三、Activity 调用示例
  7. 四、布局文件 activity_main.xml
  8. 五、注意事项
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Prompt 编写的日志分析与关键字聚类
  • 深度学习激活函数完全指南:Sigmoid、Tanh、ReLU 及现代变体
  • Circle Loss:统一 Softmax 与 Triplet 的圆形优化视角
  • 二分答案专题实战:木材加工与砍树问题解析
  • PostgreSQL 安装教程及初始使用
  • LLM 微调实战:使用 Llama-Factory 进行 DPO 训练
  • OpenClaw 安装部署全流程:搭建自托管 AI 助手网关
  • 前端后端与前台中台后台概念区别
  • Windows 10 系统安装 MySQL 9.4.0 教程
  • AI 全自动科研系统与 AIGC 动画电影的技术突破与架构解析
  • FastGPT 结合 MCP 协议实现工具增强型智能体构建
  • OpenClaw 技术解析:AI 智能体的能力、局限与安全隐忧
  • OpenHarmony 跨端生态适配指南:Flutter/RN/C/C++/仓颉鸿蒙化方案
  • OpenClaw 接入飞书实战:让 AI 机器人直接操作文档与表格
  • Gazebo 机器人三维物理仿真平台核心解析
  • AI 产品经理核心职责、薪资前景与能力成长路径
  • 35 岁转行 Python:职场转型的机遇与实战指南
  • Llama Guard 3 8B 模型安全分类技术报告解读
  • AI 大模型在制造业中的应用类型与核心能力
  • 用 WebGIS 和百度天气做一个复古天气预报页

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online