基于友盟的第三方登录
友盟地址,本文所用到的工具资源文件地址
相关代码以及上传到仓库地址的QQShare文件夹
本文参考自官方文档,十分感谢
因为我们使用第三方登录需要去QQ互联平台申请相应的权限,而QQ授权申请的审批需要时间,所以建议收藏本文后再慢慢观看。
文章分为上下两部分
- 基于友盟的第三方登录
- 基于友盟的第三方分享
准备
实现第三方登录我们有两个选择:
- 去各个平台上按照各个不同平台(如微信开放平台,QQ互联)的不同文档的不同方法进行集成
- 使用友盟这种第三方统一集成的方案
前者各个平台的文档使用方法不同,学习成本太高了。后者封装了统一的接口,降低了学习成本,所以我们使用它。
先来看一下效果的预览吧
我们点击QQ图标,调起了QQ登录的页面,选择想要的账号登录。进入主页面,在主页面中显示获取到的QQ账号信息,头像,昵称,性别,地区。点击退出登录,回到登录页面。
QQ互联平台地址:
在开始之前我们需要到QQ互联平台注册好我们的应用,拿到应用的APP ID,App Key。
我们在首页选择移动应用接入。第一次创建应用比较麻烦,选择个人接入后需要上传身份证等信息,个人信息通过后就可以进行应用创建了。
创建应用时需要填写你的应用包名和签名,包名就是我们项目对应module的build.gradle文件的applicationId,我们还需要了解怎么给应用打包签名。
然后我们使用签名获取工具(在资料文档里,其实就是一个apk安装包软件),输入我们的包名获得到我们的签名就可以了
本节只进行QQ登录的演示,不演示微信登录,因为微信登录申请通过后,你还需要交纳300元才能够使用他们的登录接口。
布局文件的书写:
创建项目QQShare,勾选kotlin语言支持后在module创建以下4个包
- app 自定义Application
- base 基础类
- module 业务包
- util 工具包
然后我们进去res-values-styles下,把主题修改成没有标题的样式
然后我们在base包下创建WinActivity,作为等等全屏化Activity的基础类
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.WindowManager
/**
* 全屏化基础Activity
* 在kotlin中,一个类要能被子类继承,父类本身要加上open关键字
*/
open class WinActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置全屏
win()
}
private fun win() {
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
}
}
一,登录布局
在module下创建login包,在login包下创建LoginActivity类。在LoginActivity类的导包中添加下面这一行,我们就不需要再为控件findViewByid了
//不再需要findID,activity_login当前Activity布局名字
import kotlinx.android.synthetic.main.activity_login.*
写登录布局文件activity_login(图片资源见博客资料drawable-hdpi)
布局代码
代码虽多,其实我们需要的,只是QQ那个小图标而已。其他的,只是我们模拟的页面。
我们回到LoginActivity,让LoginActivity继承于WinActivity。使用一个匿名内部类,实现QQ图标的点击事件。
import com.example.administrator.qqshare.base.WinActivity
//不需要在findID
import kotlinx.android.synthetic.main.activity_login.*
class LoginActivity : WinActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mQQ.setOnClickListener(View.OnClickListener {
//QQ图标点击事件
Log.d("tonjie","点击事件触发")
})
}
}
登录布局完成
二,主页面MainActivity布局
MainActivity布局用来显示QQ回调给我们的用户信息,如QQ头像,昵称,所在地区,性别。
因为要把我们的头像进行圆形处理,所以要在在build.gradle下添加图片圆形处理框架
//圆形图片处理框架
compile 'de.hdodenhof:circleimageview:2.2.0'
同样在MainActivity类的导包中添加这一行,我们就不需要再为控件写findViewByid了
//不需要在findID,activity_main当前Activity布局名字
import kotlinx.android.synthetic.main.activity_main.*
xml文件activity_main
回到MainActivity,实现注销登录布局的点击事件
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mLogOut.setOnClickListener(View.OnClickListener {
Log.d("tonjies","注销")
})
}
}
到这里,我们的布局文件就写好了
友盟的集成
一,库的集成
打开友盟网站:https://www.umeng.com/,注册账户后我们打开我的产品页面,https://mobile.umeng.com/apps。找到右边的管理页面,在页面下创建一个应用,拿到应用的appKey。
之后我们打开首页-产品-社会化分享(https://mobile.umeng.com/social),点击右边的SDK及文档,选择Androdi机器人图标进入SDK下载页面,勾选社会化分享,下载SDK。
这里我们可以在设置里面多勾选几个平台,虽然我们的第三方登录只是演示了QQ登录,但是后面我们的第三方分享,是不需要授权的,所以我们可以多勾选几个,到时候分享时可以用
到这里,我们可以使用SDK里面的jar包去一个一个复制到我们的app里面,然后进行集成。但是这样太麻烦了,而且这样你的module会显得凌乱,不好整理。
补充:今天在使用集成工具的时候,发现打包时打包出来的,总是空的module,这一问题暂时不知道怎么解决,可能是我后来安装的系统问题
如果你接下来使用辅助工具也有着同样的问题,请按照友盟官方文档,集成所需要的库,十分抱歉
其实友盟提供了另外一种方案,就是通过官方的集成辅助工具把SDK里面的jar文件,都打包成一个库,然后我们只要把这个库依赖到我们想要使用登录功能的module就可以了。
打开开发者平台,辅助工具,点击SDK集成辅助工具,下载(当然在博客资料里面也有)。
我们单独创建一个文件夹test,把集成工具和解压后的SDK文件都放到test文件夹里面。打开集成工具,点击最上边的...号,找到我们下载后解压的社会化分享SDK后,点击打开
我们点击打开后,可以看到test文件夹下多个个UMLibrary库文件,这个就是我们需要的库文件了,我们打开我们的项目,把该库导进我们的项目。命名为umeng。然后让我们的module依赖于该库
//app build.gradle
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//圆形图片处理框架
compile 'de.hdodenhof:circleimageview:2.2.0'
//友盟第三方分享库
implementation project(':umeng')
}
sync项目后我们运行程序,发现程序崩溃了!这是为什么呢,我们来到umeng库的AndroidManifest.xml文件一看,有好多的service服务爆红,显示找不到
其实如果我们当时下载SDK的时候,有勾选PushSDK,即友盟推送的SDK,那么就很容易看出,这些爆红的服务其实全都是PushSDK下的。
为什么我们只下载了Share分享SDK,它却给我们生成了PushSDK的东西这个问题我还不清楚。我的解决方案是大胆的把这些爆红的服务全都注释掉,再次运行程序,程序成功运行。
二,第三方登录具体代码
在app包下自定义Application类
/**
* Created by 舍长 on 2018/5/28.
* 舍长: 自定义Applciation
*/
class App : Application() {
override fun onCreate() {
super.onCreate()
// 友盟统计
/**
* 初始化common库
* 参数1:上下文,不能为空
* 参数2:【友盟+】 AppKey
* 参数3:【友盟+】 Channel
* 参数4:设备类型,UMConfigure.DEVICE_TYPE_PHONE为手机、UMConfigure.DEVICE_TYPE_BOX为盒子,默认为手机
* 参数5:Push推送业务的secret
*/
UMConfigure.init(this,"5afa5748f43e480cf40000a8"
,"umeng",UMConfigure.DEVICE_TYPE_PHONE,"")
/**
* 设置组件化的Log开关
* 参数: boolean 默认为false,如需查看LOG设置为true
*/
UMConfigure.setLogEnabled(true)
}
/**
* 代码块
* 101476089 为AppId
* bf12db860a21d9e0ce217b37cbc1dec7为AppKey
*/
init {
PlatformConfig.setQQZone("101476089", "bf12db860a21d9e0ce217b37cbc1dec7")
}
}
我们把我们在QQ互联平台申请到的APP ID 和APP key填去初始化代码块,kotlin的初始代码块和Java不同,必须加上init关键字,最后别忘了在AndroidManifest.xml文件中去引用
android:name=".app.App"
我们需要在AndroidManifest.xml文件里加上我们要使用的权限吗?
不需要,因为在我们的umeng库里面,已经有了我们登录所需要的权限了。
我们继续在AndroidManifest.xml文件的Application标签内添加以下代码
AndroidManifest.xml完整代码
LoginActivity:
配置友盟的工作就结束了,就可以到LoginActivity里面写我们真正的第三方登录登录代码啦。
为了方便调试,我们在util包下创建一个简单的日志工具类
/**
* Created by 舍长 on 2018/4/27.
* 在kotlin中,加了object后,L类就成为了一个单例模式的类,相当于帮我们省略掉了以前Java实现单例的代码
* 之后我们可以直接以L.d的形式调用类中的方法
*/
object L {
// TAG
public var TAG: String = "tonJies"
fun d(test: String) {
Log.d(TAG, test)
}
}
接着在util包下创建一个SharedPreferences封装类,这里就暂且使用Java代码,关于该封装类的详细介绍,可以查看我的另一篇文章,Android常用工具类
/**
* Created by Tulin_TonJie on 2018/2/10.
* SharedPreferences
*/
public class ShareUtils {
public static final String NAME = "config";
//存储
public static void putString(Context mContext, String key, String value) {
SharedPreferences sharedPreferences = mContext.getSharedPreferences(NAME, Context.MODE_PRIVATE);
sharedPreferences.edit().putString(key, value).commit();
}
//获取
public static String getString(Context mContext, String key, String defValue) {
SharedPreferences sharedPreferences = mContext.getSharedPreferences(NAME, Context.MODE_PRIVATE);
return sharedPreferences.getString(key, defValue);
}
//存储
public static void putInt(Context mContext, String key, int value) {
SharedPreferences sharedPreferences = mContext.getSharedPreferences(NAME, Context.MODE_PRIVATE);
sharedPreferences.edit().putInt(key, value).commit();
}
//获取
public static int getInt(Context mContext, String key, int defValue) {
SharedPreferences sharedPreferences = mContext.getSharedPreferences(NAME, Context.MODE_PRIVATE);
return sharedPreferences.getInt(key, defValue);
}
//存储
public static void putBoolean(Context mContext, String key, boolean value) {
SharedPreferences sharedPreferences = mContext.getSharedPreferences(NAME, Context.MODE_PRIVATE);
sharedPreferences.edit().putBoolean(key, value).commit();
}
//获取
public static boolean getBoolean(Context mContext, String key, Boolean defValue) {
SharedPreferences sharedPreferences = mContext.getSharedPreferences(NAME, Context.MODE_PRIVATE);
return sharedPreferences.getBoolean(key, defValue);
}
//删除 单个
public static void deleShare(Context context, String key) {
SharedPreferences sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE);
sharedPreferences.edit().remove(key).commit();
}
//删除全部
public static void deleAll(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE);
sharedPreferences.edit().clear().commit();
}
}
LoginActivity完整的代码
//不需要在findID
import kotlinx.android.synthetic.main.activity_login.*
class LoginActivity : WinActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mQQ.setOnClickListener(View.OnClickListener {
L.d("登录")
//QQ图标点击事件
UMShareAPI.get(this@LoginActivity).getPlatformInfo(this@LoginActivity, SHARE_MEDIA.QQ, authListener)
})
}
/**
* 第三方登录回调
*/
internal var authListener: UMAuthListener = object : UMAuthListener {
/**
* 开始登录的回调
* @param platform 第三方登录的平台名称
*/
override fun onStart(platform: SHARE_MEDIA) {
L.d("登录的第三方平台是:" + platform)
}
/**
* 登录成功回调
* @param platform
* @param action
* @param map
*/
override fun onComplete(platform: SHARE_MEDIA, action: Int, map: Map) {
// 遍历map集合,取出QQ登录后回调给我们的信息
for (key in map.keys) {
L.d("key值是:" + key + " 对应的具体值:" + map[key] + "\n")
// 将取出的QQ账户信息存储到SharedPreferences中
ShareUtils.putString(this@LoginActivity, key, map[key])
}
L.d("登录")
}
/**
* 失败
* @param platform
* @param action
* @param t
*/
override fun onError(platform: SHARE_MEDIA, action: Int, t: Throwable) {
L.d("登录失败" + t.message)
}
/**
* 取消登录的回调
* @param platform
* @param action
*/
override fun onCancel(platform: SHARE_MEDIA, action: Int) {
L.d("取消登录")
}
}
/**
* QQ登录必须加入此回调
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
super.onActivityResult(requestCode, resultCode, data)
UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data)
}
}
运行程序后点击QQ图标,我们调出了QQ授权登录的页面,输入账户的密码后,查看Log可以看到已经有了相应的回调信息
MainActivity:
在module的build.grale下添加图片加载框架Glide,用于对QQ头像的加载
// 图片加载框架
compile 'com.github.bumptech.glide:glide:4.5.0'
创建initData方法,使用ShareUtils封装类取出用户的信息,并展示到布局上
/**
* 加载用户信息
*/
private fun initData() {
// QQ昵称
val screen_name = ShareUtils.getString(this, "name", "")
// 性别
val gender = ShareUtils.getString(this, "gender", "")
// 省份
val province = ShareUtils.getString(this, "province", "")
// 城市
val city = ShareUtils.getString(this, "city", "")
// 头像地址
val iconurl = ShareUtils.getString(this, "iconurl", "http://p5olxq226.bkt.clouddn.com//test_user_logo.jpeg")
// 用户昵称
mUsername.setText(screen_name)
// 用户性别
mUserSex.setText(gender)
// 用户地区
mUserArea.setText(province + " " + city)
// 用户头像
Glide.with(this).load(iconurl).into(mUserLogo)
}
创建LogOut方法,用来注销账户信息
private fun LogOut() {
mLogOut.setOnClickListener(View.OnClickListener {
UMShareAPI.get(this).deleteOauth(this, SHARE_MEDIA.QQ, object : UMAuthListener {
/**
* 成功
*/
override fun onComplete(p0: SHARE_MEDIA?, p1: Int, p2: MutableMap?) {
L.d("注销回调:成功")
}
override fun onCancel(p0: SHARE_MEDIA?, p1: Int) {
L.d("注销回调:取消")
}
override fun onError(p0: SHARE_MEDIA?, p1: Int, p2: Throwable?) {
L.d("注销回调:取消")
}
override fun onStart(p0: SHARE_MEDIA?) {
}
})
startActivity(Intent(this@MainActivity, LoginActivity::class.java))
})
}
MainActivity完整代码
//绑定控件
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : WinActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 加载用户信息
initData()
// 注销账号
LogOut()
}
private fun LogOut() {
mLogOut.setOnClickListener(View.OnClickListener {
UMShareAPI.get(this).deleteOauth(this, SHARE_MEDIA.QQ, object : UMAuthListener {
/**
* 成功
*/
override fun onComplete(p0: SHARE_MEDIA?, p1: Int, p2: MutableMap?) {
L.d("注销回调:成功")
}
override fun onCancel(p0: SHARE_MEDIA?, p1: Int) {
L.d("注销回调:取消")
}
override fun onError(p0: SHARE_MEDIA?, p1: Int, p2: Throwable?) {
L.d("注销回调:取消")
}
override fun onStart(p0: SHARE_MEDIA?) {
}
})
startActivity(Intent(this@MainActivity, LoginActivity::class.java))
})
}
/**
* 加载用户信息
*/
private fun initData() {
// QQ昵称
val screen_name = ShareUtils.getString(this, "name", "")
// 性别
val gender = ShareUtils.getString(this, "gender", "")
// 省份
val province = ShareUtils.getString(this, "province", "")
// 城市
val city = ShareUtils.getString(this, "city", "")
// 头像地址
val iconurl = ShareUtils.getString(this, "iconurl", "http://p5olxq226.bkt.clouddn.com//test_user_logo.jpeg")
val stringBuffer = StringBuffer()
stringBuffer.append("姓名: $screen_name\n性别:$gender 省份:$province 城市:$city ")
// 用户昵称
mUsername.setText(screen_name)
// 用户性别
mUserSex.setText(gender)
// 用户地区
mUserArea.setText(province + " " + city)
// 用户头像
Glide.with(this).load(iconurl).into(mUserLogo)
}
}
整个第三方登录的案例到这里就结束了