记录Android微信分享功能的吐槽与思考

在App内潜入分享到微信好友或朋友圈的功能想必大家已经屡见不鲜了,比如Android分享一个网页信息(URL)到微信客户端的代码:

 /**
 * 微信分享:分享网页
 * @param context
 * @param url
 * @param title
 * @param description
 * @param scene
 */
 public static void shareToWeChatWithWebpage(Context context, String url,
 String title, String description, int scene){
 IWXAPI iwxapi = WXAPIFactory.createWXAPI(context, WXEntryActivity.WXAPI_APP_ID);
 if (!iwxapi.isWXAppInstalled()){
 ToastManager.getInstance(context.getApplicationContext()).showToast("您尚未安装微信客户端");
 return;
 }
 WXWebpageObject wxWebpageObject = new WXWebpageObject();
 wxWebpageObject.webpageUrl = url;
 WXMediaMessage wxMediaMessage = new WXMediaMessage(wxWebpageObject);
 wxMediaMessage.mediaObject = wxWebpageObject;
 wxMediaMessage.title = title;
 wxMediaMessage.description = description;
 wxMediaMessage.thumbData =
 ImageManager.bmpToByteArray(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_share_invite), true);
 SendMessageToWX.Req req = new SendMessageToWX.Req();
 req.transaction = String.valueOf(System.currentTimeMillis());
 req.message = wxMediaMessage;
 req.scene = scene;
 iwxapi.sendReq(req);
 } 

虽然已经在不同的App内使用了N遍,但在最近的一个项目中还是出了错:执行完这段代码,应用没有任何反应,无法调起微信客户端,并且没有任何错误信息打印提示。不得已查看官方资料 —— [Android常见问题],找到这样一段提示: 

Q:调用wxapi.sendReq接口,返回true,但微信客户端并未启动,请检查以下几项:
A:
 1)微信是否安装
 2)调用时的Apk包名和签名是否与开放平台填写的一致,签名请使用该工具:点击下载,常发生在安装了debug版本又安装release版本情况,确定包名签名后卸载微信重装或者清除微信数据再做测试
 3)检查发送时的缩略图大小是否超过32k
 4)能够调起微信到选择好友列表,但是点击发送后无响应,请检查proguard配置是否对微信SDK代码进行了混淆,建议不要对SDK对混淆,参考以下proguard配置:
 -keep class com.tencent.mm.sdk.** {
 *;

经检查,发现代码iwxapi.sendReq(req);执行过后返回了false,其实按照上面Q&A的写法,已经不属于该问题范畴了。但是还是照着这四点检查了一遍,发送的缩略图本地预览大小只有不到20KB,其他配置也没有问题,可还是出错,到底问题出在哪里了呢?
 纠结,沉思,差点就怀疑人生了!最后冒着试一试的态度,我把缩略图换成一张不到7KB的小图,再次执行代码,结果惊人地发现:iwxapi.sendReq(req);返回true,并成功调起微信客户端!当时心中一万头草泥马奔腾而过啊!
 一番激动之后,就开始研究了,为什么之前使用的缩略图没有超过官网文档32K的限制,却无法调起微信客户端呢,难道官网文档写错了,上限不是32KB?于是回归源码,打开微信SDK提供的类WXMediaMessage,找到如下定义的一系列常量:

public static final int THUMB_LENGTH_LIMIT = 32768;
private static final int TITLE_LENGTH_LIMIT = 512;
private static final int DESCRIPTION_LENGTH_LIMIT = 1024;
private static final int MEDIA_TAG_NAME_LENGTH_LIMIT = 64;
private static final int MESSAGE_ACTION_LENGTH_LIMIT = 2048;
private static final int MESSAGE_EXT_LENGTH_LIMIT = 2048; 

果不其然,微信SDK对于分享到微信的缩略图大小、标题长度、描述长度等信息都做了限制。其中,缩略图大小限制为32768,源码中并没有注释写明单位。好奇的我将其除以1024,刚好得到32,这不就是官网文档提到的上限值32KB嘛(说明源码中的数值单位为Byte)!那就是说官网文档没有写错,可是问题出在哪儿了呢?
 其实事关图片的实际硬盘占用大小和内存占用大小问题。存放在电脑硬盘中的图片文件,会根据不同图片格式的压缩规则进行压缩,从而减少硬盘占用大小,比如常见如JPEG这种有损压缩的图片格式。而在Android系统中,将图像读取到内存当中所占用的内存大小与图片存放在硬盘当中的实际大小没有一点关系,可能更大,也可能更小,使用如下代码即可获取图像所占用的内存大小:

 private Bitmap decodeResource(Resources resources, int id) {
TypedValue value = new TypedValue();
resources.openRawResource(id, value);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inTargetDensity = value.density;
Bitmap bitmap = BitmapFactory.decodeResource(resources, id, opts);
Log.i("Bitmap", "size is " + bitmap.getRowBytes() * bitmap.getHeight());
return bitmap;
} 

其中,bitmap.getRowBytes() * bitmap.getHeight()获取的便是Bitmap的内存占用大小,单位为Byte,再除以1024,便可以转换为BK单位。注意:上述从资源中获取Bitmap对象的过程,并没有直接使用decodeResource(Resources res, int id)含带两个参数的方法,是为了避免由于图片存放在不同drawable或者mipmap文件夹下导致的内存占用不一致问题,对Android屏幕适配有所了解的朋友应该懂得这个,这里就不细说了,大家可以参考凯子哥的一篇文章 —— [关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析]。
 通过PS工具,修改缩略图尺寸大小,然后通过上面这段代码测试不同大小的图片在Android手机中所占用的内存大小,同时查看是否可以调起微信客户端。经过这样的测试,最终发现,微信SDK和官方文档中的32KB缩略图上限大小指的是内存占用大小,而非图片的硬盘占用大小。这样,也就解决了前面我所遇到的问题。
 最后,还是得吐槽一下Android微信SDK的诟病,也是一些包括支付宝SDK在内其他第三方服务供应商的通用问题,别无他意,仅作发泄:
 •签名唯一性
 做Android开发的都知道,开发过程中编译打包并运行在手机或模拟器上的apk文件使用的是IDE提供的默认通用签名,而正式上线发布的apk文件使用的是开发人员自定义的正式签名文件。微信SDK在注册应用时只能输入一个签名信息,导致必须在正式包中测试微信SDK相关功能,而正式包又无法做到跟踪调试,非常不方便。当然你也可以这样做,处于开发阶段时,在微信开放平台注册测试包的签名信息,上线时再修改成正式签名文件信息;或者你也可以修改IDE的默认签名文件。但是这些都不是很方便,如果微信开放平台能够像其他一些第三方服务供应商一样,针对一个应用提供两个或多个签名信息的注册,岂不快哉。
 •文档不清晰
 很多大型的第三方服务供应商只管功能的提供,不管文档的说明,甚至连Samples代码都写的乱七八糟的,导致我们开发人员在使用过程中连个完整的参考说明都没有,出了问题也无从下手,白白浪费很多不必要的时间和精力。

文章有些啰嗦,主要是阐述了自己这次在开发微信分享时遇到问题、分析问题并解决问题的过程,希望给大家一些借鉴。