Question&Answer
1.QQ移动应用和Web应用openid不一样,可使用unionid,参考如下链接解决
https://blog.csdn.net/liuhuanchao/article/details/50527896
2.Android QQ第三方登录时有QQ客户端加载QQ客户端,没有则拉起网页登陆的解决办法
https://blog.csdn.net/yinzhong39/article/details/51774462
3.QQ拉起客户端提示非正式应用啥的
QQ互联的移动应用中的Android部分没有填写签名包名等数据,或者数据不正确导致的
4.QQ互联官方资料,基本按照官方教程就可实现基本功能
http://wiki.connect.qq.com/qq%E7%99%BB%E5%BD%95
代码
码云:https://gitee.com/zhangtao3/android_thirdlogin
准备
QQ互联申请移动应用,未通过审核也可进行登录开发,包名和签名有条件直接填写正确的,没有,后续也可添加
QQSDK没有找到直接的下载地址,只能通过下载demo,来拿到jar包
开始
1.sdk open_sdk_r5990_lite.jar导入
2.AndroidManifest.xml处理
// 网络权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- QQ登录相关 start -->
<activity
android:name="com.tencent.tauth.AuthActivity"
android:noHistory="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="tencent1106910756" />
</intent-filter>
</activity>
<activity
android:name="com.tencent.connect.common.AssistActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="behind"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<!-- QQ登录相关 end -->
3.创建GlobalData,存放基础数据
/** QQ相关 */
public static final String QQ_APP_ID = "XXX";
public static final String QQ_GET_USER_INFO = "https://graph.qq.com/user/get_user_info";
4.创建MyApplication全局类,并在AndroidManifest.xml添加android:name=”.MyApplication”,在这里初始化Tencent类,后续也会在这里初始化WeiXinApi
// 初始化QQ
private void initQQ() {
// Tencent类是SDK的主要实现类,开发者可通过Tencent类访问腾讯开放的OpenAPI。
mTencent = Tencent.createInstance(GlobalData.QQ_APP_ID, mAppContext);
// QQ客户端和网页端登录处理,该方法就是用来处理吊起QQ客户端或者Web页登录,具体代码可参照以上网址,或者源码
fixWebAuth(this, mTencent);
}
5.参照官方教程,实现回调IUiListener,在onActivityResult方法中增加mTencent.onActivityResult(requestCode, resultCode, data);
public class BaseUiListener implements IUiListener {
@Override
public void onComplete(Object response) {
Log.d(GlobalData.TAG, "qq_BaseUiListener_onComplete: " + response.toString());
try {
doComplete((JSONObject) response);
} catch (JSONException e) {
e.printStackTrace();
}
}
protected void doComplete(JSONObject values) throws JSONException {
Log.e(GlobalData.TAG, "qq_BaseUiListener_doComplete: " + values);
}
@Override
public void onError(UiError e) {
Log.e(GlobalData.TAG, "qq_BaseUiListener_onCancel: " + e.errorDetail);
}
@Override
public void onCancel() {
Log.e(GlobalData.TAG, "qq_BaseUiListener_onCancel: ");
}
}
//MainActivity
// QQ登录回调
private void initQQLoginListener() {
qqLoginListener = new BaseUiListener() {
@Override
protected void doComplete(JSONObject values) throws JSONException {
//{"auth_time":"1527600141911","ret":"0","pay_token":"","pf":"desktop_m_qq-10000144-android-2002-","page_type":"1",
// "expires_time":1535376201953,"openid":"","expires_in":"7776000","pfkey":"8b372551e05c1b82a131de8d16cb691f",
// "access_token":"DC92D06A6458A3CE2D54C343C61F9100"}
if (values.getInt("ret") == 0) {
MyApplication.mTencent.setOpenId(values.getString("openid"));
MyApplication.mTencent.setAccessToken(values.getString("access_token"), values.getString("expires_in"));
// 保存QQToken
saveQQToken(values);
// 如需获取unionid,可getQQUnionId(values)
// 获取用户信息
getQQUserInfo();
}else{
logoutQQ();
loginQQ();
}
}
@Override
public void onCancel() {
ToastUtils.showShortToast("取消QQ登录");
tv_result.setText("取消QQ授权");
}
};
}
6.调用QQ登录接口,获取AccesToken,openid,unionid
// QQ登录
private void loginQQ() {
if (!checkQQTokenValid()) {
tv_result.setText("QQ登录授权...");
MyApplication.mTencent.login(MainActivity.this, "", qqLoginListener);
} else {
SharedPreferences sp = getSharedPreferences("thirdlogin", Context.MODE_PRIVATE);
MyApplication.mTencent.setOpenId(sp.getString("qq_openid", ""));
MyApplication.mTencent.setAccessToken(sp.getString("qq_access_token", ""), sp.getString("qq_expires_in", ""));
getQQUserInfo();
}
}
7.校验QQToken是否有效
// 校验QQToken是否有效
private boolean checkQQTokenValid() {
SharedPreferences sp = getSharedPreferences("thirdlogin", Context.MODE_PRIVATE);
String expires_time = sp.getString("qq_expires_time", "0");
long var7 = Long.parseLong(expires_time);
long var9 = System.currentTimeMillis();
if (var9 < var7) {
String access_token = sp.getString("qq_access_token", null);
String expires_in = sp.getString("qq_expires_in", null);
String openid = sp.getString("qq_openid", null);
MyApplication.mTencent.setOpenId(openid);
MyApplication.mTencent.setAccessToken(access_token, expires_in);
return true;
} else {
return false;
}
}
8.存储QQToken
// 存储QQToken
private void saveQQToken(JSONObject data) throws JSONException {
SharedPreferences sp = getSharedPreferences("thirdlogin", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("qq_expires_time", data.getString("expires_time"));
editor.putString("qq_access_token", data.getString("access_token"));
editor.putString("qq_expires_in", data.getString("expires_in"));
editor.putString("qq_openid", data.getString("openid"));
editor.commit();
}
9.QQ注销登录
// QQ注销登录
private void logoutQQ() {
MyApplication.mTencent.logout(MyApplication.getAppContext());
tv_result.setText("QQ已注销");
SharedPreferences sp = getSharedPreferences("thirdlogin", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove("qq_expires_time");
editor.commit();
}
10.获取QQ用户信息
// 获取QQ用户信息
private void getQQUserInfo() {
Map<String, String> params = new HashMap<>();
params.put("access_token", MyApplication.mTencent.getAccessToken());
params.put("oauth_consumer_key", GlobalData.QQ_APP_ID);
params.put("openid", MyApplication.mTencent.getOpenId());
OkHttpUtils.getInstance().getRequestAsync(GlobalData.QQ_GET_USER_INFO, params, new OkHttpUtils.MyOkHttpCall() {
@Override
public void success(Call call, Response response) throws IOException {
// response.body()只可调用一次
final String resultString = response.body().string();
Log.d(GlobalData.TAG, "http_getuserinfo: " + resultString);
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject resultObj = new JSONObject(resultString);
if (resultObj.getInt("ret") == 0) {
tv_result.setText("QQ昵称:" + resultObj.getString("nickname"));
} else {
logoutQQ();
loginQQ();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}
11.获取QQ unionid
// 获取QQ unionid
private void getQQUnionId(final JSONObject values) {
// URL处理
String url = "https://graph.qq.com/oauth2.0/me";
Map<String, String> getParams = new HashMap<>();
getParams.put("access_token", mTencent.getAccessToken());
getParams.put("unionid", "1");
// Get请求
OkHttpUtils.getInstance().getRequestAsync(url, getParams, new OkHttpUtils.MyOkHttpCall() {
@Override
public void success(Call call, Response response) throws IOException {
final String resultString = response.body().string();
Log.d(GlobalData.TAG, "getQQUnionId_onResponse: " + resultString);
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO 获取unionid方法有待改善
String[] split = resultString.split(":");
String s = split[split.length - 1];
split = s.split("\"");
String unionid = split[1];
Log.d(GlobalData.TAG, "getQQUnionId_unionid: " + unionid);
try {
saveQQToken(values, unionid);
launcher.callExternalInterface("qqLogin", unionid);
} catch (JSONException e) {
e.printStackTrace();
ToastUtils.showShortToast("QQ登录数据异常");
}
}
});
}
});
}
12.OkHttp网络请求类简单封装
public class OkHttpUtils {
private static final byte[] LOCKER = new byte[0];
private static OkHttpUtils mInstance;
private OkHttpClient mOkHttpClient;
public static OkHttpUtils getInstance() {
if (mInstance == null) {
synchronized (LOCKER) {
if (mInstance == null) {
mInstance = new OkHttpUtils();
}
}
}
return mInstance;
}
private OkHttpUtils() {
OkHttpClient.Builder ClientBuilder = new OkHttpClient.Builder();
ClientBuilder.readTimeout(20, TimeUnit.SECONDS);//读取超时
ClientBuilder.connectTimeout(10, TimeUnit.SECONDS);//连接超时
ClientBuilder.writeTimeout(60, TimeUnit.SECONDS);//写入超时
//支持HTTPS请求,跳过证书验证
ClientBuilder.sslSocketFactory(createSSLSocketFactory());
ClientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
mOkHttpClient = ClientBuilder.build();
}
/**
* 自定义网络回调接口
*/
public interface MyOkHttpCall {
void success(Call call, Response response) throws IOException;
}
/**
* Get异步请求
*
* @param url
* @param getParams get请求参数,可不传,直接将参数组合到url中
* @param myOkHttpCall
*/
public void getRequestAsync(String url, Map<String, String> getParams, final MyOkHttpCall myOkHttpCall) {
//1 构造Request
Request.Builder builder = new Request.Builder();
// 处理Get参数
String finalUrl = setGetParam(url,getParams);
Request request = builder.get().url(finalUrl).build();
//2 将Request封装为Call
Call call = mOkHttpClient.newCall(request);
//3 执行Call
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 请求失败,统一处理
Message message = new Message();
message.what = GlobalData.NET_FAIL_CODE;
MyApplication.handler.sendMessage(message);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
myOkHttpCall.success(call, response);
}
});
}
/**
* Get请求拼接参数
*
* @param url
* @param getParams
*/
private String setGetParam(String url, Map<String, String> getParams) {
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
if (getParams != null) {
Iterator<String> iterator = getParams.keySet().iterator();
String key = "";
while (iterator.hasNext()) {
key = iterator.next().toString();
urlBuilder.addQueryParameter(key, getParams.get(key));
}
}
Log.d(GlobalData.TAG, "http_get_url:" + urlBuilder.build().toString());
return urlBuilder.build().toString();
}
/**
* Post异步请求
*
* @param url
* @param bodyParams
* @param myOkHttpCall
*/
public void postDataAsynToNet(String url, Map<String, String> bodyParams, final MyOkHttpCall myOkHttpCall) {
Log.d(GlobalData.TAG, "http_post_url:" + url);
//1构造RequestBody
RequestBody body = setRequestBody(bodyParams);
//2 构造Request
Request.Builder requestBuilder = new Request.Builder();
Request request = requestBuilder.post(body).url(url).build();
//3 将Request封装为Call
Call call = mOkHttpClient.newCall(request);
//4 执行Call
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 请求失败,统一处理
Message message = new Message();
message.what = GlobalData.NET_FAIL_CODE;
MyApplication.handler.sendMessage(message);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
myOkHttpCall.success(call, response);
}
});
}
/**
* post的请求参数,构造RequestBody
*
* @param BodyParams
* @return
*/
private RequestBody setRequestBody(Map<String, String> BodyParams) {
RequestBody body = null;
okhttp3.FormBody.Builder formEncodingBuilder = new okhttp3.FormBody.Builder();
if (BodyParams != null) {
Iterator<String> iterator = BodyParams.keySet().iterator();
String key = "";
while (iterator.hasNext()) {
key = iterator.next().toString();
formEncodingBuilder.add(key, BodyParams.get(key));
Log.d(GlobalData.TAG, "http_post_key:" + key + ",post_value:" + BodyParams.get(key));
}
}
body = formEncodingBuilder.build();
return body;
}
/**
* 生成安全套接字工厂,用于https请求的证书跳过
*
* @return
*/
public SSLSocketFactory createSSLSocketFactory() {
SSLSocketFactory ssfFactory = null;
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
ssfFactory = sc.getSocketFactory();
} catch (Exception e) {
}
return ssfFactory;
}
/**
* 用于信任所有证书
*/
class TrustAllCerts implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
结束
QQ登录比较简单,参照文档基本就可以了