前言
Unity2019版本的classes.jar中没有包含UnityPlayerActivity,所以和接SDK和原来网上的教程有一点点不一样,所以自己摸索了一个接入SDK的流程。
项目地址:https://gitee.com/blinkedu/unity_qq_sdk
实现功能:
- QQ登录
- 获取QQ用户信息
- 获取用户UnionId
- QQ空间分享(图文分享)
- QQ消息分享(图文分享)
- 注销QQ登录
准备工作
环境
- Unity 2019.3.0f6 (需要配置好Android打包环境)
https://www.jianshu.com/p/2399112eb23e - Android Studio 3.5.2
QQ互联官网
- 登录QQ互联官网:https://connect.qq.com/
- 创建应用
- 在官网选择 应用管理
- 选择 创建应用
- 选择 创建移动应用
按照提示创建完,等待审核通过就行了。在
应用管理 -> 移动应用 可以查看到刚刚创建的应用

Android工程
创建Android工程
经过上面的创建,我们的Android工程(空工程)算是创建好了,但是这还只是一个空工程,无法和Unity进行交互,接下来会导入我们所需要的库文件
切记!!!切记!!!切记!!! 之后的所有操作都是在 qqlib 下面进行的
下载 QQSDK 并添加到项目中
SDK下载页网址: https://wiki.connect.qq.com/sdk%e4%b8%8b%e8%bd%bd

将 jar包复制到 项目中的 lib 目录下,并添加到
qqlib moudle

添加 Unity相关jar包到项目
在 Unity 安装目录下找到
classes.jar
参考路径:D:\Unity\2019.3.0f6\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Development\Classes
关于 mono 和 il2cpp 这两个目录下的 classes.jar ,你看你的Unity项目选择的哪种方式就选择对应下面的 classes.jar

将 classes.jar 也添加到 Android项目中,方式和添加 QQSDK 步骤一样
添加 UnityPlayerActivity.java 到 项目中
在 Unity 2019 版本(具体是2019的哪个版本没有去试过),classes.jar 中是找不到 UnityPlayerActitiy 了的,所有需要我们手动将它添加到项目中去
参考路径:D:\Unity\2019.3.0f6\Editor\Data\PlaybackEngines\AndroidPlayer\Source\com\unity3d\player

修改 qqlib 下面 AndroidManifest.xml

打开 AndroidManifest.xml ,将其中的内容替换为下面的内容
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.blinkedu.qqlib">
<!--权限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<!--QQ登录-->
<activity
android:name="com.tencent.tauth.AuthActivity"
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" />
</application>
</manifest>
还有就是修改中AppId(在QQ互联官网的应用管理里面可以看到自己申请的应用的AppId)

