AndroidQQ登录接入详细介绍(kotlin搭建)
# 一、前言
由于之前自己项目的账号系统不是非常完善,所以考虑接入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冲突的问题,然后当时新建的项目没有用,结果报错,最后还是加上了,才可以
# 3. jar的引入
将jar放入lib包下,然后在app 同级的 build.gradle添加以下代码即完成jar的引用
dependencies {
...
implementation fileTree(dir: 'libs', include: '*.jar')
...
}
2
3
4
5
# 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
上面的哪个代码的最后提供了一个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>
2
3
4
5
# 三、初始化配置
# 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)
2
3
4
5
6
# 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()
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
建立一个按钮用于监听,这里进行登录操作
button.setOnClickListener {
if (!mTencent.isSessionValid) {
//判断会话是否有效
when (mTencent.login(this, "all",iu)) {
//下面为login可能返回的值的情况
0 -> "正常登录".showToast()
1 -> "开始登录".showToast()
-1 -> "异常".showToast()
2 -> "使用H5登陆或显示下载页面".showToast()
else -> "出错".showToast()
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这边对mTencent.login(this, "all",iu)中login的参数做一下解释说明
mTencent.login(this, "all",iu)
//这里Tencent的实例mTencent的login函数的三个参数
//1.为当前的context,
//2.权限,可选项,一般选择all即可,即全部的权限,不过目前好像也只有一个开放的权限了
//3.为UIlistener的实例对象
2
3
4
5
还差最后一步,获取回调的结果的代码,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)
}
}
}
2
3
4
5
6
7
8
9
10
至此,已经可以正常登录了,但还有一件我们开发者最关心的事情没有做,获取的用户的数据在哪呢?可以获取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
}
2
3
4
5
6
7
8
9
检查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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 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()
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
1:正常登录
这个就无需做处理了,直接在回调那里做相关的登录处理即可
0:开始登录
同正常登录
-1:异常登录
这个需要做一点处理,当时第一次遇到这个情况就是主activity异常消耗退回登录的activity,此时在此点击登录界面的按钮导致了异常情况的出现,不过这个处理起来还是比较容易的,执行强制下线操作即可
"异常".showToast() mTencent.logout(QQLoginTestApplication.context)
1
22:使用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)
}
}
}
2
3
4
5
6
7
8
9
10
# 3.进入主activity
这里需要放置一个按钮执行下线操作,方便调试,同时这里需要将之前的token移除重新获取token等数据的缓存
button.setOnClickListener {
mTencent.logout(this)
val kv = MMKV.defaultMMKV()
kv.remove("qq_login")
//返回登录界面的相关操作
"退出登录成功".showToast()
}
2
3
4
5
6
7
至此,其实还有一个很重要的东西没有说明,那就是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() }
1
2
3
4
5
6
7
8
9
10
11
12
13
14个人信息的数据
这个需要在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()) }
1
2
3
4
5
6
7
8
9创建一个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()) } }) }
1
2
3
4
5
6
7
8
9
10
11
# 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