跳到主要内容
版本:v3

唤起更新开发指南

TapTap 开发者服务为游戏和玩家提供唤起 TapTap 客户端更新游戏的功能。

当游戏发布了新版本,且需要玩家进行更新才能体验新版本时,在游戏内绘制一个界面告知玩家并提供「更新」按钮。

玩家点击按钮调用 updateGameAndFailToWebInTapTap 接口,此时跳转到 TapTap 客户端内的游戏详情页,玩家在 TapTap 内完成更新。

信息

唤起更新只提供「唤起 TapTap 客户端,跳转至游戏详情页」这一简单功能。

TapTap 上游戏的版本号来自开发者在构建新版本时上传的 APK,唤起更新功能并不提供「检查 TapTap 商店是否有新版本」接口,游戏需自行实现判断版本的功能。

SDK 获取

SDK 可以通过 Unity Package Manager 导入或手动导入,二者任选其一。

如果选择 UPM 导入,可以在项目的 Packages/manifest.json 文件中添加:

"dependencies":{
"com.taptap.tds.common":"https://github.com/TapTap/TapCommon-Unity.git#3.28.3",
"com.leancloud.storage": "https://github.com/leancloud/csharp-sdk-upm.git#storage-2.3.0",
}

如果选择手动导入:

  • 下载页 找到 TapSDK Unity 下载地址,下载 TapSDK-UnityPackage.zip 然后解压,导入其中的 TapTap_Common 模块。
  • 下载 LeanCloud-SDK-Storage-Unity.zip,解压后为 Plugins 文件夹,拖拽至 Unity 即可。

唤起 TapTap 检查更新

提示

自 TapSDK 3.3.0 版本开始,针对更新功能做了逻辑优化,唤起 TapTap 客户端更新游戏失败时可以跳转到自定义网页。TapSDK 3.3.0 版本对之前的版本向下兼容。 该版本一般情况下不需要在使用以下 API 前特别检查是否安装 TapTap 客户端,自 TapSDK 3.3.0 版本开始推荐使用新接口。

在 TapTap 客户端更新游戏,失败时通过浏览器打开 TapTap 网站对应的游戏页面:

// 适用于中国大陆
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapTap(string appId);

// 适用于其他国家或地区
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapGlobal(string appId);

唤起 TapTap 客户端更新游戏失败,跳转到自定义网页:

// 适用于中国大陆
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapTap(string appId, string webUrl);

// 适用于其他国家或地区
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapGlobal(string appId, string webUrl);
点击展开 TapSDK 3.3.0 之前的相关接口

TapSDK 3.3.0 及之后版本不必参考这一节,推荐使用上面「唤起 TapTap 检查更新」的 API。

检查 TapTap 客户端是否安装

// 适用于中国大陆
TapCommon.IsTapTapInstalled(installed =>
{
if (installed) {
Debug.Log("TapTap 已经安装");
}
});

// 适用于其他国家或地区
TapCommon.IsTapTapGlobalInstalled(installed =>
{
if (installed) {
Debug.Log("TapTap 已经安装");
}
});

唤起 TapTap 更新游戏

TapCommon.UpdateGameInTapTap("appid", callSuccess =>
{
if (callSuccess) {
Debug.Log("TapTap 唤起成功");
}
});

常见问题:Android 11 或更高版本无法拉起 TapTap 客户端

Android 11(API level 30)之后加强了隐私保护策略,引入了大量变更和限制,其中一个重要变更——软件包可见性,将会导致第三方应用无法拉起 TapTap 客户端,从而影响 TapTap 相关功能的正常使用,包括但不限于更新唤起 TapTap、购买验证等功能。

如果没有完成适配,Android 版本为 11 及更高版本的客户端打开游戏会提示「本游戏需要最新版 TapTap 服务支持」,无法正常进入游戏。异常呈现如下图所示:

图片描述

对此提供如下两种适配方案:

方案一:

编译时将 targetSdkVersion 改为 29(目前设置成 >= 30 会触发该问题)。

