新浪微博開放平台提供 Android SDK 支援 App 唤起微博客戶端進行授權與分享。本文整合官方最新流程,涵蓋 SSO 授權、Token 持久化、多媒體分享 (文字/圖片/網頁)、回調處理 等完整實作,並針對常見坑點給出解法。
App Key、App Secret、設定 Redirect URL (如 https://api.weibo.com/oauth2/default.html)。// build.gradle
implementation 'com.sina.weibo.sdk:core:4.4.0' // 請用最新版
implementation 'com.sina.weibo.sdk:share:4.4.0'
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<activity
android:name="com.sina.weibo.sdk.web.WeiboSdkWebActivity"
android:configChanges="keyboardHidden|orientation"
android:exported="false"
android:windowSoftInputMode="adjustResize" />
<!-- 回調 Activity,需在包名下建立 wb_sdk_Activity -->
<activity
android:name=".wb_sdk_Activity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wb{AppKey}" /> <!-- 如 wb123456789 -->
</intent-filter>
</activity>
public class WeiboConstants {
public static final String APP_KEY = "YOUR_APP_KEY";
public static final String REDIRECT_URL = "https://api.weibo.com/oauth2/default.html";
public static final String SCOPE = "email,direct_messages_read,direct_messages_write,"
+ "friendships_groups_read,friendships_groups_write,statuses_to_me_read,"
+ "follow_app_official_microblog,invitation_write";
}
public class AccessTokenKeeper {
private static final String PREF = "weibo_token_pref";
private static final String KEY_TOKEN = "access_token";
private static final String KEY_EXPIRES = "expires_in";
private static final String KEY_UID = "uid";
public static void write(Context ctx, Oauth2AccessToken token) {
if (token == null) return;
ctx.getSharedPreferences(PREF, Context.MODE_PRIVATE).edit()
.putString(KEY_TOKEN, token.getToken())
.putLong(KEY_EXPIRES, token.getExpiresTime())
.putString(KEY_UID, token.getUid())
.apply();
}
public static Oauth2AccessToken read(Context ctx) {
SharedPreferences sp = ctx.getSharedPreferences(PREF, Context.MODE_PRIVATE);
String token = sp.getString(KEY_TOKEN, "");
long expires = sp.getLong(KEY_EXPIRES, 0);
String uid = sp.getString(KEY_UID, "");
if (token.isEmpty() || expires == 0) return null;
Oauth2AccessToken t = new Oauth2AccessToken(token, expires);
t.setUid(uid);
return t;
}
public static void clear(Context ctx) {
ctx.getSharedPreferences(PREF, Context.MODE_PRIVATE).edit().clear().apply();
}
}
public class ShareActivity extends AppCompatActivity
implements IWeiboHandler.Response {
private IWeiboShareAPI shareAPI;
private SsoHandler ssoHandler;
private Oauth2AccessToken accessToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share);
// 1. 初始化 SDK
shareAPI = WeiboShareSDK.createWeiboAPI(this, WeiboConstants.APP_KEY);
shareAPI.registerApp(); // 註冊到微博客戶端
// 2. 恢復 Token
accessToken = AccessTokenKeeper.read(this);
// 3. 處理從微博客戶端回傳的響應 (onNewIntent)
if (savedInstanceState != null) {
shareAPI.handleWeiboResponse(getIntent(), this);
}
findViewById(R.id.btn_share).setOnClickListener(v -> checkEnvAndShare());
}
/** 檢查環境並啟動分享流程 */
private void checkEnvAndShare() {
// 檢查微博客戶端是否安裝且支援 API
if (!shareAPI.isWeiboAppSupportAPI()) {
Toast.makeText(this, "請安裝微博客戶端", Toast.LENGTH_SHORT).show();
// 可引導至應用市場
return;
}
// Token 有效直接分享,否則先授權
if (accessToken != null && accessToken.isSessionValid()) {
sendMultiMessage();
} else {
ssoHandler = new SsoHandler(this, new AuthInfo(this, WeiboConstants.APP_KEY, WeiboConstants.REDIRECT_URL, WeiboConstants.SCOPE));
ssoHandler.authorize(new AuthListener());
}
}
/** SSO 授權回調 */
private class AuthListener implements WeiboAuthListener {
@Override public void onComplete(Bundle values) {
accessToken = Oauth2AccessToken.parseAccessToken(values);
if (accessToken.isSessionValid()) {
AccessTokenKeeper.write(ShareActivity.this, accessToken);
sendMultiMessage(); // 授權成功後直接分享
} else {
Toast.makeText(ShareActivity.this, "授權失敗", Toast.LENGTH_SHORT).show();
}
}
@Override public void onCancel() { Toast.makeText(ShareActivity.this, "取消授權", Toast.LENGTH_SHORT).show(); }
@Override public void onWeiboException(WeiboException e) { Toast.makeText(ShareActivity.this, "錯誤: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }
}
/** 發送多媒體分享 (文字 + 網頁卡片 + 縮圖) */
private void sendMultiMessage() {
WeiboMultiMessage msg = new WeiboMultiMessage();
msg.textObject = getTextObj();
msg.mediaObject = getWebpageObj();
SendMultiMessageToWeiboRequest req = new SendMultiMessageToWeiboRequest();
req.transaction = String.valueOf(System.currentTimeMillis());
req.multiMessage = msg;
shareAPI.sendRequest(req); // 唤起微博分享介面
}
private TextObject getTextObj() {
TextObject obj = new TextObject();
obj.text = getString(R.string.share_content) + " #我的App#";
return obj;
}
private WebpageObject getWebpageObj() {
WebpageObject obj = new WebpageObject();
obj.actionUrl = getString(R.string.share_url); // 落地頁
obj.identify = Utility.generateGUID();
obj.title = getString(R.string.share_title);
obj.description = getString(R.string.share_content);
// 縮圖:建議 < 32KB,尺寸 120x120 以上
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
obj.setThumbImage(bmp);
return obj;
}
/** === 必須重寫的生命週期回調 === */
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
shareAPI.handleWeiboResponse(intent, this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (ssoHandler != null) ssoHandler.authorizeCallBack(requestCode, resultCode, data);
}
/** === 分享結果回調 === */
@Override
public void onResponse(BaseResponse baseResp) {
String tip;
switch (baseResp.errCode) {
case WBConstants.ErrorCode.ERR_OK: tip = "分享成功"; break;
case WBConstants.ErrorCode.ERR_CANCEL: tip = "取消分享"; break;
default: tip = "分享失敗: " + baseResp.errMsg; break;
}
Toast.makeText(this, tip, Toast.LENGTH_LONG).show();
}
}
| 問題 | 原因 | 解法 |
|---|---|---|
| SSO 授權跳轉網頁而非客戶端 | 簽名雜湊未在開放平台配置、或包名不符 | 用 PackageManager.getPackageInfo(..., GET_SIGNATURES) 列印 SHA1 填入後台 |
| 分享後回調不觸發 onResponse | 缺少 wb_sdk_Activity 或 onNewIntent 未轉交 | 嚴格依照 Manifest 範本建立回調 Activity,並在主 Activity 重寫 onNewIntent |
| 縮圖不顯示/壓扁 | 圖片過大、格式不支援、未壓縮 | 壓縮至 32KB 以下、正方形、使用 Bitmap.compress() |
| Token 過期未自動刷新 | SDK 無自動刷新機制 | 檢測 isSessionValid() 失敗時重新 ssoHandler.authorize() |
| Android 11+ 封裝可見性 | 需在 Manifest <queries> 聲明微博包名 | <queries><package android:name="com.sina.weibo" /></queries> |
微博分享核心流程:初始化 SDK → 環境檢查 → Token 有效性判斷 → SSO 授權 (含回調處理) → 構建 MultiMessage → sendRequest → 處理 onResponse。做好 簽名配置、回調 Activity、Token 持久化、錯誤碼對應 四大基石,即可穩定上線。建議封裝為 WeiboShareManager 單例,供全專案複用。