在集成微信分享的过程中,如果缩略图
是url形式,或者大图分享
的图片是个url,就需要我们先把图片下载下来,然后依据微信的要求对图片做一些压缩操作,最后将图片的数据设置给要分享的对象即可。
我们一般需要支持的分享类型主要有文字类型(WXTextObject)
、图片类型(WXImageObject)
和网页类型(WXWebpageObject)
,具体请看分享与收藏功能-Android开发手册。
拿我们常见的网页分享举例,msg.thumbData
对应的就是缩略图对象,具体代码如下:
//初始化一个WXWebpageObject,填写url
WXWebpageObject webpage = new WXWebpageObject();
webpage.webpageUrl ="网页url";
//用 WXWebpageObject 对象初始化一个 WXMediaMessage 对象
WXMediaMessage msg = new WXMediaMessage(webpage);
msg.title ="网页标题 ";
msg.description ="网页描述";
Bitmap thumbBmp = BitmapFactory.decodeResource(getResources(), R.drawable.send_music_thumb);
msg.thumbData =Util.bmpToByteArray(thumbBmp, true);
//构造一个Req
SendMessageToWX.Req req = new SendMessageToWX.Req();
req.transaction = buildTransaction("webpage");
req.message =msg;
req.scene =mTargetScene;
req.userOpenId = getOpenId();
//调用api接口,发送数据到微信
api.sendReq(req);
示意图如下:
我们看下thumbData
的定义,如下图所示:
thumbData
的类型是字节数组byte[]
,并且大小不超过32kb
。
一般情况下,thumbData
设置的是应用内置的icon
,通过BitmapFactory.decodeResource
即可获取到对应的Bitmap对象,接着对bitmap进行压缩,最后将Bitmap的对应的字节数组设置给thumbData
即可。
如果thumbData
的数据来源是url,则我们需要先下载图片,再进行后续的操作。经过研究,我们可以通过glide
提供的api来下载图片url对应的字节数组
,接着将字节数组压缩到32k以内,最后将压缩后的字节数组设置给thumbData
即可。
byte[] bytes = Glide.with(context)
.load(url)
.asBitmap()
.toBytes()
.into(150, 150)
.get();
/**
* 将Bitmap的字节流压缩为目标大小
*
* @param src
* @param targetSize 单位B
* @return
*/
private static byte[] compressBitmapBytes2TargetSize(byte[] src, int targetSize) {
// 将字节数据转换成临时bitmap对象,为压缩做准备
Bitmap bmp = BitmapFactory.decodeByteArray(src, 0, src.length);
byte[] result = getBytesFromCompressBitmap(bmp, targetSize);
// 回收不用的Bitmap
if (!bmp.isRecycled()) {
bmp.recycle();
}
return result;
}
/**
* 压缩bitmap的字节数据,quality每次减少5
* @param bitmap
* @param targetSize
* @return
*/
private static byte[] getBytesFromCompressBitmap(Bitmap bitmap, int targetSize) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 默认quality为100,quality取值范围[0, 100]
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.PNG, quality, baos);
byte[] bytes = baos.toByteArray();
while (bytes.length > targetSize && quality >= 5) {
quality -= 5;
if (quality < 0) {
quality = 0;
}
// 重置,不然会累加
baos.reset();
// 将数据写入ByteArrayOutputStream对象中
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
// 将流转换成字节数组
bytes = baos.toByteArray();
}
// 关闭流
try {
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
return bytes;
}
具体调用:
// 略缩图byte[]小于32k
msg.thumbData = compressBitmapBytes2TargetSize(bytes, 32 * 1024);
由于下载url的字节数据这个操作是io操作,所以我们需要放到子线程中来完成,下载完成后再在主线程中进行处理即可。完整版代码如下:
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
try {
byte[] bytes = Glide.with(context)
.load(url)
.asBitmap()
.toBytes()
.into(150, 150)
.get();
emitter.onNext(bytes);
emitter.onComplete();
} catch (Exception e) {
e.printStackTrace();
emitter.onError(new Throwable("下载略缩图失败"));
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(byte[] bytes) {
if (bytes != null) {
// 略缩图byte[]小于32k
msg.thumbData = compressBitmapBytes2TargetSize(bytes, 32 * 1024);
} else {
// 设置默认的缩略图,这里一般使用应用logo
}
// 发送分享请求
}
@Override
public void onError(Throwable e) {
// 设置默认的缩略图,这里一般使用应用logo
// 发送分享请求
}
@Override
public void onComplete() {
}
});
这里使用了RxJava做的切换线程的操作。当然了,这里的压缩bitmap数据也属于io操作,也应该放在子线程中完成,这个细节有待完善。
与缩略图的处理方式不同,缩略图我们是知道最终要显示的大小的(150,来自微信官方demo),所以我们直接通过下载byte[]进行后续即可。而我们这里要分享的大图的大小和宽高等信息都是未知的,在下载完成之前我们无法通过url获取更多信息,所以我们还是需要先将图片下载下来。
由于下载byte[]的api必须指定宽高,所以我们换另一个不需要指定宽高的api,直接下载bitmap对象,具体代码如下:
Glide.with(mContext)
.load(url)
.asBitmap()
.into(new SimpleTarget() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation super Bitmap> glideAnimation) {
// bitmap下载完成
}
});
我们再看下微信提供的分享图片的api,它支持图片的字节数组
和图片的本地路径
两种方式,具体的大小都不能超过10M。
如果我们使用字节数组
这种方式来实现,即使我们的图片小于10M,或者我们把bitmap对应的字节数组压缩到10M以下了,依然会遇到分享不成功的问题。失败的主要原因是因为Intent传值有大小限制,最大只能512KB,给微信发送分享数据,最终还是通过Binder传递的,Binder传递的数据大小很有限,这一步还是行不通。另外,如果图片比较大,对应的bitmap对象也很大,进行压缩等操作会极其耗时,影响用户体验。
这里选择使用图片的本地路径
这种方式来实现,先将图片下载到本地的bitmap对象,然后将bitmap存储到手机上,将对应的存储路径设置给imagePath参数即可。
具体代码如下:
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(final ObservableEmitter emitter) throws Exception {
Glide.with(mContext)
.load(url)
.asBitmap()
.into(new SimpleTarget() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation super Bitmap> glideAnimation) {
emitter.onNext(resource);
emitter.onComplete();
}
});
}
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.map(new Function() {
@Override
public BitmapAndFilePathBean apply(Bitmap bitmap) throws Exception {
String filePath = "";
if (bitmap != null) {
// 将图片存储到手机,返回决定路径
}
BitmapAndFilePathBean bitmapAndFilePathBean = new BitmapAndFilePathBean(bitmap, filePath);
return bitmapAndFilePathBean;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(BitmapAndFilePathBean bitmapAndFilePathBean) {
WXImageObject imgObj = new WXImageObject();
//设置大图
imgObj.setImagePath(bitmapAndFilePathBean.getFilePath());
WXMediaMessage msg = new WXMediaMessage();
msg.mediaObject = imgObj;
// 设置缩略图
Bitmap thumbBmp = Bitmap.createScaledBitmap(bitmapAndFilePathBean.getBitmap(), 150, 150, true);
msg.thumbData = ImageUtil.bitmapToByteArray(thumbBmp, true);
// 发送分享请求
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
这里的BitmapAndFilePathBean对象其实是Bitmap对象和filePath的包装类,具体如下:
public class BitmapAndFilePathBean extends BaseBean {
private Bitmap bitmap;
private String filePath;
public BitmapAndFilePathBean(Bitmap bitmap, String filePath) {
this.bitmap = bitmap;
this.filePath = filePath;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
当然了,保存图片到手机是需要文件读写权限的,需要做好动态权限申请和校验。
关于微信分享中使用url设置图片的问题,这里提供了一个解决思路,同学们如果有更好的方式,欢迎沟通。
Android 使用Glide下载图片的几种方式
Glide坑遇记:宽度铺满高度自适应 & GIF加载之坑
微信分享大图遇到的问题(Android)
bitmap的六种压缩方式,Android图片压缩