修改 build.gradle
将其中的 dependencies 修改为下面这样
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation files('libs/open_sdk_3.5.11.6_r13b47b7_lite.jar') // 打包后会包含到arr包中
compileOnly files('libs/classes.jar') // 只编译,不包含到arr包中
}
为什么要改成这样呢?
因为libs/classe.jar 我们是不希望它包含到构建的arr包中的,因为导入Unity中后,如果包含了classes.jar,会和Unity中已经存在的包冲突。所以在构建arr的时候只让它进行编译
接下来就开始撸代码啦
首先创建 MainActivity, 这个相当于Android项目的入口
创建完成的
MainActivity 需要继承 UnityPlayerActivity
package cn.blinkedu.qqlib;
public class MainActivity extends UnityPlayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
创建回调类 BaseUiListener
需要实现实现
IUiListener 接口,这个类的对象是用来接收进行登录/分享等操作后接收返回的回调
之后进行登录等操作时,会创建一个该类的对象接收对应的操作的返回
package cn.blinkedu.qqlib;
import com.tencent.tauth.IUiListener;
import com.tencent.tauth.UiError;
class BaseUiListener implements IUiListener {
// 完成操作
@Override
public void onComplete(Object o) {
}
// 发生错误
@Override
public void onError(UiError uiError) {
}
// 取消操作
@Override
public void onCancel() {
}
// 警告
@Override
public void onWarning(int i) {
}
}
实现登录和分享等功能
MainActivity 完整代码,已经注释好了,可以自行查看
package cn.blinkedu.qqlib;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.tencent.connect.UnionInfo;
import com.tencent.connect.UserInfo;
import com.tencent.connect.common.Constants;
import com.tencent.connect.share.QQShare;
import com.tencent.connect.share.QzoneShare;
import com.tencent.tauth.IUiListener;
import com.tencent.tauth.Tencent;
import com.unity3d.player.UnityPlayer;
import org.json.JSONObject;
import java.util.ArrayList;
public class MainActivity extends UnityPlayerActivity {
private final static String APP_ID = "101993349"; // 这里填你自己AppId
private final static String TAG = "QQSdk";
private Tencent mTencent = null;
private IUiListener loginListener = null; // 登录回调
private IUiListener shareQQListener = null; // QQ消息分享回调
private IUiListener shareQZoneListener = null; // QQ空间分享回调
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化sdk
mTencent = Tencent.createInstance(APP_ID, this.getApplicationContext());
// 设置授权(很重要),如果没有设置授权,无法进行登录,会提示未授权
if (Tencent.isPermissionNotGranted()) {
Tencent.setIsPermissionGranted(true);
}
// 初始化登录回调
loginListener = new BaseUiListener() {
@Override
public void onComplete(Object o) {
JSONObject jsonObject = (JSONObject) o;
// 保存openid和token
initOpenIdAndToken(jsonObject);
// 将接收到的json数据发到unity
// 第一个参数:Unity场景中挂在接收数据的脚本的游戏物体名称
// 第二个参数:脚本中的接收事件的方法名称
// 第三个参数:字符串数据(这里传的是json字符串)
UnityPlayer.UnitySendMessage("QQSdk", "OnLoginComplete", jsonObject.toString());
}
};
// 初始化分享到QQ回调
shareQQListener = new BaseUiListener() {
@Override
public void onComplete(Object o) {
Toast.makeText(MainActivity.this, "分享成功", Toast.LENGTH_SHORT).show();
}
};
// 初始化分享到QQ空间回调
shareQZoneListener = new BaseUiListener() {
@Override
public void onComplete(Object o) {
Toast.makeText(MainActivity.this, "分享成功", Toast.LENGTH_SHORT).show();
}
};
}
// 登录QQ
public void login() {
// 判断会话是否有效,如果会话无效就进行登录操作
if (!mTencent.isSessionValid()) {
mTencent.login(this, "all", loginListener);
}
}
// 注销登录
public void logout() {
if (mTencent != null) {
mTencent.logout(this);
UnityPlayer.UnitySendMessage("QQSdk", "OnLogout", "");
Log.d(TAG, "logout! ");
}
}
// 获取用户信息
public void getUserInfo() {
if (mTencent != null && mTencent.isSessionValid()) {
UserInfo info = new UserInfo(this, mTencent.getQQToken());
info.getUserInfo(new BaseUiListener() {
@Override
public void onComplete(Object o) {
JSONObject jsonObject = (JSONObject) o;
UnityPlayer.UnitySendMessage("QQSdk", "OnGetUserInfoComplete", jsonObject.toString());
}
});
}
}
// 获取UnionId
public void getUnionId() {
if (mTencent != null && mTencent.isSessionValid()) {
UnionInfo unionInfo = new UnionInfo(this, mTencent.getQQToken());
unionInfo.getUnionId(new BaseUiListener(){
@Override
public void onComplete(Object o) {
JSONObject jsonObject = (JSONObject) o;
UnityPlayer.UnitySendMessage("QQSdk", "OnGetUnionIdComplete", jsonObject.toString());
}
});
}
}
// 分享到QQ
public void shareToQQ(String title, String summary, String targetUrl, String imgUrl, String appName) {
Bundle params = new Bundle();
// 分享类型
params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_DEFAULT);
// 标题
params.putString(QQShare.SHARE_TO_QQ_TITLE, title);
// 摘要
params.putString(QQShare.SHARE_TO_QQ_SUMMARY, summary);
// 跳转链接
params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, targetUrl);
// 图片链接
params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, imgUrl);
// App名称
params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName);
// 分享
mTencent.shareToQQ(this, params, shareQZoneListener);
}
// 分享到QQ空间(图文)
public void shareToQZone(String title, String summary, String targetUrl, String imgUrl, String appName) {
Bundle params = new Bundle();
// 分享类型
params.putInt(QzoneShare.SHARE_TO_QZONE_KEY_TYPE, QzoneShare.SHARE_TO_QZONE_TYPE_IMAGE_TEXT);
// 标题
params.putString(QzoneShare.SHARE_TO_QQ_TITLE, title);
// 摘要
params.putString(QzoneShare.SHARE_TO_QQ_SUMMARY, summary);
// 跳转链接
params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL, targetUrl);
// 图片
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(imgUrl);
params.putStringArrayList(QzoneShare.SHARE_TO_QQ_IMAGE_URL, arrayList);
// App名称
params.putString(QzoneShare.SHARE_TO_QQ_APP_NAME, appName);
// 分享
mTencent.shareToQzone(this, params, shareQZoneListener);
}
// 保存token和openId
private void initOpenIdAndToken(JSONObject o) {
try {
String token = o.getString(Constants.PARAM_ACCESS_TOKEN);
String expires = o.getString(Constants.PARAM_EXPIRES_IN);
String openId = o.getString(Constants.PARAM_OPEN_ID);
// 确保保存的数据不为空
if (!TextUtils.isEmpty(token) && !TextUtils.isEmpty(expires) && !TextUtils.isEmpty(openId)) {
mTencent.setAccessToken(token, expires);
mTencent.setOpenId(openId);
Log.d(TAG, "initOpenIdAndToken success!");
} else {
Log.e(TAG, "initOpenIdAndToken fail!");
}
} catch (Exception e) {
Log.e(TAG, "initOpenIdAndToken: ", e);
}
}
// 这个方法必须加,不然可能无法接收到回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IUiListener listener = null;
if (requestCode == Constants.REQUEST_LOGIN || requestCode == Constants.REQUEST_APPBAR) {
listener = loginListener;
} else if (requestCode == Constants.REQUEST_QQ_SHARE) {
listener = shareQQListener;
} else if (requestCode == Constants.REQUEST_QZONE_SHARE) {
listener = shareQZoneListener;
}
Tencent.onActivityResultData(requestCode, resultCode, data, listener);
super.onActivityResult(requestCode, resultCode, data);
}
}
构建arr包
Android相关的代码就是上面这么多了,接下来就是构建 arr 了