方案二:

  1. 将 gradle build tools 改为 4.1.0+:

    classpath 'com.android.tools.build:gradle:4.1.0'
  2. 在 AndroidManifest.xml 里添加 <queries> 标签中的内容:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="string">

    <application
    . . .
    </application>

    <!--添加如下 queries 标签的内容,直接在 manifest 标签下-->
    <queries>
    <package android:name="com.taptap" />
    <package android:name="com.taptap.pad" />
    <package android:name="com.taptap.global" />
    </queries>
    </manifest>

打开游戏评论区

// 适用于中国大陆
TapCommon.OpenReviewInTapTap(appId, openSuccess =>
{
if (openSuccess) {
Debug.Log("打开游戏评论区成功");
}
});

// 适用于其他国家或地区
TapCommon.OpenReviewInTapGlobal(appId, openSuccess =>
{
if (openSuccess) {
Debug.Log("打开游戏评论区成功");
}
});

appid:游戏在 TapTap 商店的唯一身份标识。 例如:https://www.taptap.cn/app/187168,其中 187168appid

常见问题

未接入 TapSDK,如何唤起 TapTap 客户端更新游戏

受限于苹果政策,iOS 平台的 TapTap 客户端不提供游戏更新功能,以下方案仅限于 Android 平台使用。

未接入 TapSDK、使用旧版 TapSDK 难以升级的游戏,可以通过以下方案进行手动唤起 TapTap 客户端更新游戏:

根据玩家设备是否安装 TapTap 客户端来对应打开 URL:

  • 如果玩家设备安装 TapTap 客户端则直接唤起 TapTap 客户端到游戏详情页进行更新;
  • 如果玩家设备没有安装 TapTap 客户端,则以 Web 形式打开游戏详情页,根据页面底部提示引导玩家下载 TapTap 客户端,安装成功后打开 TapTap 客户端,玩家根据提示选择在 TapTap 客户端里打开游戏详情页进行更新。

未安装 TapTap 客户端对应的 URL:

  • 适用于中国大陆:https://l.taptap.cn/5d1NGyET?subc1=AppID
  • 适用于其他国家或地区:https://l.taptap.io/GNYwFaZr?subc1=AppID

已安装 TapTap 客户端对应的 URL:

  • 适用于中国大陆:taptap://taptap.cn/app?app_id=AppID&source=outer|update
  • 适用于其他国家或地区:tapglobal://taptap.tw/app?app_id=游戏商店id&source=outer|update

注意替换其中的 AppIDAppID 是游戏在 TapTap 商店的唯一身份标识,例如:https://www.taptap.cn/app/187168,其中 187168 是 AppID。

注意,除了打开 URL 外,还需要检测设备是否已经安装 TapTap 客户端,以及处理唤起失败的逻辑,这些代码都需要自行编写。

下面提供 TapSDK 唤起更新的代码供参考。

参考代码

适用于中国大陆:

package com.tds.common.utils;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.util.Locale;

public class TapGameUtil {

private static final String TAG = TapGameUtil.class.getName();

public static final String PACKAGE_NAME_TAPTAP = "com.taptap";

public static final String CLIENT_URI_TAPTAP = "taptap://taptap.cn";

public static final String DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP = "https://l.taptap.cn/5d1NGyET";

// 这里更新的时候不检查 Tap 客户端,一是因为特定 schema 没被应用注册的话大概率是直接返回 error 的,在这里被 try catch 后返回 false 可以近似等于客户端不存在
// 二是因为 Android 11 开始检查客户端需要游戏做特殊配置,这个配置无法在 SDK 内做好,因为和编译工具版本强绑定,无法做前后版本兼容。
public static boolean updateGameAndFailToWebInTapTap(Activity activity, String appId) {
return updateGameInTapTap(activity, appId) || openWebDownloadUrlOfTapTap(activity, appId);
}

public static boolean updateGameAndFailToWebInTapTap(Activity activity, String appId, String webUrl) {
if (TextUtils.isEmpty(webUrl)) {
return updateGameAndFailToWebInTapTap(activity, appId);
}
return updateGameInTapTap(activity, appId) || openWebDownloadUrl(activity, webUrl);
}

public static boolean isTapTapInstalled(Context context) {
return isTapClientInstalled(context, PACKAGE_NAME_TAPTAP);
}

public static boolean isTapClientInstalled(Context context, String clientPackageName) {
if (context != null && !TextUtils.isEmpty(clientPackageName)) {
boolean TapTapInstalled = false;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(clientPackageName, 0);
if (null != packageInfo) {
TapTapInstalled = true;
}
} catch (Exception e) {
Log.e(TAG, clientPackageName + " isInstalled=false");
}
return TapTapInstalled;
}
return false;
}

