PS:持续更新...
什么是SDK?
SDK(Software Development Kit,软件开发工具包)是一个用于构建应用程序的工具集,包含开发特定软件的必要工具、库、文档和示例代码。SDK通常由软件或硬件厂商提供,帮助开发者更容易地为特定平台、操作系统、设备或服务创建应用程序。
SDK的应用场景
1.平台开发:如Android、iOS等移动操作系统的SDK,提供了开发移动应用的所有必要资源。
2.服务集成:如云服务的SDK,帮助开发者快速集成和使用特定的云服务(如AWS、Google Cloud等)。
3.硬件开发:一些硬件厂商提供SDK,以便开发者创建与其硬件设备兼容的软件。
Google新版登录SDK--Credential
依赖项:
(1)implementation 'androidx.appcompat:appcompat:1.3.1'
(2)implementation "com.google.android.gms:play-services-auth:21.2.0"
(3)implementation "androidx.credentials:credentials:1.3.0-rc01"
(4)implementation "androidx.credentials:credentials-play-services-auth:1.3.0-rc01"
(5)implementation "com.google.android.libraries.identity.googleid:googleid:1.1.0"
接入流程:官方文档
常见错误:
(1)Failed to transform annotation-experimental-1.4.0.aar
(2)androidx.credentials.exceptions.GetCredentialProviderConfigurationException: getCredentialAsync no provider dependencies found - please ensure the desired provider dependencies are added
1.可能是设备的Google Play Service版本太低导致的,可以尝试更新设备的Google Play Service;
(3)During begin sign in, failure response from one tap: Missing Feature{name=auth_api_credentials_begin_sign_in, version=8}
1.提高依赖项"androidx.credentials:credentials"和"androidx.credentials:credentials-play-services-auth"的版本;
(4)During begin sign in, failure response from one tap: 10: [28444] Developer console is not set up correctly.
1.这个可能是因为Google后台配置问题,也可能是本地客户端ID设置错误所致;
(5)androidx.credentials.exceptions.GetCredentialCancellationException: activity is cancelled by the user.
1.这个可能是本地客户端ID设置错误所致;
特殊要求:
(1)设备Android系统版本最低为8.0,需要设备具备Google三件套(Google服务框架、Google Play服务、Google商店);
(2)需要设备具备VPN功能,能够访问Google服务(例如能够正常打开Google商店);
常见第三方SDK(海外)
1.Google身份验证SDK;
2.Google支付SDK;
3.AppsFlyer移动归因和营销分析SDK;
……
后台配置
Google登录SDK:
1.登录Google Cloud后台,创建一个项目;
2.开启API和服务(Identity Toolkit API);
3.设置OAuth权限请求页面,添加测试用户;
4.添加OAuth2.0客户端ID,包括Web应用和Android,输入包名以及本地导包的jks的SHA-1指纹(Web应用的客户端ID将用于Google登录SDK);
5.值得注意的是,如果本地导包的jks发生改变,也应在后台更新SHA-1指纹,否则无法成功调用SDK。
Google支付SDK:
1.这个需要Google开发者账号,需要在Google Play控制台配置商品信息;
2.还需要上传一个应用程序签名密钥,这个需要上传一个.zip文件,生成方法则是通过pepk将开发者账号的配套密钥的pem与本地开发所用的jks联合加密,然后导出为.zip文件;
3.注意开发者账号的配套密钥不能作为本地开发密钥,Google Play控制台为了安全考虑这种方式不被允许;
示例1
示例1将演示如何为Unity3D开发的Android项目接入Google登录SDK。
开发环境:
1.Android Studio 2024.1.0.0
2.Windows 10
3.Unity3D 2020.3.48f1c1
4.Android SDK 34 (Android Studio导出环境) Android SDK 10/11(Unity构建环境)
5.Android NDK 19.0.5232133(Unity构建环境、Android Studio导出环境)
6.JDK 11.0.24(Android Studio导出环境) JDK 1.8.0(Unity构建环境)
7.Gradle 6.1.1(Unity构建环境、Android Studio导出环境)
8.Gradle Plugin 4.0.1(Android Studio导出环境)
Unity配置:
1.Minimum api level 22
2.Target api level 34
运行环境:
1.Mumu模拟器,设备(OPPO K10 PGJM10),Android系统(12);
功能描述:
1.Unity3D 搭建UI界面,可供玩家交互,包括登录、注销登录、查看用户信息、退出游戏四个功能;
2.Android Studio接入Google身份验证SDK,需要登录、注销登录、发送提示信息三个功能;
流程描述:
1.玩家进入游戏;
2.玩家点击登录按钮,触发Google登录功能,需要对登录结果和异常进行反馈;
3.玩家点击注销登录按钮,触发Google注销登录功能,需要对注销登录结果和异常进行反馈;
4.玩家点击查看用户信息按钮,显示账户的Google id、名称、邮箱、邮箱是否验证信息,需要对异常进行反馈;
5.玩家点击退出游戏按钮,关闭游戏程序;
6.对于结果和异常反馈应采用定时关闭的提示框进行显示;
方法对应表:
方法/端口
Android(Java)
Unity3D(C#)
Unity3D(C#)回调
登录
askLogin
OnLoginClick
OnLoginSuccess
注销登录
askLogout
OnLogoutClick
OnLogoutSuccess
发送提示信息
Null
SendTip
Null
用户信息表:
信息/端口
Android(Java)
与用户的 Google 帐号相关联的电子邮件地址
id
在条目上显示的显示名
name
用户的个人资料照片 URI
photo
用户的 Google ID Toekn
token
(1)Unity3D游戏代码
Tip.cs
using UnityEngine;
using UnityEngine.UI;
// 提示框组件:用以显示提示框信息以及交互
public class Tip : MonoBehaviour
{
[SerializeField] Text content;
[SerializeField] Button close;
public bool isUsed { get; private set; }
public bool Send(string tip)
{
if (isUsed || string.IsNullOrEmpty(tip)) return false;
isUsed = true;
content.text = tip;
gameObject.SetActive(true);
return true;
}
void Start()
{
close.onClick.AddListener(OnClose);
}
void OnClose()
{
gameObject.SetActive(false);
isUsed = false;
}
}
TipController.cs
using System.Collections.Generic;
using UnityEngine;
// 提示框控制器组件:显示提示框的统一调用接口以及控制提示框
public class TipController : MonoBehaviour
{
public Tip tip;
static Queue<string> contents = new Queue<string>();
const int MAX_COUNT = 10;
public static void Send(string content)
{
if (string.IsNullOrEmpty(content) || contents.Count >= MAX_COUNT) return;
contents.Enqueue(content);
}
void Update()
{
if (contents.Count > 0 && tip.Send(contents.Peek()))
contents.Dequeue();
}
}
UILogic.cs
using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
// UI逻辑:UI交互相关的逻辑以及 Android 和 Unity 相互调用
public class UILogic : MonoBehaviour
{
public Button login;
public Button show;
public Button logout;
public Button quit;
public ScrollRect showView;
public RawImage portrait;
public Text showText;
public Button showViewCloseButton;
AndroidJavaObject jo;
UserData userData;
bool isLoginSuccess;
void Start()
{
jo = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
login.onClick.AddListener(OnLoginClick);
show.onClick.AddListener(OnShowClick);
logout.onClick.AddListener(OnLogoutClick);
quit.onClick.AddListener(OnQuitClick);
showViewCloseButton.onClick.AddListener(OnShowViewCloseClick);
}
void OnDestroy()
{
jo.Dispose();
}
void OnLoginClick()
{
jo.Call("askLogin");
}
void OnShowClick()
{
if (!isLoginSuccess)
{
TipController.Send("请先进行登录。");
return;
}
if (portrait.texture == null) StartCoroutine(LoadPortrait(userData.photo));
if (string.IsNullOrEmpty(showText.text))
{
StringBuilder builder = new StringBuilder()
.Append("Gmail:" + userData.id)
.Append("\nUserName:" + userData.name)
.Append("\nToken:" + userData.token);
showText.text = builder.ToString();
}
showView.gameObject.SetActive(true);
}
void OnLogoutClick()
{
jo.Call("askLogout");
}
void OnQuitClick()
{
Application.Quit();
}
void OnShowViewCloseClick()
{
showView.gameObject.SetActive(false);
}
IEnumerator LoadPortrait(string uri)
{
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(uri))
{
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
TipController.Send("头像加载失败!");
else
{
Texture2D texture = DownloadHandlerTexture.GetContent(www);
portrait.texture = texture;
}
}
}
// ************************** Java Call CSharp ******************************
// 登录成功回调:带有用户信息
public void OnLoginSuccess(string jsonStr)
{
try
{
Debug.Log(jsonStr);
userData = JsonUtility.FromJson<UserData>(jsonStr);
Debug.Log(userData);
isLoginSuccess = true;
}
catch (Exception ex)
{
TipController.Send("用户信息获取失败,请尝试重新登录。");
Debug.LogError(ex.Message);
}
}
// 注销登录成功回调
public void OnLogoutSuccess(string value)
{
isLoginSuccess = false;
}
// 信息提示
public void SendTip(string content)
{
TipController.Send(content);
}
}
UserData.cs
// 用户信息:记录Google登录用户的信息结构体
[System.Serializable]
public struct UserData
{
// 与用户的 Google 帐号相关联的电子邮件地址
public string id;
// 在条目上显示的显示名
public string name;
// 用户的个人资料照片 URI
public string photo;
// 用户的 Google ID Toekn
public string token;
public override string ToString()
{
return $"[id:{id},name:{name},photo:{photo},token:{token}]";
}
}
(2)Android端Java代码(部分)
UnityPlayerActivity.java
// ****************************** Unity Call *******************************
public void askLogin(){
GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setAutoSelectEnabled(true)
.setServerClientId(getString(R.string.WEB_CLIENT_ID))
.build();
GetCredentialRequest request = new GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build();
CancellationSignal signal = new CancellationSignal();
signal.setOnCancelListener(() -> {
Log.d(TAG, "askLogin: Preparing credentials with Google was cancelled.");
sendTip("你已取消登录!");
});
getCredentialManager().getCredentialAsync(
this,
request,
signal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
@Override
public void onResult(GetCredentialResponse result) {
handleSignIn(result);
}
@Override
public void onError(GetCredentialException e) {
Log.e(TAG, "askLogin: ", e);
sendTip("登录失败!");
}
});
}
public void askLogout(){
ClearCredentialStateRequest clearCredentialStateRequest = new ClearCredentialStateRequest();
android.os.CancellationSignal cancellationSignal = new android.os.CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
Log.d(TAG, "askLoginOut:Preparing credentials with Google was cancelled.");
sendTip("你已取消注销登录操作!");
});
if (credentialManager != null) {
getCredentialManager().clearCredentialStateAsync(
clearCredentialStateRequest,
cancellationSignal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<Void, ClearCredentialException>() {
@Override
public void onResult(Void unused) {
Log.d(TAG, "askLoginOut:google注销登录成功");
sendTip("注销登录成功!");
UnityPlayer.UnitySendMessage("Canvas","OnLogoutSuccess","");
}
@Override
public void onError(ClearCredentialException e) {
Log.e(TAG, "askLoginOut:" , e);
sendTip("注销登录失败!");
}
}
);
}
}
// ****************************** Java Call *******************************
private void sendTip(String content){
UnityPlayer.UnitySendMessage("Canvas","SendTip",content);
}
// ****************************** Google Login SDK *******************************
private CredentialManager getCredentialManager(){
if(credentialManager == null)
credentialManager = CredentialManager.create(this);
return credentialManager;
}
private void handleSignIn(GetCredentialResponse result) {
// Handle the successfully returned credential.
Credential credential = result.getCredential();
if (credential instanceof PublicKeyCredential) {
String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
// Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
} else if (credential instanceof PasswordCredential) {
String username = ((PasswordCredential) credential).getId();
String password = ((PasswordCredential) credential).getPassword();
// Use id and password to send to your server to validate and authenticate
} else if (credential instanceof CustomCredential) {
if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
// Use googleIdTokenCredential and extract id to validate and
// authenticate on your server
GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.getData());
String idToken = googleIdTokenCredential.getIdToken();
try {
JSONObject googleLoginInfoReturn = new JSONObject();
googleLoginInfoReturn.put("id", googleIdTokenCredential.getId());
googleLoginInfoReturn.put("name",googleIdTokenCredential.getDisplayName());
googleLoginInfoReturn.put("photo",googleIdTokenCredential.getProfilePictureUri());
googleLoginInfoReturn.put("token",idToken);
Log.d(TAG, "handleSignIn: "+ googleLoginInfoReturn);
sendTip("登录成功!");
UnityPlayer.UnitySendMessage("Canvas","OnLoginSuccess",googleLoginInfoReturn.toString());
} catch (JSONException e) {
Log.e(TAG, "handleSignIn: ", e);
sendTip("用户信息解析异常!");
}
} else {
// Catch any unrecognized custom credential type here.
Log.d(TAG, "handleSignIn: Unexpected type of credential");
sendTip("未知的登录方式!");
}
} else {
// Catch any unrecognized credential type here.
Log.d(TAG, "handleSignIn: Unexpected type of credential");
sendTip("未知的登录方式!");
}
}
视频
Google登录SDK
如果这篇文章对你有帮助,请给作者点个赞吧!