可以在 build/outputs/arr 目录下查看到生成的arr包

这里我们可以看到生成的是debug版本,这里顺带说一下怎么修改为生成release版的arr包 (选中左边侧栏下面的Build Variants)

生成的arr包我们是需要拷贝到Unity中进行使用的
Unity 工程
创建Unity工程
这一步就略过了哈,既然接sdk,相信Unity大家都应该很熟悉了
(注意将平台切换到Android平台,这里就默认各位的Unity中Android环境已经配置好了)

将 arr 拷贝到Unity中
创建 AndroidManifest.xml
将 AndroidManifest.xml 中的内容替换成下面这样
<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity3d.player" xmlns:tools="http://schemas.android.com/tools">
<!--权限-->
<uses-permission android:name="android.permission.INTERNET" />
<application>
<activity android:name="cn.blinkedu.qqlib.MainActivity" android:theme="@style/UnityThemeSelector" android:screenOrientation="landscape" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density" android:hardwareAccelerated="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
<meta-data android:name="android.notch_support" android:value="true" />
</activity>
<meta-data android:name="unity.splash-mode" android:value="0" />
<meta-data android:name="unity.splash-enable" android:value="True" />
<meta-data android:name="notch.config" android:value="portrait|landscape" />
<meta-data android:name="unity.build-id" android:value="5ae1a0d2-2a7d-41e3-972f-a67457200620" />
</application>
<uses-feature android:glEsVersion="0x00030000" />
<uses-feature android:name="android.hardware.vulkan.version" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
</manifest>
注意这里的Activity标签里面的包名保持和Android工程中的包名一致
添加 LitJson.dll
为什么要添加这个动态链接库?
这里笔者用这个库来解析Android端发过来的Josn数据,如果各位读者想用其他库解析(或者不用)也是可以的。该库的可以在工程示例中找到,工程链接在文章最前面