public static boolean updateGameInTapTap(Activity activity, String appId) {
return updateGameInTapClient(activity, appId, CLIENT_URI_TAPTAP);
}

public static boolean updateGameInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?app_id=%s&source=outer|update", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}

public static boolean openReviewInTapTap(Activity activity, String appId) {
return openReviewInTapClient(activity, appId, CLIENT_URI_TAPTAP);
}

public static boolean openReviewInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?tab_name=review&app_id=%s", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}

public static boolean openWebDownloadUrlOfTapTap(Activity activity, String appId) {
return openWebDownloadUrl(activity, String.format(Locale.US, DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP + "?subc1=%s", appId));
}

public static boolean openWebDownloadUrl(Activity activity, String url) {
if (activity != null && !TextUtils.isEmpty(url)) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "openWebUrl fail");
return false;
}
return true;
}
return false;
}

}

适用于其他国家或地区:

package com.tds.common.utils;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.util.Locale;

public class TapGameUtil {

private static final String TAG = TapGameUtil.class.getName();

public static final String PACKAGE_NAME_TAPTAP_GLOBAL = "com.taptap.global";

public static final String CLIENT_URI_TAPTAP_GLOBAL = "tapglobal://taptap.tw";

public static final String DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP_GLOBAL = "https://l.taptap.io/GNYwFaZr";

// 这里更新的时候不检查 Tap 客户端,一是因为特定 schema 没被应用注册的话大概率是直接返回 error 的,在这里被 try catch 后返回 false 可以近似等于客户端不存在
// 二是因为 Android 11 开始检查客户端需要游戏做特殊配置,这个配置无法在 SDK 内做好,因为和编译工具版本强绑定,无法做前后版本兼容。

public static boolean updateGameAndFailToWebInTapGlobal(Activity activity, String appId) {
return updateGameInTapGlobal(activity, appId) || openWebDownloadUrlOfTapGlobal(activity, appId);
}

public static boolean updateGameAndFailToWebInTapGlobal(Activity activity, String appId, String webUrl) {
if (TextUtils.isEmpty(webUrl)) {
return updateGameAndFailToWebInTapGlobal(activity, appId);
}
return updateGameInTapGlobal(activity, appId) || openWebDownloadUrl(activity, webUrl);
}

public static boolean isTapGlobalInstalled(Context context) {
return isTapClientInstalled(context, PACKAGE_NAME_TAPTAP_GLOBAL);
}

public static boolean isTapClientInstalled(Context context, String clientPackageName) {
if (context != null && !TextUtils.isEmpty(clientPackageName)) {
boolean TapTapInstalled = false;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(clientPackageName, 0);
if (null != packageInfo) {
TapTapInstalled = true;
}
} catch (Exception e) {
Log.e(TAG, clientPackageName + " isInstalled=false");
}
return TapTapInstalled;
}
return false;
}

public static boolean updateGameInTapGlobal(Activity activity, String appId) {
return updateGameInTapClient(activity, appId, CLIENT_URI_TAPTAP_GLOBAL);
}

public static boolean updateGameInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?app_id=%s&source=outer|update", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}

public static boolean openReviewInTapGlobal(Activity activity, String appId) {
return openReviewInTapClient(activity, appId, CLIENT_URI_TAPTAP_GLOBAL);
}

public static boolean openReviewInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?tab_name=review&app_id=%s", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}

public static boolean openWebDownloadUrlOfTapGlobal(Activity activity, String appId) {
return openWebDownloadUrl(activity, String.format(Locale.US, DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP_GLOBAL + "?subc1=%s", appId));
}

public static boolean openWebDownloadUrl(Activity activity, String url) {
if (activity != null && !TextUtils.isEmpty(url)) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "openWebUrl fail");
return false;
}
return true;
}
return false;
}

}