一、前言
因为之前本人我的项目的账号零碎不是十分欠缺,所以思考接入 QQ 这个弱小的第三方平台的接入,目前我的项目临时应用 QQ 登录的接口进行后期的测试,这次从搭建到欠缺花了整整两天工夫,不得不吐槽一下 QQ 互联的官网文档,从界面就能够看出了,好几年没培修了,示例代码也写的不是很分明,翻了好多源代码和官网的 demo,这个 demo 能够作为辅助参考,官网文档的 api 生效了能够从外面找相应的代替,但它的代码也太多了,一个 demo 一万行代码,心累,过后把 demo 弄到能够运行就花了不少工夫,很多 api 如同是生效了,笔者本人做了一些解决和欠缺,简直把 sdk 性能列表的登录相干的 api 都尝试了一下,真的相当的坑,注释行将开始,心愿这篇文章可能给后来者一些参考和帮忙。
二、环境配置
1. 获取利用 ID
这个比较简单,间接到 QQ 互联官网申请一个即可,官网地址
https://connect.qq.com
申请利用的时候须要留神利用名字不能呈现违规词汇,否则可能申请不通过
利用信息的填写须要以后利用的包名和签名,这个腾讯这边提供了一个获取包名和签名的 app 供咱们开发者应用,下载地址
https://pub.idqqimg.com/pc/misc/files/20180928/c982037b921543bb937c1cea6e88894f.apk
未通过审核只能应用调试的 QQ 号进行登录,通过就能够面向全副用户了,以下为审核通过的图片
2. 官网下载相干的 sdk
下载地址
https://tangram-1251316161.file.myqcloud.com/qqconnect/OpenSDK_V3.5.10/opensdk_3510_lite_2022-01-11.zip
举荐间接下载最新版本的,不过着实没看懂最新版本的更新布告,说是修复了 retrofit 抵触的问题,而后过后新建的我的项目没有用,后果报错,最初还是加上了,才能够
[图片上传失败 …(image-f70a8f-1647264931314)]
3. jar 的引入
将 jar 放入 lib 包下,而后在 app 同级的 build.gradle 增加以下代码即实现 jar 的援用
dependencies {
...
implementation fileTree(dir: 'libs', include: '*.jar')
...
}
4. 配置 Manifest
在 AndroidManifest.xml 中的 application 结点下减少以下的 activity 和启动 QQ 利用的申明, 这两个 activity 无需咱们在另外创立文件,引入的 jar 曾经解决好了
<application
...
<!-- 这里的权限为开启网络拜访权限和获取网络状态的权限,必须开启,不然无奈登录 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<activity
android:name="com.tencent.tauth.AuthActivity"
android:exported="true"
android:launchMode="singleTask"
android:noHistory="true">
<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="tencent 你的 appId" />
</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" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.tencent.login.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
</application>
下面的哪个代码的最初提供了一个 provider 用于拜访 QQ 利用的,须要另外创立一个 xml 文件,其中的 authorities 是自定义的名字,确保惟一即可,这边最上面那个 provider 是翻 demo 找的,文档没有写,在 res 文件夹中新增一个包 xml,外面增加文件名为 file\_paths 的 xml,其内容如下
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="opensdk_external" path="Images/tmp"/>
<root-path name="opensdk_root" path=""/>
</paths>
三、初始化配置
1. 初始化 SDK
退出以下代码在创立登录的那个 activtiy 下,不然无奈拉起 QQ 利用的登录界面,至于官网文档所说的须要用户抉择是否受权设施的信息的阐明,这里通用的做法是在利用外部申明一个第三方 sdk 的列表,而后在外面阐明 SDK 用到的相干设施信息的权限
Tencent.setIsPermissionGranted(true, Build.MODEL)
2. 创立实例
这部分倡议放在全局配置,这样能够实现登录异样强制退出等性能
/**
* 其中 APP_ID 是申请到的 ID
* context 为全局 context
* Authorities 为之前 provider 外面配置的值
*/
val mTencent = Tencent.createInstance(APP_ID, context, Authorities)
3. 开启登录
在开启登录之前须要本人创立一个 UIListener 用来监听回调后果(文档没讲怎么创立的,找了良久的 demo)这里的代码为根底的代码,比拟容易实现,目前还没写回调相干的代码,次要是为了疾速展现成果
open class BaseUiListener(private val mTencent: Tencent) : DefaultUiListener() {private val kv = MMKV.defaultMMKV()
override fun onComplete(response: Any?) {if (response == null) {"返回为空, 登录失败".showToast()
return
}
val jsonResponse = response as JSONObject
if (jsonResponse.length() == 0) {"返回为空, 登录失败".showToast()
return
}
"登录胜利".showToast()
doComplete(response)
}
private fun doComplete(values: JSONObject?) { }
override fun onError(e: UiError) {Log.e("fund", "onError: ${e.errorDetail}")
}
override fun onCancel() {"勾销登录".showToast()
}
}
建设一个按钮用于监听,这里进行登录操作
button.setOnClickListener {if (!mTencent.isSessionValid) {
// 判断会话是否无效
when (mTencent.login(this, "all",iu)) {
// 上面为 login 可能返回的值的状况
0 -> "失常登录".showToast()
1 -> "开始登录".showToast()
-1 -> "异样".showToast()
2 -> "应用 H5 登陆或显示下载页面".showToast()
else -> "出错".showToast()}
}
}
这边对 mTencent.login(this, “all”,iu) 中 login 的参数做一下解释阐明
mTencent.login(this, "all",iu)
// 这里 Tencent 的实例 mTencent 的 login 函数的三个参数
//1. 为以后的 context,//2. 权限, 可选项,个别抉择 all 即可,即全副的权限,不过目前如同也只有一个凋谢的权限了
//3. 为 UIlistener 的实例对象
还差最初一步,获取回调的后果的代码,activity 的回调,这边显示办法曾经废除了,原本想革新一下的,前面发现要革新的话须要动 sdk 外面的源码,有点麻烦就没有改了,等更新
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)
// 腾讯 QQ 回调,这里的 iu 依然是相干的 UIlistener
Tencent.onActivityResultData(requestCode, resultCode, data,iu)
if (requestCode == Constants.REQUEST_API) {if (resultCode == Constants.REQUEST_LOGIN) {Tencent.handleResultData(data, iu)
}
}
}
至此,曾经能够失常登录了,但还有一件咱们开发者最关怀的事件没有做,获取的用户的数据在哪呢?能够获取 QQ 号吗?上面将为大家解答这方面的纳闷。
四、接入流程以及相干代码
首先答复一下下面提出的问题,能够取得两段比拟要害的 json 数据,一个是 login 的时候获取的,次要是 token 相干的数据,还有一段就是用户的个人信息的 json 数据,这些都在 UIListener 中进行解决和获取。第二个问题能不能获取 QQ 号,答案是不能,咱们只能获取与一个与 QQ 号一样具备惟一标记的 id 即 open\_id, 显然这是出于用户的隐衷平安思考的,接下来简述一下具体的登录流程
1. 登录之前查看是否有 token 缓存
- 有,间接启动主 activity
- 无,进入登录界面
判断是否具备登录数据的缓存
// 这里采纳微信的 MMKV 进行贮存键值数据
MMKV.initialize(this)
val kv = MMKV.defaultMMKV()
kv.decodeString("qq_login")?.let{val gson = Gson()
val qqLogin = gson.fromJson(it, QQLogin::class.java)
QQLoginTestApplication.mTencent.setAccessToken(qqLogin.access_token,qqLogin.expires_in.toString())
QQLoginTestApplication.mTencent.openId = qqLogin.openid
}
查看 token 和 open\_id 是否无效和 token 是否过期,这里采取不同于官网的举荐的用法,次要是 api 生效了或者是本人没用对办法,总之官网提供的 api 进行缓存还不如 MMKV 键值存 login json 来的切实,也很不便,这里倡议多多应用日志,不便排查谬误
// 这里对于 uiListener 进行了重写,object 的作用有点像 java 外面的匿名类
// 用到了 checkLogin 的办法
mTencent.checkLogin(object : DefaultUiListener() {override fun onComplete(response: Any) {
val jsonResp = response as JSONObject
if (jsonResp.optInt("ret", -1) == 0) {val jsonObject: String? = kv.decodeString("qq_login")
if (jsonObject == null) {"登录失败".showToast()
} else {// 启动主 activity}
} else {"登录已过期,请从新登录".showToast()
// 启动登录 activity
}
}
override fun onError(e: UiError) {"登录已过期,请从新登录".showToast()
// 启动登录 activity
}
override fun onCancel() {"勾销登录".showToast()
}
})
2. 进入登录界面
在判断 session 无效的状况下,进入登录界面,对 login 登录可能呈现的返回码做一下解释阐明
Login.setOnClickListener {if (!QQLoginTestApplication.mTencent.isSessionValid) {when (QQLoginTestApplication.mTencent.login(this, "all",iu)) {0 -> "失常登录".showToast()
1 -> "开始登录".showToast()
-1 -> {"异样".showToast()
QQLoginTestApplication.mTencent.logout(QQLoginTestApplication.context)
}
2 -> "应用 H5 登陆或显示下载页面".showToast()
else -> "出错".showToast()}
}
}
-
1:失常登录
这个就无需做解决了,间接在回调那里做相干的登录解决即可
-
0:开始登录
同失常登录
-
-1:异样登录
这个须要做一点解决,过后第一次遇到这个状况就是主 activity 异样耗费退回登录的 activity,此时在此点击登录界面的按钮导致了异常情况的呈现,不过这个解决起来还是比拟容易的,执行强制下线操作即可
"异样".showToast() mTencent.logout(QQLoginTestApplication.context)
-
2:应用 H5 登陆或显示下载页面
通常状况下是未装置 QQ 等软件导致的,这种状况无需解决,SDK 主动封装好了,这种状况会主动跳转 QQ 下载界面
同样的有呈现 UIListener 就须要调用回调进行数据的传输
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)
// 腾讯 QQ 回调
Tencent.onActivityResultData(requestCode, resultCode, data,iu)
if (requestCode == Constants.REQUEST_API) {if (resultCode == Constants.REQUEST_LOGIN) {Tencent.handleResultData(data, iu)
}
}
}
3. 进入主 activity
这里须要搁置一个按钮执行下线操作,不便调试,同时这里须要将之前的 token 移除从新获取 token 等数据的缓存
button.setOnClickListener {mTencent.logout(this)
val kv = MMKV.defaultMMKV()
kv.remove("qq_login")
// 返回登录界面的相干操作
"退出登录胜利".showToast()}
至此,其实还有一个很重要的货色没有阐明,那就是 token 数据的缓存和个人信息数据的获取,这部分我写的登录的那个 UIlistener 外面了,登录胜利的同时,获取 login 的 response 的 json 数据和个人信息的 json 数据
4. 获取两段重要的 json 数据
-
login 的 json 数据
这个比拟容易,当咱们登录胜利的时候,oncomplete 外面的 response 即咱们想要的数据
override fun onComplete(response: Any?) {if (response == null) {"返回为空, 登录失败".showToast() return } val jsonResponse = response as JSONObject if (jsonResponse.length() == 0) {"返回为空, 登录失败".showToast() return } // 这个即利用 MMKV 进行缓存 json 数据 kv.encode("qq_login",response.toString()) "登录胜利".showToast()}
-
个人信息的数据
这个须要在 login 无效的前提下能力返回失常的数据
// 首先须要用上一步获取的 json 数据对 mTencent 进行赋值,这部分放在 doComplete 办法中执行 private fun doComplete(values: JSONObject?) { // 利用 Gson 进行格式化成对象 val gson = Gson() val qqLogin = gson.fromJson(values.toString(), QQLogin::class.java) mTencent.setAccessToken(qqLogin.access_token, qqLogin.expires_in.toString()) mTencent.openId = qqLogin.openid Log.e("fund",values.toString()) }
创立一个 get\_info 办法进行获取,留神这里须要对 mTencent 设置相干的属性能力获取失常获取数据
private fun getQQInfo(){ val qqToken = mTencent.qqToken // 这里的 UserInfo 是 sdk 自带的类,传入上下文和 token 即可 val info = UserInfo(context,qqToken) info.getUserInfo(object :BaseUiListener(mTencent){override fun onComplete(response: Any?){ // 这里对数据进行缓存 kv.encode("qq_info",response.toString()) } }) }
5. 踩坑系列
这里次要吐槽一下对于腾讯的自带的 session 缓存机制,过后是抱着不必本人实现缓存间接用现成的机制去看的,很遗憾这波偷懒失败,这部分 session 的设置不晓得具体的缓存机制,只晓得大略是用 share preference 实现的,外面有 saveSession,initSession,loadSession 这三个办法,看上去很容易的样子,而后抱着这种心态去尝试了一波,果然不出意外空指针异样,尝试批改了一波回调的程序依然空指针异样,折腾了大略三个多小时,放弃了,心态给搞崩了,最终释然了,为什么要用腾讯提供的办法,这个缓存本人实现也是相当的容易,这时想到了 MMKV,两行代码实现读取,最初只批改了多数的代码实现了登录的 token 的缓存机制,翻看 demo 外面的实现,外面如同是用这三种办法进行实现的,可能是某个实现机制没有弄明确,其实也不想明确,本人的思路比再去看 demo 容易多了,只是多了一个 json 的转对象的过程,其余的没有差异。所以倡议后来者间接本人实现缓存,不必管 sdk 提供的那些办法,真的有点难用。
五、总结
总之这次实现 QQ 接入踩了许多的坑,不过幸好最终还是实现了,心愿腾讯互联这个 sdk 可能上传 github 让更多的人参加和提供反馈,不然这个文档说是最差 sdk 体验也不为过。上面附上这次实现 QQ 登录的 demo 的 github 地址以及相干的 demo apk 供大家进行参考,大略总共就 400 行代码左右比官网的 demo 好很多,有问题欢送留言
https://github.com/xyh-fu/QQLoginTest.git