创建 QQSdk.cs 脚本
该脚本是一个单例类,,目的是为了方便其他模块调用
在场景中创建一个空的GameObject, 然后将该脚本挂载上去即可
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class QQSdk : MonoBehaviour
{
// 单例
public static QQSdk Instance { get; private set; }
private AndroidJavaClass jc = null;
private AndroidJavaObject jo = null;
private void Awake()
{
Instance = this;
jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
// 跳转场景不销毁
DontDestroyOnLoad(this);
}
/// <summary>
/// 登录
/// </summary>
public void Login()
{
jo?.Call("login");
}
/// <summary>
/// 注销
/// </summary>
public void Logout()
{
jo?.Call("logout");
}
/// <summary>
/// 获取用户信息
/// </summary>
public void GetUserInfo()
{
jo?.Call("getUserInfo");
}
/// <summary>
/// 获取UnionId
/// </summary>
public void GetUnionId()
{
jo.Call("getUnionId");
}
/// <summary>
/// 分享到QQ消息
/// </summary>
/// <param name="title">标题</param>
/// <param name="summary">概要内容</param>
/// <param name="targetUrl">跳转URL</param>
/// <param name="imgUrl">图片URL</param>
/// <param name="appName">应用名称</param>
public void ShareToQQ(string title, string summary, string targetUrl, string imgUrl = "", string appName = "")
{
jo?.Call("shareToQQ", title, summary, targetUrl, imgUrl, appName);
}
/// <summary>
/// 分享到QQ空间
/// </summary>
/// <param name="title">标题</param>
/// <param name="summary">概要内容</param>
/// <param name="targetUrl">跳转URL</param>
/// <param name="imgUrl">图片URL</param>
/// <param name="appName">应用名称</param>
public void ShareToQZone(string title, string summary, string targetUrl, string imgUrl = "", string appName = "")
{
jo?.Call("shareToQZone", title, summary, targetUrl, imgUrl, appName);
}
#region 需要Android调用的方法
// 登录成功
public void OnLoginComplete(string json)
{
GetUserInfo(); // 获取用户信息
GetUnionId(); // 获取UnionId
}
// 获取用户信息成功
public void OnGetUserInfoComplete(string json)
{
// 测试用,打开界面并刷新信息显示
Test.Instance.ShowInfoPanel();
Test.Instance.RefreshInfo(json);
}
// 获取UnionId成功
public void OnGetUnionIdComplete(string json)
{
}
// 注销登录
public void OnLogout(string arg)
{
// 测试用
Test.Instance.ShowLoginPanel();
}
#endregion
}
其实到这一步SDK已经可以使用了,自己可以调用脚本中提供的接口,就可以获取相关数据了,具体测试内容可自己下载工程查看,这里就不展示了
解决分享到QQ消息失败问题
大家测试的时候会发现,分享到QQ消息会提示分享失败,但是其他功能都是正常的,这个是怎么回事呢?
这个是因为我们没有设置应用签名导致的,并不是我们代码写的有问题
解决方法
Unity自带生成keystore功能




















