微博授权V2.5,目前最新V3.0
Android SDK地址:https://github.com/sinaweibosdk/weibo_android_sdk
在应用中使用SSO授权时,首先需要做的是实例化授权类对象:
mWeiboAuth = new WeiboAuth(this, Constants.APP_KEY,
Constants.REDIRECT_URL, Constants.SCOPE);
分别指定
APP_KEY:应用密钥
REDIRECT_URL:回调地址,创建应用时指定
SCOPE:权限接口
然后创建SSOHandler,并传入监听:
mSsoHandler = new SsoHandler(SSOAuthActivity.this, mWeiboAuth);
mSsoHandler.authorize(new AuthListener());
监听必须是实现WeiboAuthListener接口的类,这个里面主要是处理授权过程中的状态:
public abstract interface WeiboAuthListener
{
public abstract void onComplete(Bundle paramBundle);
public abstract void onWeiboException(WeiboException paramWeiboException);
public abstract void onCancel();
}
提交:保存登陆信息的bundle
异常:获取异常信息,主要是web授权时
取消:截获触发取消授权的信息
在内部的授权流程中,采用SSO授权时,首先会检测微博客户端是否安装,主要方法是采用启动微博客户端的特定service:com.sina.weibo.remotessoservice。通过绑定本地的service并与其通信。
启动方式:
private boolean bindRemoteSSOService(Context context, String packageName)
{
String tempPkgName = (TextUtils.isEmpty(packageName)) || (packageName.trim().equals("")) ?
"com.sina.weibo" : packageName;
Intent intent = new Intent("com.sina.weibo.remotessoservice");
intent.setPackage(tempPkgName);
if (!context.bindService(intent, this.mConnection, 1)) {
intent = new Intent("com.sina.weibo.remotessoservice");
return context.bindService(intent, this.mConnection, 1);
}
return true;
}
ServiceConnection对象:
private ServiceConnection mConnection = new ServiceConnection()
{
public void onServiceDisconnected(ComponentName name) {
SsoHandler.this.mWeibo.anthorize(SsoHandler.this.mAuthListener);
}
public void onServiceConnected(ComponentName name, IBinder service)
{
RemoteSSO remoteSSOservice = RemoteSSO.Stub.asInterface(service);
try {
String ssoPackageName = remoteSSOservice.getPackageName();
String ssoActivityName = remoteSSOservice.getActivityName();
boolean singleSignOnStarted = SsoHandler.this.startSingleSignOn(ssoPackageName, ssoActivityName);
if (!singleSignOnStarted)
SsoHandler.this.mWeibo.anthorize(SsoHandler.this.mAuthListener);
}
catch (RemoteException e) {
e.printStackTrace();
}
}
};
在与service链接成功时就会继续使用SSO,当失败时会自动转成web授权。
在web授权中,主要是通过打开一个自定义的Dialog:
public void anthorize(WeiboAuthListener listener)
{
authorize(listener, 1);
}
public void authorize(WeiboAuthListener listener, int type)
{
startDialog(listener, type);
}
private void startDialog(WeiboAuthListener listener, int type)
{
if (listener == null) {
return;
}
LinkedHashMap requestParams = new LinkedHashMap();
requestParams.put("client_id", this.mAuthInfo.mAppKey);
requestParams.put("redirect_uri", this.mAuthInfo.mRedirectUrl);
requestParams.put("scope", this.mAuthInfo.mScope);
requestParams.put("response_type", "code");
requestParams.put("display", "mobile");
if (1 == type) {
requestParams.put("packagename", this.mAuthInfo.mPackageName);
requestParams.put("key_hash", this.mAuthInfo.mKeyHash);
}
String url = "https://open.weibo.cn/oauth2/authorize?" + Utility.packUrl(requestParams);
if (!NetworkHelper.hasInternetPermission(this.mContext)) {
UIUtils.showAlert(this.mContext, "Error", "Application requires permission to access the Internet");
}
else if (NetworkHelper.isNetworkAvailable(this.mContext)) {
new WeiboDialog(this.mContext, url, listener, this).show();
} else {
String networkNotAvailable = ResourceManager.getString(this.mContext, 2);
LogUtil.i("Weibo_web_login", "String: " + networkNotAvailable);
UIUtils.showToast(this.mContext, networkNotAvailable, 0);
}
}
在弹出对话框之前,进行网络判断,
应用是否授予网络权限
网络可用,弹出对话框
网络不可用的提示
接下来就要看WeiboDialog类,它继承自Dialog。先看构造方法:
public WeiboDialog(Context context, String authUrl, WeiboAuthListener listener, WeiboAuth weibo)
{
super(context, theme);
this.mAuthUrl = authUrl;//授权地址
this.mListener = listener;//监听
this.mContext = context;//上下文
this.mWeibo = weibo;//WeiboAuth对象
}
主要是一些实例化操作。
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initWindow();
initLoadingDlg();
initWebView();
initCloseButton();
}
在oncreate中,是进行界面的初始化,主要的地方是在webview。
webview中有这样一句:
this.mWebView.setWebViewClient(new WeiboWebViewClient(null));
来进行事件处理。
private class WeiboWebViewClient extends WebViewClient
{
private boolean isCallBacked = false;
private WeiboWebViewClient() {
}
public boolean shouldOverrideUrlLoading(WebView view, String url) { LogUtil.i("WeiboDialog", "load URL: " + url);
if (url.startsWith("sms:")) {
Intent sendIntent = new Intent("android.intent.action.VIEW");
sendIntent.putExtra("address", url.replace("sms:", ""));
sendIntent.setType("vnd.android-dir/mms-sms");
WeiboDialog.this.getContext().startActivity(sendIntent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
{
LogUtil.d("WeiboDialog", "onReceivedError: errorCode = " + errorCode +
", description = " + description +
", failingUrl = " + failingUrl);
super.onReceivedError(view, errorCode, description, failingUrl);
if (WeiboDialog.this.mListener != null) {
WeiboDialog.this.mListener.onWeiboException(new WeiboDialogException(description, errorCode, failingUrl));
}
WeiboDialog.this.dismiss();
}
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
LogUtil.d("WeiboDialog", "onPageStarted URL: " + url);
if ((url.startsWith(WeiboDialog.this.mWeibo.getAuthInfo().getRedirectUrl())) &&
(!this.isCallBacked)) {
this.isCallBacked = true;
WeiboDialog.this.handleRedirectUrl(url);
view.stopLoading();
WeiboDialog.this.dismiss();
return;
}
super.onPageStarted(view, url, favicon);
if ((!WeiboDialog.this.mIsDetached) && (WeiboDialog.this.mLoadingDlg != null) && (!WeiboDialog.this.mLoadingDlg.isShowing()))
WeiboDialog.this.mLoadingDlg.show();
}
public void onPageFinished(WebView view, String url)
{
LogUtil.d("WeiboDialog", "onPageFinished URL: " + url);
super.onPageFinished(view, url);
if ((!WeiboDialog.this.mIsDetached) && (WeiboDialog.this.mLoadingDlg != null)) {
WeiboDialog.this.mLoadingDlg.dismiss();
}
WeiboDialog.this.mWebView.setVisibility(0);
}
}
在页面开始加载时的方法中有一个判断:
if ((url.startsWith(WeiboDialog.this.mWeibo.getAuthInfo().getRedirectUrl())) &&
(!this.isCallBacked)) {
this.isCallBacked = true;
WeiboDialog.this.handleRedirectUrl(url);
view.stopLoading();
WeiboDialog.this.dismiss();
return;
}
因为最早的时候在创建应用时给定了一个回调页的地址,当要加载的页面地址开头是回调页时,就开始进行解析用户信息和授权信息,因为在这个返回的的url中包含四个参数:
access_token,remind_in,expires_in,uid。
access_token代表口令信息,remind_in和expires_in都代表过期时间,uid代表被授权用户id。
private void handleRedirectUrl(String url)
{
Bundle values = Utility.parseUrl(url);
String errorType = values.getString("error");
String errorCode = values.getString("error_code");
String errorDescription = values.getString("error_description");
if ((errorType == null) && (errorCode == null))
this.mListener.onComplete(values);
else
this.mListener.onWeiboException(
new WeiboAuthException(errorCode, errorType, errorDescription));
}
Utility中提供了解析url的方法。
至于其中的另外一个条件:
(!this.isCallBacked)
我的理解是:因为返回的url的状态码是301,意思是客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL,也就是回调页,这个时候要做的判断就是,这个页面的url开头也是回调页,所以就要用isCallbacked进行阻止,意思是已经回调过了,就不会再去解析它了,其实里面也是空的,还有一点就是,解析过一次,这个webview就会被关掉,这个变量可能是防止解析出错延时做的措施。
接下来,解析到的信息就被放进了bundle,在判断其中信息无误后调用onComplete(value)。