Android 微信图片分享 TransactionTooLargeException

818 阅读3分钟

问题描述:
在做微信图片分享的时候,分享图片至微信好友,如果立即选择好友发送的话没有问题。但是在选择好友的页面停留一段时间后再选择朋友发送,选择界面将会闪退。发送对话框仍然会弹出,并且能够发送成功。
正常效果如下:

正常.jpg 异常效果:

异常.jpg

图片的分享代码如下:

public void share() {
	Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.send_img);
	WXImageObject imgObj = new WXImageObject(bmp);
	
	WXMediaMessage msg = new WXMediaMessage();
	msg.mediaObject = imgObj;
	
	Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
	bmp.recycle();
	msg.thumbData = Util.bmpToByteArray(thumbBmp, true);

	SendMessageToWX.Req req = new SendMessageToWX.Req();
	req.transaction = buildTransaction("img");
	req.message = msg;
	req.scene = mTReq(req);
	
	//finish();
}        

调试发现如下报错信息:

2022-08-02 19:50:49.626 1320-1338/? E/ActivityTaskManager: Transaction too large, intent: Intent { cmp=com.tencent.mm/.ui.transmit.SendAppMessageWrapperUI (has extras) }, extras size: 417988, icicle size: 0  
2022-08-02 19:50:59.628 1320-1338/? W/ActivityTaskManager: Exception when starting activity com.tencent.mm/.ui.transmit.SendAppMessageWrapperUI
    android.os.TransactionTooLargeException: data parcel size 427240 bytes
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(BinderProxy.java:527)
        at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction(IApplicationThread.java:2591)
        at android.app.servertransaction.ClientTransaction.schedule(ClientTransaction.java:135)
        at com.android.server.wm.ClientLifecycleManager.scheduleTransaction(ClientLifecycleManager.java:47)
        at com.android.server.wm.ActivityStackSupervisor.realStartActivityLocked(ActivityStackSupervisor.java:1126)
        at com.android.server.wm.ActivityStackSupervisor.startSpecificActivityLocked(ActivityStackSupervisor.java:1263)
        at com.android.server.wm.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:3590)
        at com.android.server.wm.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:3024)
        at com.android.server.wm.RootActivityContainer.resumeFocusedStacksTopActivities(RootActivityContainer.java:1207)
        at com.android.server.wm.RootActivityContainer.resumeFocusedStacksTopActivities(RootActivityContainer.java:1194)
        at com.android.server.wm.ActivityStack.completePauseLocked(ActivityStack.java:2230)
        at com.android.server.wm.ActivityStack.activityPausedLocked(ActivityStack.java:2157)
        at com.android.server.wm.ActivityTaskManagerService.activityPaused(ActivityTaskManagerService.java:2452)
        at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:2194)
        at android.os.Binder.execTransactInternal(Binder.java:1021)
        at android.os.Binder.execTransact(Binder.java:994)

可以发现报了 TransactionTooLargeException 错误。错误日志是 Binder 类出现的,字面意思是数据过大。查看微信的 SDK 源码可以发现实际上调用微信 API,微信会将图片传递给一个新的 Activity。而 Intent传值有大小限制,最大只能512KB !
既然发现了问题,那该如何解决?
既然直接传图片的Bitmap太大,那就把图片先保存在本地,然后直接传递图片的地址。

解决方法:
通过查询微信分享文档可以发现两种分享图片的方式:
WXImageObject (WXMediaMessage.IMediaObject的派生类,用于描述一个图片对象)

字段类型含义备注
imageDatabyte[]图片的二进制数据内容大小不超过10MB
imagePathString图片的本地路径对应图片内容大小不超过10MB

上述代码采用的是 imageData 直接传递图片的二进制数据。我们改成传递图片本地路劲的方式的话,先要保存图片到本地。 如果分享的消息中涉及文件路径(如图片类型消息),建议开发者针对 Android 7.0 版本及以上设备,判断微信版本支持的情况下,更新为 FileProvider 的方式进行分享。详情查阅《OpenSDK 支持 FileProvider 方式分享文件到微信文档》

保存图片代码:

private static String saveImage(Context context, Bitmap bitmap) {
	String storePath = context.getExternalCacheDir() + File.separator + "share";
	File appDir = new File(storePath);
        // 此处可以考虑每次保存的话将老的文件删除,这样起到删除缓存的作用
	if (!appDir.exists()) {
		appDir.mkdir();
	}

	String fileName = System.currentTimeMillis() + ".jpg";
	File file = new File(appDir, fileName);
	try {
		FileOutputStream fileOutputStream = new FileOutputStream(file);
		bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
		fileOutputStream.close();
	} catch (IOException e) {

	}
        
        String path;
        if (checkVersionValid(context) && checkAndroidNotBelowN()) {
             Uri contentUri = FileProvider.getUriForFile(context, 
                "com.example.app.fileprovider",  // 要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app
                file);

            // 授权给微信访问路径
            context.grantUriPermission("com.tencent.mm",  // 这里填微信包名    
                contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

            path = contentUri.toString();   // contentUri.toString() 即是以"content://"开头的用于共享的路径
        } else {
                path = file.getAbsolutePath();
        }
	return path;
}

改良之后的分享图片代码:

public void share() {
	Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.send_img);
	WXImageObject imgObj = new WXImageObject();
	// 保存图片,返回存储路径
	String path = saveImage(context, bmp);
	imgObj.setImagePath(path);
	
	WXMediaMessage msg = new WXMediaMessage();
	msg.mediaObject = imgObj;
	
	Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
	bmp.recycle();
	msg.thumbData = Util.bmpToByteArray(thumbBmp, true);

	SendMessageToWX.Req req = new SendMessageToWX.Req();
	req.transaction = buildTransaction("img");
	req.message = msg;
	req.scene = mTargetScene;
	api.sendReq(req);
	
	//finish();
}

简单总结下来,就是不使用 WXImageObject 中的 imageData 直接传递图片二进制数据,而是使用 imagePath 传递保存的图片本地路径。