简介:友盟分享Android Social SDK 6.4.6自定义版是一份服务Android开发者的社交分享集成资源,基于友盟移动开发者服务平台,可帮助快速接入微信、QQ、微博等主流社交平台,也兼容Facebook、Twitter等国际平台,减少逐一对接各方API的工作量。压缩包大小约58.92MB,内含SDK核心库与配套Demo工程。开发者可参照Demo理解SDK初始化(Application中设置AppKey与渠道)、分享接口shareToPlatform的调用、分享结果回调监听,并通过UMWeb、UMImage、UMVideo等对象构造文字、图片、链接、视频形态的分享内容。在界面定制方面,可借助SocializeEntity与UMShareViewBuilder构建与自身应用风格一致的分享面板。目前已有216人学习下载,适合中初级Android开发者快速上手,也便于有集成经验者查阅6.4.6版本的接口用法与工程结构,节省联调时间。
1. 友盟分享:先把 6.4.6_custom 这个包能替你解决什么说清楚
友盟分享在 Android 里属于“接入文档二十页,真踩坑时全靠猜”的模块。这个 umeng_android_social_sdk_6.4.6_custom 包,解决的问题很具体:第一,把分享 SDK 从“远程坐标依赖”降级成“本地 jar + res + assets”,离线也能编译,老工程不用为了加个分享去折腾仓库环境;第二,6.4.6 这个版本把平台实现拆成了独立 jar,微信、QQ、微博哪个出问题就替换哪一块,不用整包回滚。它适合两类人:一类是项目里要尽快落地微信 / QQ / 微博三端分享,能接受一套稳定但不算新的实现;另一类是以后想自绘分享面板、自己接管回调的人,6.4.6 的代码结构比后来聚合 SDK 更直白,适合拆开看。这篇文章我按拆包、链路、配置、避坑、改造的顺序讲,目标是让你照着走一遍,能跑通也能改。
2. 拆开 6.4.6_custom:包目录、jar 职责与集成前的准备
2.1 先看整体目录结构:不是“一个 jar 打天下”
这个包和后来在仓库上一行依赖拉下来的聚合版本不一样,6.4.6_custom 是“多个 jar 分工协作”的本地包。解压之后,主要会看到这样三层:
umeng_android_social_sdk_6.4.6_custom/ ├── libs/ │ ├── umeng_common_xxx.jar // 公共基础:日志、网络、上报 │ ├── umeng_social_api_xxx.jar // 对外 API,入口类都在这里 │ ├── umeng_social_net_xxx.jar // 网络请求与回包解析 │ ├── umeng_social_shareview_xxx.jar // 默认分享面板 View │ ├── umeng_social_tool_xxx.jar // 工具:图片处理、JSON 序列化 │ ├── umeng_social_wx_xxx.jar // 微信平台实现 │ ├── umeng_social_qq_xxx.jar // QQ 平台实现 │ └── umeng_social_sina_xxx.jar // 新浪微博平台实现 ├── res/ // 分享面板的布局、drawable、values └── assets/说明一下:上面文件名里的 xxx 是具体版本尾号,不同渠道放出来的包命名略有出入,但文件划分逻辑基本一致。custom 的含义就是“按项目需要裁剪过的改版”——常见的裁剪方式包括去掉用不到的平台 jar、替换默认面板布局、去掉统计上报模块,所以你在自己工程里二次裁剪时,也应该按这个粒度做,不要动 res 里的关键控件 id,否则面板可能直接找不到布局。
2.2 每个 jar 的职责边界:知道谁管什么,定位问题快一半
先说最常用的入口。umeng_social_api里是UMShareAPI、ShareAction、PlatformConfig这些门面类,平时写的“初始化平台”、“发起分享”都调它。umeng_common是地基,SDK 自己打日志、上报埋点都靠它,如果你在 Application 里没初始化UMConfigure,后面所有分享请求都会在“网络层”报错,而且报错信息很含糊。
umeng_social_shareview单独拎出来是一个好设计。它只负责“弹出来的那个底部面板”——左边一列平台图标,点击之后才真正走进某个平台的分享逻辑。这意味着如果你不想用默认面板,完全可以不把这个 jar 拷进工程,自己在 UI 层画一个半圆弹窗,点击某个平台时直接调ShareAction的setPlatform()指定平台。裁剪成本就在这:shareview是唯一跟 UI 强相关的模块,其余 jar 几乎都是逻辑层。
umeng_social_net负责与友盟服务器通信,包括校验 AppKey、换取各平台授权参数。很多人不知道的一点是:即使在分享纯本地图片的场景,SDK 也可能走一次网络请求来校验配置,所以完全断网时分享会失败,这不算 bug,是旧版本校验机制的通病。umeng_social_tool里的工具类平时不直接碰,但图片压缩、URL 拼接这类细节都藏在里面,后面讲UMImage时你还会遇到它。
平台 jar 的职责最简单也最容易理解:umeng_social_wx内部封装了微信开放平台的 SDK 回调,umeng_social_qq处理 QQ 互联的 Tencent 回调,umeng_social_sina对应微博开放平台的 SSO 与网页授权。它们各自持有平台相关的常量、回调 Activity 和签名校验逻辑,拆开的好处是——微信回调闪退时,你只需要替换和调试wx这一个小 jar,不影响其他端。
2.3 依赖冲突与资源合并:本地包集成时最先遇到的坎
把 jar 直接拖进libs目录是最简单的集成方式,但如果你项目里已经通过依赖引入了别的网络库,就要小心“重复类”。
常见做法是先用gradle的依赖报告查一遍是否有同一个包名下的类冲突,尤其是com.umeng前缀出现两次时,大概率是因为工程之前已经拉过更高版本的友盟 SDK,新旧两个版本混在一起,编译时不报错,运行时类加载顺序一变就崩。
./gradlew :app:dependencies这条命令会输出当前模块的依赖树,搜索umeng关键词,能看到哪些直接依赖引入了友盟相关坐标。如果有,我一般会先排除掉旧坐标,再以本地 jar 为准。需要提醒的是,6.4.x 系列对support包有依赖,如果你的项目已经迁移到 AndroidX,直接拷贝旧 jar 可能会在android.support.annotation上报 NoClassDefFoundError,这时候需要在工程里保留一小份 support 兼容库,或者用 jarjar 工具把 SDK 内部引用的 support 类重定位到 AndroidX 命名空间。
res合并是第二个坑。6.4.6 的分享面板资源里有自己的strings.xml、colors.xml和一组以umeng_socialize_开头的布局文件,如果你的项目资源里恰好有同名定义,构建时会出现resource merging conflict。解决方法是把res/values下冲突的键名在项目里加tools:override="true",或者直接用 Android Studio 的Merge Resource面板看看冲突来源。更省事的做法是:把 SDK 的res文件夹改个前缀,比如res_social,然后通过sourceSets指过去,让资源名别跟主工程纠缠。
还有一点要提前预防:assets目录下如果有同名文件,会被主工程覆盖且不报错,最终表现为某些异常行为找不出原因。拷贝前先ls对比一遍,特别是umeng开头的配置文件。
3. 核心链路拆解:从初始化到分享面板弹出再到回调返回
3.1 Application 里初始化:UMConfigure 的参数到底填什么
6.4.6 的初始化必须在onCreate()里最早执行,我一般放在第一行,原因后面会讲。核心代码很简单:
public class App extends Application { @Override public void onCreate() { super.onCreate(); UMConfigure.init(this, "5a7b2f1e8c4d9e10f2000000", // 友盟平台申请的 AppKey "main", // 渠道名,统计时用来区分来源 UMConfigure.DEVICE_TYPE_PHONE, // 设备类型 null); // 扩展参数,6.4.6 传 null 即可 UMConfigure.setLogEnabled(true); // 联调时开,上线前务必关掉 PlatformConfig.setWeixin("wx1234567890abcdef", "abcdef1234567890abcdef1234567890"); PlatformConfig.setQQ("1101234567", "kGzW3bCqQdE5mM8n"); PlatformConfig.setSinaWeibo("1234567890", "abcdef1234567890abcdef1234567890", "http://sns.whalecloud.com/sina2/callback"); } }参数说明:第一个参数是Context,必须传 Application 级别,如果传入 Activity,SDK 内部持有它的引用时容易造成内存泄漏;第二个是你在友盟平台申请到的 AppKey,注意它跟你后面填的微信 AppID 不是同一个东西,很多人把这两串值填反;第三个渠道名只影响统计报表展示,分享功能本身不依赖;第四个DEVICE_TYPE_PHONE表示手机环境,如果你的应用跑在电视或平板上,有些平台的授权策略会不一样。
我在项目里会额外做一件事:把这三个平台的密钥按 build 环境分开。测试包用测试 AppID,正式包用线上 AppID,不然就会出现“测试机上能分享,正式包一分享就提示应用未审核”的灵异问题。做法是在build.gradle里埋buildConfigField,代码里读BuildConfig.WX_APP_ID再传给PlatformConfig。
3.2 构建分享内容:ShareAction 的链式调用与参数
分享的核心类是ShareAction,它的设计是链式调用。下面是一段分享网页的代码:
UMWeb web = new UMWeb("https://example.com/article/1"); web.setTitle("来自某 App 的分享"); web.setDescription("这是我做的一个示例分享内容"); web.setThumb(new UMImage(this, R.drawable.ic_share_thumb)); // 缩略图 new ShareAction(MainActivity.this) .withMedia(web) // 分享内容:网页/图片/音乐/视频 .setCallback(new UMShareListener() { // 回调监听 @Override public void onStart(SHARE_MEDIA platform) { // 点击平台图标后,分享动作刚发起 } @Override public void onResult(SHARE_MEDIA platform) { // 分享动作完成,注意“完成”不等于用户真正发送成功 } @Override public void onError(SHARE_MEDIA platform, Throwable t) { // 分享失败,t.getMessage() 常常是关键线索 } @Override public void onCancel(SHARE_MEDIA platform) { // 用户中途取消 } }) .share();这段代码里有两个关键点。第一,我故意没有调用setPlatform(),这样.share()会弹出默认的分享面板;如果只想调起某一个平台,比如“一键分享到微信”,可以加一行.setPlatform(SHARE_MEDIA.WEIXIN),WEIXIN和WEIXIN_CIRCLE是两个独立枚举值,朋友圈与微信好友要区别处理。第二,UMWeb是承载网页分享的媒体对象,它还有兄弟类UMImage、UMusic、UMVideo,分别对应图片、音乐、视频分享,构造方式大同小异。
注意withMedia()的语义是“替换媒体内容”,不是“追加”。如果你写.withText("文字").withMedia(web),最终分享出去的内容以UMWeb为准,withText里的纯文本会被忽略或降级为描述字段。这个细节在我第二次接这个 SDK 时才踩明白,分享微博时想带一段自定义文案,结果文案一直不见,最后发现是UMWeb的description优先于withText。
3.3 回调里拿结果:onResult / onError / onCancel 的时序
回调接口UMShareListener的四个方法对应一次分享动作的四个阶段:onStart在拉起平台客户端前触发,适合在这里打点“开始分享”;onResult在平台返回成功时触发;onCancel是用户取消;onError则要重点关注,它拿到的Throwable可能是ShareException、网络异常或者平台自己返回的错误码。
这里必须澄清一个常见误解:onResult不等于“用户已经发送成功”。微信分享时,只要用户授权跳转回来,回调就是onResult,即使用户在微信里点了取消发送,也可能拿到onResult。所以业务上如果要记录“真正分享成功”,我一般配合平台返回的errCode再校验一次,微信 errCode 为 0 才认为成功,其它平台同理。
还有一个很关键的接线步骤:必须在Activity里把收到的onActivityResult转交给 SDK:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data); }这段代码放在Activity里,最好放在onActivityResult的第一行,不要被任何条件判断挡住。如果漏掉,你会在回调里发现onCancel或onError经常被触发,而微信分享回来后完全没有反应——这是典型的“数据没转交”。
另一个容易漏的地方是拉起 QQ 客户端时,如果 MainActivity 设置了singleTask启动模式,分享完成后会走onNewIntent而不是onActivityResult。同样需要调用UMShareAPI.get(this).onActivityResult(...),否则 QQ 端回调会丢。这两处接线是“玄学”问题的高发点,先检查它们,再检查配置。
3.4 面板与分享动作的衔接:从点击到拉起外部 App
默认分享面板是一个BottomSheet风格的视图,几秒钟就能点出平台图标,但里面还有一条隐式链路:点击“微信”图标后,SDK 先检查本地有没有安装微信客户端,没安装时会尝试拉起网页版授权;QQ 也一样,优先调起 QQ,再考虑网页跳转;微博则会优先尝试 SSO 单点登录,失败后回退到 Web 授权。
这种“降级策略”是 SDK 内部实现的,你只会看到回调结果。但它带来的一个实际问题是:在 Android 高版本上,应用间跳转需要考虑到包可见性。如果 targetSdk 升到 30 以上,PackageManager.getLaunchIntentForPackage("com.tencent.mm")这类查询可能拿不到结果,SDK 会误判为“未安装微信”,直接走 Web 授权或者报错。旧版 SDK 对这部分适配得很差,所以如果你的应用 targetSdk 较高,可能需要自己提前判断微信是否安装,再决定是否让用户进入分享面板。
4. 平台接入实操:微信、QQ、微博的关键配置与回调注册
4.1 平台注册与 AppKey 从哪来到哪去
友盟分享这层只是中转,真正的身份认证是在微信开放平台、QQ 互联、微博开放平台上完成的。你要先分别在这三个平台注册应用,拿到各自的 AppID 和 AppSecret,然后在友盟后台把这三个平台的参数绑定到你的应用下。整个体系里有两套密钥:一套是友盟自己的 AppKey,另一套是各平台的 AppID/AppSecret,漏配任何一个都会导致分享失败。
常见做法是把平台参数集中放在一个配置类里,避免在 Activity 里散落赋值。我用过下面这种方式:
public final class SharePlatformConfig { public static final String WEIXIN_APP_ID = "wx1234567890abcdef"; public static final String WEIXIN_APP_KEY = "abcdef1234567890abcdef1234567890"; public static final String QQ_APP_ID = "1101234567"; public static final String QQ_APP_KEY = "kGzW3bCqQdE5mM8n"; public static final String SINA_APP_ID = "1234567890"; public static final String SINA_APP_KEY = "abcdef1234567890abcdef1234567890"; public static final String SINA_REDIRECT_URL = "http://sns.whalecloud.com/sina2/callback"; }说明:微信的 AppID 以wx开头;QQ 的 AppID 是纯数字,通常 10 位左右;微博的 AppID 也是一串数字。这三组值建议全部定义为常量,而不是直接硬编码在setWeixin调用处,方便日后发版切换密钥。SINA_REDIRECT_URL是微博网页授权回调地址,两端必须完全一致,否则微博授权页会停在“授权失败”或“回调地址不合法”的页面上。
PlatformConfig.setWeixin(SharePlatformConfig.WEIXIN_APP_ID, SharePlatformConfig.WEIXIN_APP_KEY); PlatformConfig.setQQ(SharePlatformConfig.QQ_APP_ID, SharePlatformConfig.QQ_APP_KEY); PlatformConfig.setSinaWeibo(SharePlatformConfig.SINA_APP_ID, SharePlatformConfig.SINA_APP_KEY, SharePlatformConfig.SINA_REDIRECT_URL);要注意传参顺序,setWeixin的第一个参数是 AppID 而不是 AppSecret,我曾在一次重构时不慎把两个参数颠倒,结果微信授权一直报 “invalid appsecret”,排查了很久才发现是低级错误。
4.2 AndroidManifest 中必须出现的回调 Activity
这一节是整个接入过程里最容易翻车的地方。微信、QQ 分享完成后,平台客户端要跳回你的应用,靠的就是 Manifest 里注册的回调 Activity。6.4.6 的 SDK 提供了现成的回调类,你只需要声明:
<application> <activity android:name="com.umeng.socialize.weixin.view.WXCallbackActivity" android:exported="true" /> <activity android:name="com.umeng.socialize.qq.view.QQCallbackActivity" android:exported="true" /> </application>这两个 Activity 不需要接任何 Intent-Filter,exported="true"是必须的,不加的话微信客户端没有权限启动它,回调会静默失败。微信的WXCallbackActivity内部会校验 AppID 与签名,所以同样一套代码,换到另一个包名上就会回调失败,不是代码问题,是平台侧配置问题。
QQ 回调在这里也需要一个可导出的 Activity。很多项目把QQCallbackActivity漏了,结果手机装了 QQ 时分享正常,没装 QQ 时走 Web 授权也正常,唯独从 QQ 客户端跳回来时没有反应,卡在“分享中”的转圈状态,最后超时。如果你遇到这类问题,第一反应应该是检查 Manifest,而不是去查回调代码。
4.3 包名、签名与平台后台配置的一致性
这三个平台的回调都跟“包名 + 签名”强绑定。Android 的调试签名和正式签名不同,如果你在微信开放平台只配置了正式包的签名,那么调试期分享到微信后一定没有回调;反过来,如果测试时正常,打包上线后回调消失,也基本是签名没对齐。
友盟提供过一个辅助工具类来读取当前签名的 MD5,你可以把它打进一个 Debug 页面里,对照平台后台填写的签名值:
String signature = UMShareAPI.get(this).getSignature(this, getPackageName()); Log.d("UMShareDebug", "signature=" + signature);我一般会在 Debug 开关打开时打印这行日志,正式包不打印。这个值如果跟平台后台不一致,平台会直接返回签名校验失败,onError里抛出的异常甚至不会带有“签名错误”字样,而是伪装成授权失败,所以提前打日志是最快的排障路径。
还有一个容易忽略的耗时点:微博开放平台的回调地址在网页端配置,微信和 QQ 不需要回调地址,但微博必须。如果SINA_REDIRECT_URL配置错,微博分享会跳到浏览器里打开一个错误页。项目里也有另一个冷门问题:微博 SSO 分享时要求你的应用包名必须是开放平台登记的主包名,多进程或插件化改造后的包名不一致也会导致授权失败。
5. 避坑手册:分享集成最容易翻车的 4 个点
5.1 微信分享后无回调:Activity 没声明或签名不一致
现象:点击“分享到微信”后,微信客户端被拉起,但分享完成后直接回到应用,onResult和onCancel都不被触发,整个回调像进了黑洞。
原因:最常见的是WXCallbackActivity没有加到 Manifest,或者exported不是true。排在第二位的是签名不匹配——微信开放平台填的 MD5 签名跟你当前打包用的签名不一致。这两种情况的表现几乎一样,都是“无回调”。
解决:先检查 Manifest 里有没有com.umeng.socialize.weixin.view.WXCallbackActivity,再确认签名。签名用上面介绍的方法在 Debug 页打印出来,去微信开放平台后台逐字符比对。还有一个快速验证技巧:用adb shell am start -n手动拉起WXCallbackActivity,如果系统报Permission Denied,说明exported配错了。
5.2 混淆后分享面板白屏或直接闪退
现象:Debug 包一切正常,开了混淆的 Release 包一进分享面板就白屏,或者点击分享图标后秒退,控制台报ClassNotFoundException。
原因:主工程开启了代码压缩,但没有为友盟 SDK 保留必要的类。com.umeng下有大量通过反射调用的类,混淆后方法名或类名改变,运行时找不到入口。
解决:在混淆配置文件里加规则:
-keep class com.umeng.** { *; } -keep class com.umeng.socialize.** { *; } -keep class com.umeng.commonsdk.** { *; }如果业务里直接引用了com.umeng.socialize.media.UMWeb等类,确保它们也在 keep 范围内。我一般在proguard-rules.pro的开头把这四条规则固定写进去,下次新加模块也不会忘。注意-keep class com.umeng.** { *; }已经覆盖子包,后面两条属于“冗余防御”,但保留它们能防止别人误删第一行时翻车。
5.3 Android 7.0 以上分享本地图片崩溃
现象:分享一张本地相册图片,点击分享按钮后应用直接抛FileUriExposedException崩溃,只在 Android 7.0 及以上的设备出现。
原因:SDK 内部或平台 SDK 用file://协议传递图片 Uri,而高版本系统禁止应用之间在 Intent 中暴露file://Uri,必须改用content://配合 FileProvider。
解决:如果你是通过UMImage(this, fileUri)构造的图片,换成先从文件路径拿一张 Bitmap,再构造UMImage,或者使用自定义的 FileProvider 路径。一个更简单的兜底方案是:
UMImage image = new UMImage(context, new File(imagePath)); image.compressStyle = UMImage.CompressStyle.SCALE;设置compressStyle会让 SDK 对图片做缩放压缩并重写到一个临时文件,这个临时文件通常是用FileProvider或者缓存目录生成的,能在一定程度上规避直接抛文件 Uri 的问题。如果你已经给工程配了 FileProvider,也可以在 Manifest 里把 SDK 需要的临时目录映射声明出来,不过这个方法要兼容各平台实现,我建议还是让 SDK 自己处理图片更稳。
5.4 onError 报网络请求失败或 appkey 不存在
现象:分享按钮一按,回调立刻进入onError,Throwable.getMessage()显示“网络请求失败 请检查网络配置”,或者“AppKey不存在”,但网络明明是通的。
原因:UMConfigure.init没能在Application.onCreate的最早阶段执行,或者 AppKey 填写的不是友盟后台申请的 key。6.4.6 里 SDK 需要先通过一次网络请求校验 AppKey 和平台配置,校验失败就直接中断分享。
解决:把初始化代码移到onCreate的第一行,任何第三方库的初始化都不要放在它前面,有些库会改写ClassLoader或网络栈,导致友盟初始化时拿到异常环境。然后核对 AppKey 是否是无空格、无换行的一串十六进制字符。最后,如果你的测试手机时间被改成非当前时间,HTTPS 证书校验会失败,也会表现为网络请求失败——这个坑我在老设备上遇到过,校准系统时间后一切恢复正常。
6. 二次开发:改面板、加平台、做埋点的三个入口
6.1 替换默认分享面板:只留逻辑不要 View
默认分享面板足够演示,但放到业务里往往不够精致。想自绘面板的话,不需要改动 SDK 的 jar,只要在构建分享动作时不调用默认面板即可。自己画的弹窗布局里,每个平台按钮对应一次ShareAction指定平台的调用:
new ShareAction(activity) .setPlatform(SHARE_MEDIA.WEIXIN) .withMedia(web) .setCallback(listener) .share();这样分享面板就成了你自己完全可控的 View,平台顺序、品牌色、是否展示“更多”按钮都由业务决定。SDK 的shareviewjar 可以直接从 libs 里移除,减小包体。需要注意的是,回调 Activity 仍然依赖平台 jar,不能顺手删掉。
6.2 加一个内部工具类统一打点和回调
我习惯在项目里包一层ShareHelper,把所有分享入口收拢到一个类里。这样埋点、日志、判断平台安装情况都只写一遍。每个分享入口记录onStart到onResult的耗时,顺便把失败异常统一上报到自己的监控平台,问题定位效率比翻日志高很多。
6.3 验证方法和一点习惯
改完面板后,验证顺序是:先用 Debug 包在真机上分别点三个平台,确认回调都能触发;再开混淆跑一次 Release,把上面提到的 proguard 规则实际过一遍;最后把签名切换成正式签名,在微信后台核对一遍。从那以后,我每次接社交分享都会强制走一遍这个测试矩阵,凡是“分享后无回调”的反馈,先查 Manifest 再查签名,省掉了无数瞎猜的时间。希望这些拆包和排障的思路,能帮你在自己的项目里少踩几个坑。
本文还有配套的精品资源,点击获取