目录
问题起源
这几天翻看了项目中以前添加的新浪分享功能,发现有个问题是虽然添加了WbShareCallback,但是并没有真正的实现回调,而只是放了代码而已,所以就想着把新浪分享之后的回调给加进去。结果就开始各种踩坑。
第一次尝试
由于项目中并不是一个页面在调用新浪分享的功能,所以在每个Activity中添加
<intent-filter>
<action android:name="com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
必然无法解决问题。首先多个Activity注册该action,肯定在进行新浪分享结果处理完回调之后,会出现多个icon供选择,所以就想到了是不是可以将回调的逻辑写到一个空的Activity中,这样子就可以回调到该Activity,所以就有了如下代码:
public class SinaEntryActivity extends Activity implements WbShareCallback {
//......省略代码
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (wbShareHandler == null) {
finish();
return;
}
wbShareHandler.doResultIntent(intent, this);
}
@Override
public void onWbShareSuccess() {
}
@Override
public void onWbShareCancel() {
}
@Override
public void onWbShareFail() {
}
}
这样子的确是可以回调成功,但是就是遇到了分享成功、取消或者失败之后,回调到该SinaEntryActivity的时候,就会从底部弹出一个对话框,有两个icon的入口,提示点击进入?
为什么会出现这种问题呢?我搜索了全局也只有这一个Activity注册了该Action,理论上应该只有这一个Activity,是时候去查看下源码了。
源码分析
1.实例化WbShareHandler
WbSdk.install(getApplicationContext(), new AuthInfo(getApplicationContext(), appkey, redirectUrl, ""));
wbShareHandler = new WbShareHandler(activity);
wbShareHandler.registerApp();
不做过多解释
2.发送消息
wbShareHandler.shareMessage(weiboMultiMessage, false);
主要就是将要分享的信息发送给微博的API。主要进入源码看下
public void shareMessage(WeiboMultiMessage message, boolean clientOnly) {
if (!this.hasRegister) {
throw new RuntimeException("please call WbShareHandler.registerApp(),before use share function");
} else if (WbSdk.isWbInstall(this.context) || !clientOnly) {
if (!clientOnly && !WbSdk.isWbInstall(this.context)) {
this.startWebShare(message);
} else {
this.startClientShare(message);
}
}
}
进入到startClientShare()
private void startClientShare(WeiboMultiMessage message) {
//...省略代码
Intent intent = new Intent();
intent.setClass(this.context, WbShareTransActivity.class);
intent.putExtra("startPackage", WeiboAppManager.getInstance(this.context).getWbAppInfo().getPackageName());
intent.putExtra("startAction", "com.sina.weibo.sdk.action.ACTION_WEIBO_ACTIVITY");
intent.putExtra("startFlag", 0);
intent.putExtra("startActivity", this.context.getClass().getName());
intent.putExtra("progressColor", this.progressColor);
intent.putExtra("progressId", this.progressId);
if (data != null) {
intent.putExtras(data);
}
try {
this.context.startActivity(intent);
} catch (Exception var5) {
LogUtil.v("weibo sdk error ", var5.toString());
}
}
从源码中可以看出主要就是封装了Bundle,去启动了WbShareTransActivity这个Activity。主要看下里面几个主要的关键字
"startPackage" | 将内置SDK的包名传入到WbShareTransActivity |
"startAction" | "com.sina.weibo.sdk.action.ACTION_WEIBO_ACTIVITY"传入到WbShareTransActivity |
"startActivity" | 就是将调用该发送分享消息的Activity也传入了WbShareTransActivity |
主要看下上述几个关键字怎么在WbShareTransActivity中发挥作用的。
3.处理发送到微博SDK上的消息
主要进入到WbShareTransActivity查看下源码:
- 1)onCreate()
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.initView();
this.startActivityName = this.getIntent().getStringExtra("startActivity");
if (savedInstanceState != null) {
this.startActivityName = savedInstanceState.getString("startActivity");
this.flag = savedInstanceState.getBoolean("resultDataFlag", false);
} else {
this.checkSource();
}
}
主要就是从Bundle里面获取"startActivity"对应的类名,在本实例中就是定义的SinaEntryActivity。
- 2)initView()
主要根据Bundle中设置的进度条的颜色和id,即开发者即可以设置SDK中的进度条的样式,也可以调用自己定义的进度条。
private void initView() {
this.progressColor = this.getIntent().getIntExtra("progressColor", -1);
this.progressId = this.getIntent().getIntExtra("progressId", -1);
this.rootLayout = new FrameLayout(this);
if (this.progressId != -1) {
LayoutInflater inflater = (LayoutInflater)this.getSystemService("layout_inflater");
try {
this.progressBar = inflater.inflate(this.progressId, (ViewGroup)null);
} catch (Exception var3) {
this.progressBar = new WbSdkProgressBar(this);
}
} else {
this.progressBar = new WbSdkProgressBar(this);
if (this.progressColor != -1) {
((WbSdkProgressBar)this.progressBar).setProgressColor(this.progressColor);
}
}
LayoutParams params = new LayoutParams(-2, -2);
params.gravity = 17;
this.rootLayout.addView(this.progressBar, params);
this.rootLayout.setBackgroundColor(855638016);
}
返回到WbShareHandler中可以看到,提供下面两个方法来设置对应的参数
public void setProgressColor(int color) {
this.progressColor = color;
}
public void setProgressId(int id) {
this.progressId = id;
}
- 3)进行SDK调用分享
刚进来的时候savedInstanceState的时候为null,所以进入到else的流程中,即调用到checkSource()
private void checkSource() {
Bundle bundle = this.getIntent().getExtras();
WeiboMultiMessage multiMessage = new WeiboMultiMessage();
multiMessage.toObject(bundle);
this.transPicAndVideoResource(multiMessage);
}
跟进到transPicAndVideoResource()中可以看到,跟进分享的信息是如果是多张图片或者video的话,就走else,如果不是这两者的就直接调用到this.gotoWeiboComposer(multiMessage)。
private void transPicAndVideoResource(WeiboMultiMessage multiMessage) {
this.setContentView(this.rootLayout);
if (multiMessage.multiImageObject == null && multiMessage.videoSourceObject == null) {
this.gotoWeiboComposer(multiMessage);
} else {
this.setContentView(this.rootLayout);
if (this.copyResourceTask != null) {
this.copyResourceTask.cancel(true);
}
this.copyResourceTask = new WbShareTransActivity.CopyResourceTask(null);
WeiboMultiMessage[] messages = new WeiboMultiMessage[]{multiMessage};
this.copyResourceTask.execute(messages);
}
}
(a)分享非多张图片或者video
从下面的源码中可以看到直接调用gotoWeiboComposer(),封装Intent来打开不同的Activity。
private void gotoWeiboComposer(WeiboMultiMessage weiboMultiMessage) {
Intent intent = this.getIntent();
this.flag = true;
intent.putExtra("startFlag", -1);
Intent shareIntent = new Intent("com.sina.weibo.sdk.action.ACTION_WEIBO_ACTIVITY");
shareIntent.setPackage(intent.getStringExtra("startPackage"));
//....省略封装的Intent对应的字段
try {
//如果gotoActivity不为空的话,就进入到对应的Activity下
if (!TextUtils.isEmpty(intent.getStringExtra("gotoActivity"))) {
shareIntent.setClassName(this, intent.getStringExtra("gotoActivity"));
this.startActivity(shareIntent);
//如果安装了微博的客户端,则进入到对应action为com.sina.weibo.sdk.action.ACTION_WEIBO_ACTIVITY的微博页面中
} else if (WbSdk.isWbInstall(this)) {
this.startActivityForResult(shareIntent, 765);
} else {
//否则直接回调
this.sendCallback(2);
}
} catch (Exception var10) {
//....省略代码
}
}
(1)如果intent里的"gotoActivity"对应的类名不为空,则进入到对应的类名下面,而该关键字对应的是在WebShareHanderl中如果没有安装新浪的客户端的时候,直接调用到shareWebShare()时进行封装的,源码如下:
private void startWebShare(WeiboMultiMessage message) {
Intent webIntent = new Intent(this.context, WbShareTransActivity.class);
//。。。。省略代码
webIntent.putExtra("gotoActivity", "com.sina.weibo.sdk.web.WeiboSdkWebActivity");
this.context.startActivity(webIntent);
}
可以看出对应的为WeiboSdkWebActivity,其实该类已经封装到jar包中,通过Webview加载了Sina的H5页面。
(2)如果安装了微博的客户端,则直接进入到微博的对应该action的com.sina.weibo.sdk.action.ACTION_WEIBO_ACTIVITY的Activity中,该Activity处理完逻辑之后就回调到WbShareTransActivity的onActivityResult()中。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (this.handler != null) {
this.handler.sendEmptyMessageDelayed(0, 100L);
}
}
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
WbShareTransActivity.this.sendCallback(1);
}
};
最终调用sendCallback(1);
private void sendCallback(int resultCode) {
if (this.rootLayout != null) {
this.rootLayout.setVisibility(8);
}
try {
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putInt("_weibo_resp_errcode", resultCode);
resultIntent.putExtras(bundle);
resultIntent.setFlags(131072);
resultIntent.setClassName(this, this.startActivityName);
this.startActivity(resultIntent);
} catch (Exception var4) {
;
}
this.finish();
}
从源码中可以看出,其实就是重新start之前调用该API的那个Activity,即 this.startActivityName,也就是之前在弹出那个对话框中之所有有一个icon的原因所在。
注意:这里有一点,这里在启动startActivityName这个Activity的时候给Intent设置了一个Flag,我觉得这个Flag应该对应的Activity的启动模式就是类似于SingleTask,否则会影响到startActivityName的,不会重新创建startActivityName这个Acitivity实例,而是直接从onNewIntent(),也就是为什么需要在onNewIntent()中需要进行调用wbShareHandler.doResultIntent(intent, this);
(b)分享多张图片或者Video
运行CopyResourceTask这个异步线程,会在子线程中对图片或者video进行处理,然后在发送给微博处理分享的SDK,同样处理完之后也会走到gotoWeiboComposer(),同(a)。
4.另外一个icon
- 1)安装新浪客户端
应该是跳转到的微信客户端的对应该action的com.sina.weibo.sdk.action.ACTION_WEIBO_ACTIVITY的Activity中,在关闭新浪客户单的Activity的时候,发出的该com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY
- 2)没有安装新浪客户端
由于跳转到的是该jar包对应的WeiboSdkWebActivity这个Activity中,在点击左上角的关闭按钮
this.leftBtn.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
WeiboSdkWebActivity.this.webViewClient.closeWeb();
WeiboSdkWebActivity.this.closeActivity();
}
});
或者完成分享操作之后,调用到webviewClient中对应的方法。进入到 ShareWebViewClient中可以看到
(a)完成分享操作
主要就是WebView的监听事件来完成对应的分享成功、失败和取消的事件
@TargetApi(24)
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return this.needOverLoad(request.getUrl().toString());
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (this.requestCallback != null) {
this.requestCallback.shouldOverrideUrlLoadingCallBack(view, url);
}
return this.needOverLoad(url);
}
最终调用到needOverLoad(String url),进入源码中可以看到会根据不同的errorCode来调用不同的代码来实现发送com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY
private boolean needOverLoad(String url) {
if (url.startsWith("sinaweibo://browser/close")) {
Bundle bundle = WbUtils.parseUri(url);
String errCode;
if (this.param.getBaseData() != null && !TextUtils.isEmpty(this.param.getBaseData().getCallback())) {
errCode = this.param.getBaseData().getCallback();
//。。。省略代码
errCode = bundle.getString("code");
String errMsg = bundle.getString("msg");
//向调用者发送com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY
if (TextUtils.isEmpty(errCode)) {
this.sendSdkCancleResponse(this.activity);
} else if (!"0".equals(errCode)) {
this.sendSdkErrorResponse(this.activity, errMsg);
} else {
this.sendSdkOkResponse(this.activity);
}
if (this.requestCallback != null) {
this.requestCallback.closePage();
}
return true;
} else {
return false;
}
}
最终调用到sendSdkResponse()发出com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY,然后匹配到我们的APP的注册的action
private void sendSdkResponse(Activity activity, int errCode, String errMsg) {
Bundle bundle = activity.getIntent().getExtras();
if (bundle != null && !this.hasCallbacked) {
Intent intent = new Intent("com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY");
//。。。省略代码
try {
activity.startActivityForResult(intent, 765);
} catch (ActivityNotFoundException var8) {
;
}
this.hasCallbacked = true;
}
}
同样 点击左边的关闭按钮也是对应着该sendSdkResponse()。
综上,所以当APP中注册com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY的时候会出现两个icon。
第二次尝试
显然带着两个icon这种体验非常不好,所以怎么解决这种问题呢?
还是新浪的源码启发了我,WbShareTransActivity这个Activity的处理方式真的好微妙,用一个Activity起到了承前启后的作用,所以我就想着能不能把这新浪分享的调用和回调都写到一个Activity中,不在注册com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY该action,因为微信SDK肯定会重新启动调用分享API的那个Activity,回调之后直接在该Activity写逻辑岂不是一件很完美的事情。
就有了如下的代码:
public class SinaEntryActivity extends Activity implements WbShareCallback {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
installWB();
initBundleFromIntent();
}
/**
* 从Bundle中获取分享的内容
*/
private void initBundleFromIntent() {
Bundle bundle = getIntent().getExtras();
if (bundle == null) {
return;
}
url = bundle.getString("url");
title = bundle.getString("title");
text = bundle.getString("text");
imageUrl = bundle.getString("imageUrl");
//调用分享图片的方法
shareImg2WB();
}
/**
* 初始化sina的API
*/
private void installWB() {
WbSdk.install(getApplicationContext(), new AuthInfo(getApplicationContext(), appkey, redirectUrl, ""));
wbShareHandler = new WbShareHandler(activity);
wbShareHandler.registerApp();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (wbShareHandler == null) {
finish();
return;
}
wbShareHandler.doResultIntent(intent, this);
}
@Override
public void onWbShareSuccess() {
//.......添加逻辑代码
finish();
}
@Override
public void onWbShareCancel() {
//.......添加逻辑代码
finish();
}
@Override
public void onWbShareFail() {
//.......添加逻辑代码
finish();
}
/**
* 微博分享图片。
*/
private void shareImg2WB() {
//。。。。省略代码分享的图片的代码
}
}
最后在封装的Library的AndrodiManifest.xml文件中注册该Activity即可
<activity
android:name=".SinaEntryActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
采用一个透明的Activity来发送消息和接收回调,不需要在页面的中单独处理回调。当需要调用微博分享功能的时候,只要start该Activity即可。
总结
1)新浪在处理完分享结果之后,会重新打开调用分享的那个Activity
2)新浪在处理完分享结果之后,会发出打开注册com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY这个action的Activity
3)可以使用一个透明的Activity来巧妙的处理一些逻辑,解决需要在所有Acitivity里面需要处理的逻辑。
看别人的源码,真的是一件很开心的事情,可以学到一些技巧。