问题描述
最近接手了一个“图片分享给微信好友清晰度优化”的任务,心想,这还不小菜一碟。首先看了下需求:公司app有个将界面生成一张长图片并支持微信分享的功能。正常来讲,分享的图片不清晰应该和图片压缩有关系,可是事实并非想象的那么简单,我们项目中的这个功能是先生成长图并保存到本地,之后直接调用微信分享API,通过传递本地路径的方式实现的。项目中图片生成相关代码本身并没有什么问题,保存到本地的图片是很清晰的,可是为什么分享出去的图片变模糊了?
分析研究
笔者对微信分享图片相关内容进行了一些研究。这里对测试过程中用到的手机和图片进行如下说明:
➣ 发送方手机均使用的Redmi Note4,5.5英寸分辨率为1920x1080
➣ 接收方(好友)手机均使用的三星GALAXY Note 3 Lite(N7508V/移动4G),5.5英寸分辨率为1280x720
➣ 截图规格:1080x1920px,263.32kb,png格式
➣ 生成图规格:1080x6596px,441.21kb,jpg格式(先根据Activity中显示的View,利用view.draw(canvas)绘制到使用Bitmap.Config.RGB_565色彩模式的Bitmap上,质量压缩30%,然后通过FileOutputStream输出保存图片)
使用微信API进行分享
使用微信API进行分享,有两种方式,一种是发送图片的二进制数据,一种是发送图片的路径,具体可以参考微信开放平台。或者直接下载范例代码。
1. 发送图片的二进制数据
使用图片的二进制数据进行分享,微信分享的SDK中默认会对Bitmap进行85%的质量压缩,而且微信的Demo中还对Bitmap进行了尺寸压缩(限制图片尺寸),最后是将图片转换成字节数组(byte[])调用分享API进行的分享。
此种方式适合小图分享,发送出去的图片效果和我们最终处理之后的图片应该基本一致,笔者未做对比测试,其实无论使用那种方式进行微信图片分享,都会通过Intent启动微信的一个Activity(分享界面),对于这种方式,需要我们注意最终传递的字节数组的大小,不然很有可能发生TransactionTooLargeException。
2. 发送图片的本地路径
此种方式也是我们app中使用的图片分享方式,微信Demo中还传递了一个缩略图(thumbData字节数组长度限制在32k以内),主要用于微信好友选择界面,笔者发现如果不传缩略图,依然可以进行分享,比如我们直接分享一个长图,在微信好友选择界面看到的缩略图会以长图的中心为中心进行相应截取。
发送1080x1920px,263.32kb截图
发送到朋友圈:从发送方朋友圈看图片清晰度明显发生变化,从发送方微信朋友圈保存到本地,图片变为720x1280px,96.45k,从好友朋友圈保存到本地,图片变为720x1280px,92.27k,微信保存的图片均为jpg格式,以下不再做说明。
发送给好友:从发送方朋友圈看图片清晰度明显发生变化,从发送方微信朋友圈保存到本地,图片变为540x960px,244.38k,从好友朋友圈保存到本地,图片变为540x960px,238.65k。
发送1080x6596px,441.21kb生成图
发送到朋友圈:从发送方朋友圈看图片清晰度没有变化,从发送方微信朋友圈保存到本地,图片和原图一致,为1080x6596px,441.21k,从好友朋友圈保存到本地,图片变为1080x6596px,596.02k。
发送给好友:从发送方朋友圈看图片明显模糊了很多,但是从发送方微信朋友圈保存到本地,图片仍为1080x6596px,441.21k,在好友微信对话中查看也是非常模糊的,从好友朋友圈保存到本地,图片变为1080x6596px,430.87k。
由此可见,通过本地路径的分享方式,还是蛮坑的,对于普通图片进行分享,无论发送给好友还是分享到朋友圈,都会经过尺寸压缩,而且分享到朋友圈和分享给好友的压缩策略也不同。对于特殊图片(应该是图片尺寸比较大的,笔者试过1080x1920px,2.8M的图片进行测试,效果和“普通图片”结果类似,都会被按相同尺寸压缩),如笔者使用的生成图,在发送方,在发送成功之后,删除发送方本地的图片,再查看发送的记录,会出现“图片已过期或已被清理”,但朋友圈中的记录不受影响,而且保存到本地的文件和原图一模一样。对于我们的app来讲,因为发送的是长图,分享到朋友圈基本上没什么大问题,最大的坑就是分享给好友,好友在聊天记录中点开图片看到的是不清楚的,好在保存到本地之后是清晰的,可是谁会这样操作呢?所以微信的图片显示功能还是有待优化的。分析到这,笔者也已死心,笔者之前还好奇“大智慧”app中的图片分享为什么不模糊,反编译其代码才知道,“大智慧”app用的是“发送图片的二进制数据”的分享方式,真是心大。
使用系统自带分享功能
这里提一下系统分享,Android系统中一般都自带分享功能,使用系统自带的文件浏览工具,打开SD卡中的一张图片,或者直接使用“相册”选中一张图片,找到“发送”功能,并点击“发送”,系统便会将所有支持图片发送功能的app列出来供我们选择,这里选择微信或者朋友圈,就可以将图片发送给微信好友或者分享到朋友圈。
此种方式本质上和通过如下代码调起系统分享类似:1
2
3
4
5Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "分享到"));
使用该方式分享到朋友圈的图片大小与原图相同,质量看不出差别;该方式分享给微信好友,发送的图片一般会被压缩,一些特殊的图片,像长图,在接收方可以选择“查看原图”,但是这里“查看原图”的大小与发送的原图所占磁盘空间是不同的。
发送1080x1920px,263.32kb的截图
发送到朋友圈:从发送方朋友圈看图片清晰度明显发生变化,从发送方微信朋友圈保存到本地,图片变为720x1280px,96.45k,从好友朋友圈保存到本地,图片变为720*1280px,92.27k。
发送给好友:从发送方朋友圈看图片清晰度明显发生变化,从发送方微信朋友圈保存到本地,图片变为720x1280px,107.57k,从好友朋友圈保存到本地,图片变为720*1280px,105.05k。
发送1080x6596px,441.21kb的生成图
发送到朋友圈:从发送方朋友圈看图片清晰度没有变化,从发送方微信朋友圈保存到本地,图片变为1080x6596px,441.21k,从好友朋友圈保存到本地,图片变为1080x6596px,596.02k。
发送给好友:从发送方朋友圈看图片清晰度没有变化,从发送方微信朋友圈保存到本地,图片变为1080x6596px,441.21k,好友微信对话中出现“查看原图(430K)”的选项,从好友朋友圈保存到本地,图片变为1080x6596px,430.87k。
根据测试结果,可以看出,至少对于长图来讲,分享出去的图片尺寸没有经过微信压缩,肉眼看都是和原图没什么差别。那能不能直接使用Intent.ACTION_SEND
的方式直接调起微信分享的界面来达到我们app的分享需求呢?能是能,只要知道微信的包名和微信分享界面的Activity信息,进行定向分享即可,但这种方式貌似无法得知分享的结果(怎么告知微信分享的来源?),而且保不准分享界面的Activity信息哪天就变了,不太满足我们项目需求。
微信分享API中要调起的微信类信息如下(来自com.tencent.mm.opensdk.openapi.WXApiImplV10.class中sendReq(BaseReq var1)方法),微信可以根据appid进行回调,通知app分享结果:1
2
3
4
5
6
7......
Args var6;
(var6 = new Args()).bundle = var2;
var6.content = "weixin://sendreq?appid=" + this.appId;
var6.targetPkgName = "com.tencent.mm";
var6.targetClassName = "com.tencent.mm.plugin.base.stub.WXEntryActivity";
return MMessageActV2.send(this.context, var6);
指定微信进行分享的代码如下(分享给微信好友):1
2
3
4
5
6
7
8
9Intent intent = new Intent();
//朋友圈:"com.tencent.mm.ui.tools.ShareToTimeLineUI"
//好友:"com.tencent.mm.ui.tools.ShareImgUI"
ComponentName comp = new ComponentName("com.tencent.mm","com.tencent.mm.ui.tools.ShareImgUI");
intent.setComponent(comp);
intent.setAction(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));//uri为你要分享的图片的uri
startActivity(intent);
附:图片生成
View相关图片的生成方式,可以通过canvas绘制到指定大小的Bitmap上,类似代码如下:1
2
3
4
5
6public static Bitmap takeViewScreenShort(View view) {
Bitmap result = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(result);
view.draw(canvas);
return result;
}
这里还是要啰嗦下:Bitmap质量压缩不会影响Bitmap所占用的内存空间,Bitmap占用内存空间的计算方式始终是:占用内存 = 图片长度 图片宽度 单位像素占用的字节数。微信缩略图的处理一般既要使用尺寸压缩,又要使用质量压缩,以此限制thumbData长度。质量压缩会影响到该Bitmap保存到本地的文件大小。