QQ Connect Integration
Resolving QQ Zone Sharing Issues
When sharing to QQ Zone, a common pitfall causes the share dialog to fail silent. The mistake involves passing an image URL string directly instead of using a list. The incorrect approach uses:
params.putString(QzoneShare.SHARE_TO_QQ_IMAGE_URL, "image_link_here");
This will not trigger an error but prevents the share page from opaning. The field expects an ArrayList<String>. Fix it by constructing a list and calling the appropriate method:
ArrayList<String> imageUrls = new ArrayList<>();
imageUrls.add("https://example.com/avatar.png");
params.putStringArrayList(QzoneShare.SHARE_TO_QQ_IMAGE_URL, imageUrls);
A correct implementation example:
public static void shareToQzone(Activity context, IUiListener listener) {
Bundle params = new Bundle();
params.putInt(QzoneShare.SHARE_TO_QZONE_KEY_TYPE,
QzoneShare.SHARE_TO_QZONE_TYPE_IMAGE_TEXT);
params.putString(QzoneShare.SHARE_TO_QQ_TITLE, "Title");
params.putString(QzoneShare.SHARE_TO_QQ_SUMMARY, "Summary");
params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL,
"https://www.qq.com/news/1.html");
ArrayList<String> imageUrls = new ArrayList<>();
imageUrls.add("https://example.com/cover.jpg");
params.putStringArrayList(QzoneShare.SHARE_TO_QQ_IMAGE_URL, imageUrls);
mTencent.shareToQzone(context, params, listener);
}
Distinguishing Local and Network Images
Both QQ and QZone shares support local and remote images using different keys:
- Network image:
QQShare.SHARE_TO_QQ_IMAGE_URLorQzoneShare.SHARE_TO_QQ_IMAGE_URL - Local image:
QQShare.SHARE_TO_QQ_IMAGE_LOCAL_URLorQzoneShare.SHARE_TO_QQ_IMAGE_LOCAL_URL
params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, shareCoverUrl);
Make sure to use the appropriate putStringArrayList for the space sharing as described above.
WeChat SDK Integration
Initial Setup in Application
Register the WeChat API inside your Application subclass:
public static IWXAPI api;
private void registWX() {
api = WXAPIFactory.createWXAPI(this, WX_APP_ID, false);
api.registerApp(WX_APP_ID);
}
Share Manager Implementation
A manager class encapsulates WeChat sharing logic for different content types, avoids reusing stale data by creating new objects each time, and handles network image fetching off the main thread.
public class WechatShareManager {
private static final int THUMB_SIZE = 150;
public static final int WECHAT_SHARE_WAY_TEXT = 1;
public static final int WECHAT_SHARE_WAY_PICTURE = 2;
public static final int WECHAT_SHARE_WAY_WEBPAGE = 3;
public static final int WECHAT_SHARE_WAY_VIDEO = 4;
public static final int WECHAT_SHARE_TYPE_SESSION =
SendMessageToWX.Req.WXSceneSession;
public static final int WECHAT_SHARE_TYPE_TIMELINE =
SendMessageToWX.Req.WXSceneTimeline;
private IWXAPI mWXApi;
private Context mContext;
public WechatShareManager(Context context) {
mContext = context;
mWXApi = MyApplication.api; // reuse the globally registered API
}
public void shareByWechat(ShareContent shareContent, int shareType) {
if (!mWXApi.isWXAppInstalled()) {
Toast.makeText(mContext, "WeChat not installed", Toast.LENGTH_SHORT).show();
return;
}
switch (shareContent.getShareWay()) {
case WECHAT_SHARE_WAY_TEXT:
performTextShare(shareContent, shareType);
break;
case WECHAT_SHARE_WAY_PICTURE:
performPictureShare(shareContent, shareType);
break;
case WECHAT_SHARE_WAY_WEBPAGE:
performWebPageShare(shareContent, shareType);
break;
case WECHAT_SHARE_WAY_VIDEO:
performVideoShare(shareContent, shareType);
break;
}
}
// Abstract content class
public abstract static class ShareContent {
public abstract int getShareWay();
public abstract String getContent();
public abstract String getTitle();
public abstract String getURL();
public abstract int getPictureResource();
public abstract String getPictureUrl();
}
public static class TextContent extends ShareContent {
private String text;
public TextContent(String text) { this.text = text; }
@Override
public int getShareWay() { return WECHAT_SHARE_WAY_TEXT; }
@Override public String getContent() { return text; }
@Override public String getTitle() { return null; }
@Override public String getURL() { return null; }
@Override public int getPictureResource() { return -1; }
@Override public String getPictureUrl() { return null; }
}
// Other content types (PictureContent, WebPageContent, VideoContent) follow the same pattern.
private void performWebPageShare(ShareContent shareContent, int shareType) {
new Thread(() -> {
try {
WXWebpageObject webpage = new WXWebpageObject();
webpage.webpageUrl = shareContent.getURL();
WXMediaMessage msg = new WXMediaMessage(webpage);
msg.title = shareContent.getTitle();
msg.description = shareContent.getContent();
Bitmap bmp = BitmapFactory.decodeStream(
new URL(shareContent.getPictureUrl() + "?thumb=1").openStream());
Bitmap thumb = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
bmp.recycle();
if (thumb != null) {
msg.thumbData = Util.bmpToByteArray(thumb, true);
}
SendMessageToWX.Req req = new SendMessageToWX.Req();
req.transaction = buildTransaction("webpage");
req.message = msg;
req.scene = shareType;
mWXApi.sendReq(req);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private String buildTransaction(String type) {
return type == null ? String.valueOf(System.currentTimeMillis()) :
type + System.currentTimeMillis();
}
}
Usage example:
WechatShareManager shareManager = new WechatShareManager(context);
WechatShareManager.WebPageContent webContent =
new WechatShareManager.WebPageContent(
bean.getTitle(), bean.getDesc(), bean.getUrl(), bean.getCoverUrl());
shareManager.shareByWechat(webContent, WechatShareManager.WECHAT_SHARE_TYPE_SESSION);
WeChat Login Integration
Create a dedicated WXEntryActivity that implements IWXAPIEventHandler and disstinguishes between login and share callbacks.
public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
public static final int LOGIN_TYPE = 1;
public static final int SHARE_TYPE = 2;
private IWXAPI api;
private BaseResp resp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
api = MyApplication.api;
api.handleIntent(getIntent(), this);
}
@Override
public void onResp(BaseResp resp) {
switch (resp.errCode) {
case BaseResp.ErrCode.ERR_OK:
if (resp.getType() == LOGIN_TYPE) {
String code = ((SendAuth.Resp) resp).code;
// Forward code to backend or exchange for tokens
loginCallback.onCodeReceived(code);
} else if (resp.getType() == SHARE_TYPE) {
Toast.makeText(this, "Shared successfully", Toast.LENGTH_SHORT).show();
}
finish();
break;
case BaseResp.ErrCode.ERR_USER_CANCEL:
Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show();
finish();
break;
case BaseResp.ErrCode.ERR_AUTH_DENIED:
Toast.makeText(this, "Auth denied", Toast.LENGTH_SHORT).show();
finish();
break;
default:
finish();
break;
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
finish();
}
@Override
public void onReq(BaseReq req) { finish(); }
}
To initiate login:
if (!api.isWXAppInstalled()) {
Toast.makeText(this, "Please install WeChat", Toast.LENGTH_SHORT).show();
return;
}
WXEntryActivity.loginCallback = code -> loginManager.weChatLogin(code);
SendAuth.Req req = new SendAuth.Req();
req.scope = "snsapi_userinfo";
req.state = "app_state";
api.sendReq(req);
Common Issues
- Network images causing IOExceptions on main thread: Use a background thread as shown in the manager.
- "hit push hold" error during share: Frequently caused by expired or concurrent WeChat sessions on the device. Log out and log back in.
- Thumbnail size limit: Always scale bitmaps appropriately (e.g., 150x150) to avoid silent failures when invoking the share dialog.