微博开放SDK按官方文档配置后接入所需功能,但测试反馈偶然会出现报错提示“occur exception”,多次尝试后复现,抓取报错详细信息为“please init sdk before use it. Wb.install()”,报错来源为微博开放SDK内部方法,按翻译意思是没有初始化,但明明已经进行过初始化再调用的功能,问题出在哪呢?
此文章已收入Android偶遇杂症合集(持续更新)
1、遇到的问题
前段时间做了个集成微博开放SDK的需求,接入以后把功能跑通,结果测试阶段发现偶尔会出现报错无法正常唤起微博的情况。
多次排查后发现,第一次唤起微博时很容易出错,报错信息是“occur exception”,详细报错信息是“please init sdk before use it. Wb.install()”,按翻译意思是没有初始化。
检查了代码后发现,在唤起微博之前确实已经进行了初始化操作,但依然很容易遇到这个问题。
2、原因分析
急着改bug的小伙伴可以直接看(3、解决方式)
由于不是必现,并且只出现在首次调用,初步判断初始化逻辑应该是异步的,话不多说,看微博开放SDK的源码到底做了啥。
首先我们对SDK进行了初始化操作:
// 1. 调用AuthInfo构造方法,实例化一个authInfo
val authInfo = AuthInfo(activity, mAppId, mRedirectUrl, SCOPE)
// 2. 通过WBAPIFactory实例化一个wbApi
val wbApi = WBAPIFactory.createWBAPI(activity)
// 3. 用获得的authInfo和wbApi对象注册APP
wbApi.registerApp(activity, authInfo)
看上去第一步和第二步只是实例化对象,应该内部没有什么操作,看看源码确认一下。
第一步实例化AuthInfo,看看AuthInfo的构造函数:
public final class AuthInfo implements Parcelable, Serializable {
// ...
public AuthInfo(Context var1, String var2, String var3, String var4) {
// 都是赋值操作,没有异步逻辑
this.app_key = var2;
this.redirect_url = var3;
this.scope = var4;
this.package_name = var1.getPackageName();
this.hash = e.b(var1, this.package_name);
}
//...
}
第二步通过WBAPIFactory实例化一个wbApi,看看WBAPIFactory源码:
public class WBAPIFactory {
public WBAPIFactory() {
}
public static IWBAPI createWBAPI(Context var0) {
// 直接构造了一个对象
return new a(var0);
}
}
看看WBAPIFactory构造的a对象的构造方法:
public final class a implements IWBAPI {
// ...
public a(Context var1) {
// 也都是赋值操作,没有异步逻辑
this.mContext = var1;
this.s = new com.sina.weibo.sdk.auth.a((Activity)this.mContext);
this.t = new e((Activity)this.mContext);
}
// ...
}
粗略看来好像没啥特殊的,没有异步的逻辑在其中,那问题可能出在第三步registerApp上了,看看registerApp都做了啥:
public interface IWBAPI {
// ...
void registerApp(Context var1, AuthInfo var2);
// ...
}
是通过接口调用的,看看实现类:
public final class a implements IWBAPI {
// ...
public final void registerApp(Context var1, AuthInfo var2) {
com.sina.weibo.sdk.a.a(var1, var2);
}
// ...
}
不做停留,跟进去看看:
public final class a {
// ...
public static void a(Context var0, AuthInfo var1) {
if (!a) {
if (var1 == null) {
throw new RuntimeException("authInfo must not be null.");
}
b = var1;
String var4 = var1.getAppKey();
WeiboSsoSdkConfig var2;
(var2 = new WeiboSsoSdkConfig()).setContext(var0.getApplicationContext());
var2.setAppKey(var4);
var2.setFrom("1478195010");
var2.setWm("1000_0001");
WeiboSsoSdk.initConfig(var2);
try {
// 抓到你了!一个异步操作
WeiboSsoSdk.getInstance().visitorLogin(new VisitorLoginListener() {
public final void handler(VisitorLoginInfo var1) {
try {
if (var1 != null) {
com.sina.weibo.sdk.a.mAid = var1.getAid();
com.sina.weibo.sdk.a.c();
}
} catch (Exception var2) {
var2.printStackTrace();
}
}
});
return;
} catch (Exception var3) {
var3.printStackTrace();
}
}
}
// ...
}
果然,在初始化逻辑的第三步registerApp中有一个异步操作,WeiboSsoSdk.getInstance().visitorLogin()
,看上去是有一个SDK通过APPKey等参数进行登录的逻辑,应该是鉴权校验用的。
翻了一下官方文档,并没有提供一个初始化完成的监听回调,WeiboSsoSdk.getInstance().visitorLogin()
完成后也进入了一堆混淆过后的代码逻辑中,没有暴露给外界的方法可以用来监听状态。
换个思路,我们看看报错是从哪里来的,进入断点跟着代码的执行看看唤起微博时都发生了什么,就从微博授权登录开始看吧。首先我们知道入口是IWBAPI.authorize()
,进入实现类com.sina.weibo.sdk.openapi.a
的authorize()
方法打个断点:
public final class a implements IWBAPI {
// ...
public final void authorize(WbAuthListener var1) {
WbAuthListener var2 = var1;
com.sina.weibo.sdk.auth.a var3 = this.s;
c.a("WBSsoTag", "authorize()");
if (var2 == null) {
throw new RuntimeException("listener can not be null.");
} else {
var3.e = var2;
Context var4;
if (com.sina.weibo.sdk.a.a(var4 = (Context)var3.d.get()) && com.sina.weibo.sdk.b.a.e(var4) != null) {
// 就是这一行执行完就报错了,跟进去看看
var3.d();
} else {
var3.e();
}
}
}
// ...
}
var3.d()
里藏着那只虫子,就快要抓住它了:
package com.sina.weibo.sdk.auth;
public final class a {
// ...
public final void d() {
c.a("WBSsoTag", "startClientAuth()");
try {
Context var1;
com.sina.weibo.sdk.b.a.a var2 = com.sina.weibo.sdk.b.a.e(var1 = (Context)this.d.get());
Intent var3 = new Intent();
if (var2 == null) {
var3.setClassName("com.sina.weibo", "com.sina.weibo.SSOActivity");
} else {
var3.setClassName(var2.packageName, var2.ag);
}
// 报错的是这一行代码,之后就被catch到了错误,并提示"occur exception"
AuthInfo var6 = com.sina.weibo.sdk.a.b();
var3.putExtra("appKey", var6.getAppKey());
var3.putExtra("redirectUri", var6.getRedirectUrl());
var3.putExtra("scope", var6.getScope());
var3.putExtra("packagename", var6.getPackageName());
var3.putExtra("key_hash", var6.getHash());
var3.putExtra("_weibo_command_type", 3);
var3.putExtra("_weibo_transaction", "" + System.currentTimeMillis());
Activity var4;
if ((var4 = (Activity)this.d.get()) == null) {
this.e.onError(new UiError(-1, "activity is null", ""));
} else if (com.sina.weibo.sdk.b.a.a(var1, var3)) {
var6.getAppKey();
var3.putExtra("aid", com.sina.weibo.sdk.b.e.s());
var4.startActivityForResult(var3, 32973);
c.a("WBSsoTag", "start SsoActivity ");
} else {
this.e.onError(new UiError(-2, "your app is illegal", ""));
}
} catch (Exception var5) {
var5.printStackTrace();
c.b("WBSsoTag", var5.getMessage());
this.e.onError(new UiError(-3, "occur exception", var5.getMessage()));
}
}
// ...
}
看到了报错信息的产生位置了,com.sina.weibo.sdk.a.b()
做了什么,为什么会有报错产生:
package com.sina.weibo.sdk;
public final class a {
private static boolean a = false;
private static AuthInfo b;
private static String mAid;
public static void a(Context var0, AuthInfo var1) {
if (!a) {
if (var1 == null) {
throw new RuntimeException("authInfo must not be null.");
}
b = var1;
String var4 = var1.getAppKey();
WeiboSsoSdkConfig var2;
(var2 = new WeiboSsoSdkConfig()).setContext(var0.getApplicationContext());
var2.setAppKey(var4);
var2.setFrom("1478195010");
var2.setWm("1000_0001");
WeiboSsoSdk.initConfig(var2);
try {
WeiboSsoSdk.getInstance().visitorLogin(new VisitorLoginListener() {
public final void handler(VisitorLoginInfo var1) {
try {
if (var1 != null) {
com.sina.weibo.sdk.a.mAid = var1.getAid();
com.sina.weibo.sdk.a.c();
}
} catch (Exception var2) {
var2.printStackTrace();
}
}
});
return;
} catch (Exception var3) {
var3.printStackTrace();
}
}
}
private static void a() {
if (!a) {
throw new RuntimeException("please init sdk before use it. Wb.install()");
}
}
public static AuthInfo b() {
a();
return b;
}
// ...
}
原来又是它,第三步registerApp中异步操作的执行类,WeiboSsoSdk.getInstance().visitorLogin()
方法的调用者。变量a的值不符合预期,所以报错了RuntimeException("please init sdk before use it. Wb.install()")
,再想看看变量a的值什么时候再赋值为true
就找不到地方了,线索就到这断了?!!
3、解决方式
别急,车到山前必有路,线索虽然断了,但是解决的办法已经出现在你眼前了。注意一下最后跟踪到报错的位置:
package com.sina.weibo.sdk;
public final class a {
private static boolean a = false;
// ...
private static void a() {
if (!a) {
throw new RuntimeException("please init sdk before use it. Wb.install()");
}
}
public static AuthInfo b() {
a();
return b;
}
// ...
}
RuntimeException("please init sdk before use it. Wb.install()")
这个错误是com.sina.weibo.sdk.a.a()
抛出的,上一个调用它的方法是com.sina.weibo.sdk.a.b()
,仔细看看,com.sina.weibo.sdk.a
类是public修饰的,虽然com.sina.weibo.sdk.a.a()
方法是private修饰的,但是com.sina.weibo.sdk.a.b()
方法是public修饰的,所以我们可以手动调用com.sina.weibo.sdk.a.b()
方法来判断是否已完成微博开放平台的初始化!
修改一下原有的初始化逻辑,增加一个初始化是否完成的判断:
val authInfo = AuthInfo(activity, mAppId, mRedirectUrl, SCOPE)
val wbApi = WBAPIFactory.createWBAPI(activity)
wbApi.registerApp(activity, authInfo)
while (true) (
try {
// 如果没有抛出异常,断言通过,说明初始化已完成,循环结束
a.b()
break
} catch (e: Exception) {
e.printStackTrace()
}
)
到此问题就解决了,在初始化完成之后再执行后续的操作,再没有出现“occur exception”的报错提示了。如果怕出现死循环,可以对循环加一个超时机制。
完毕
今天的分享就到这里,文章多有不足,各位小伙伴有什么想法可以直接评论或是私信,要是对你有所帮助就给我一个赞吧,喜欢我的小伙伴可以关注我哦~
此文章已收入Android偶遇杂症合集(持续更新)
支持我的小伙伴们可以微信搜索“Android思维库”,或者微信扫描下方二维码,关注我的公众号,每天都会推送新知识~