Skip to content

Android Integration Documentation

Integration Instructions

  • Android SDK Demo Download
  • (must support Android 11, otherwise it will affect the channel listing)
  • It is recommended to use Unity to export Android Studio project for packaging
  • For first-time integration, ensure full-screen adaptation for Android phones (common adaptation methods usually involve displaying the game in full-screen mode and ensuring that the top notch or camera does not interfere with the game’s UI functions, or the rating may be affected during cloud testing)
  • Ensure that all SDK interface calls are made on the
  • When calling Java interfaces in Unity, make sure to switch to the UI thread before calling the SDK interface
  • Please review the documentation carefull. If you have any questions about the integration, communicate directly with the technical team in the integration group
  • This document will be updated periodically, and any interface changes will be communicated in the group. For more updates, please refer to the FastSdk update log

Maven Repository Configuration

project:build.gradle

xml
buildscript { 
    repositories { 
        maven { 
            url 'https://sdk.wdyxgames.com/nexus/repository/sdk-public/' 
        } 
    } 
}

allprojects { 
    repositories { 
        maven { 
            url 'https://sdk.wdyxgames.com/nexus/repository/sdk-public/' 
        } 
    } 
}

app:build.gradle

xml
dependencies { 
    implementation 'com.hoolai.access.open:hoolai-core:1.0.5.2' 
}

app: AndroidManifest.xml Special Handling

Note:

Since the packaging tool dynamically replaces the package name in the APK, please follow the rules below to modify and replace the configuration in AndroidManifest.xml during integration. Otherwise, issues such as the inability to install the channel package onto the same device or abnormal crashes may occur.
()

xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.hoolai.access.channel.impl"> 
    
    <!-- 示例 --> 
    <uses-permission android:name="android.permission.INTERNET" /> 
    
    <!-- application中有以下三个新增配置,必加(游戏无自定义 Application 时android:name需要配置 HLApplication)--> 
    <application
        android:name="com.hoolai.access.open.fastaccess.HLApplication"
        android:allowBackup="false"
        android:extractNativeLibs="true"
        android:requestLegacyExternalStorage="true"
        android:usesCleartextTraffic="true"> 
        
        <meta-data 
            android:name="android.max_aspect" 
            android:value="2.5"
            android:allowBackup="false"
            android:requestLegacyExternalStorage="true"/> 
        <activity
            android:name="游戏的主Activity"
            android:configChanges="screenLayout|orientation|keyboardHidden|keyboard|fontScale|layoutDirection|density|smallestScreenSize|screenSize|uiMode|navigation|touchscreen|locale|mnc|mcc"
            android:launchMode="standard"> 
            ... 
            <!-- copy以下内容到游戏的启动类中,必接 --> 
            <intent-filter> 
                <action android:name="android.intent.action.VIEW" /> 
                <category android:name="android.intent.category.BROWSABLE" /> 
                <category android:name="android.intent.category.DEFAULT" /> 
                <data android:scheme="hlscheme" android:host="hlhost"/> 
            </intent-filter> 
            <!-- 懂球帝分享配置(选接) --> 
            <intent-filter> 
                <action android:name="android.intent.action.VIEW" /> 
                <category android:name="android.intent.category.DEFAULT" /> 
                <category android:name="android.intent.category.BROWSABLE" />
                <!--game可自定义,与请求参数保持一致即可 --> 
                <data android:scheme="game" /> 
            </intent-filter> 
                
        </activity> 
            
        <!-- MAIN_ACTIVITY标签的value=游戏的入口类Activity全路径,否则应用宝渠道登录失败 --> 
        <!-- 该标签必须加 --> 
        <meta-data
            android:name="MAIN_ACTIVITY"
            android:value="游戏的主Activity,如com.demo.GameActivity"/> 
                
        <!-- 注意!!!! ${applicationId}需要替换成hlApplicationId --> 
        <!-- 以下为示例 无需copy --> 
        <!-- TODO 原配置 --> 
        <provider
            android:name="xx.FileProvider"
            android:authorities="${applicationId}.Fileprovider"
            ... /> 
        <!-- TODO 修改后 --> 
        <provider
            android:name="xx.FileProvider"
            android:authorities="hlApplicationId.Fileprovider"
            ... /> 
        <!-- 以上为示例 无需copy --> 
    </application> 
    
</manifest>

Note:

If third-party SDKs are integrated, the configuration containing ${applicationId} in AndroidManifest will automatically be replaced with the current APK's package name. This will prevent the package name from being replaced during channel package creation. Developers should perform a self-check after the package is generated.

Self-check process:

  1. Open the APK package in Android Studio and locate the AndroidManifest.xml file. Find the package tag, which corresponds to the APK's package name.

  2. Search for this package name in the file and copy all configurations (except those with package="package_name") to the project's AndroidManifest file. Replace all occurrences with hlApplicationId and repackage.

  3. Ensure that the game's Activity launch mode is set to android:launchMode="standard".

Interface Documentation

Application Integration ()Note: This configuration must be declared in AndroidManifest.xml.

  • Method 1: If the game does not use a custom Application, use HLApplication.
  • Method 2: If the game uses a custom Application that extends HLApplication.class, no additional configuration is needed.
  • Method 3: If the game uses a custom Application that does not extend HLApplication.class, add the following call:
  • After completing one of the above configurations, make sure the following line is present in AndroidManifest.xml: <application android:name="xxxx.xxxx.XXXApplication"
java
public class YourApplication extends YourBaseApplication {

    @Override 
    public void onCreate() { 
        super.onCreate();
        FastSdk.onApplicationCreate(this); 
    }

    @Override public void onTerminate() { 
        super.onTerminate(); 
        FastSdk.onTerminate(this); 
    }

    @Override public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig); 
        FastSdk.onConfigurationChanged(this, newConfig); 
    }

    @Override protected void attachBaseContext(Context context) { 
        super.attachBaseContext(context); 
        FastSdk.attachBaseContext(this, context); 
    }
}

SDK Feature Integration

Initialization ()

Note:

  • During SDK initialization, to prevent a black screen in the game, it is recommended to prepare an image named unity_static_splash and place it in the res/drawable directory. This interface must be called first when the game starts. can hot updates, version checks, game initialization, login, and other features be used.
  • If no initialization callback is received or if onInitFailed is received, the game must not proceed with any further operations.
  • In case of initialization failure, it is recommended to guide the user to re-initialize or restart the game.
java
// Inside the onCreate lifecycle method of the game’s main entry class (not the splash screen class
// SDK initialization callback 
FastSdk.hlSystemListener = new HLSystemListener() { 
    @Override 
    public void onInitSuccess(InitResult initResult) {
        // Initialization succeeded
        // InitResult parameters: gameId, channelId, channel
        // gameId: The product ID, needed for login verification
        // channelId: ID for different distribution channels under the product
        // channel: Channel identifier, such as xiaomi, huawei, etc.
    }

    @Override
    public void onInitFailed(String reason) {
    // It is recommended to show a dialog prompt here,
    // and provide options to retry or exit the game
    }

    @Override
    public void onCustomExit() {
        // Custom game exit interface
        // Implement your own exit logic here
        // Otherwise, the app might fail channel review
        // The following is a demo example — do NOT directly copy it into production
        new AlertDialog.Builder(GameActivity.this)
                .setTitle("Game Exit Dialog")
                .setMessage("This is the game's custom exit interface.")
                .setNegativeButton("Cancel", null)
                .setPositiveButton("Confirm", (dialog, which) -> onExitSuccess(s))
                .setCancelable(false).show();
    }

    @Override
    public void onExitSuccess(String result) {
        // Recommended exit process for maximum compatibility with platforms like Oppo, Vivo, etc.
        // Not following this may cause black screens when relaunching the game
        moveTaskToBack(true);
        finish();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
        android.os.Process.killProcess(android.os.Process.myPid());
    }

    @Override
    public void onUpdate(String data) {
        // Version update handling (reserved for future implementation)
    }
}; 
// SDK account callback
FastSdk.hlAccountListener = new HLAccountListener() {
    @Override
    public void onRefreshUser(LoginResult result) {
        // Sub-account switch In this callback, only verify the user's legitimacy in the game
        // If your game does not support switching user info internally, you need to log out first, then log back in (no need to reinitialize)
    }

    @Override
     public void onLoginSuccess(LoginResult result) {
        // Login successful
    }

    @Override
    public void onLoginFailed(String reason) {
        // Login failed
    }

    @Override
    public void onLogout(Object... var1) {
        // Logout
    }
}; 
// SDK payment callback
FastSdk.hlPaymentListener = new HLPaymentListener() {
    @Override
    public void onPaySuccess(String result) {
        // Payment successful, this only indicates a successful callback.
        // Actual item delivery should be based on server-side notification.
        // Note: result may be an empty string.
    }

    @Override
    public void onPayFailed(String reason) {
        // Payment failed. Note: 'reason' may be an empty string.
    }

    @Override
    public void onQuerySuccess(List<GoodsInfo> list) {
        // Used for overseas games to fetch the product list for display.
    }
}; 
// SDK share callback (note: some third-party shares may not receive a callback, such as WeChat sharing)
FastSdk.hlShareListener = new HLShareListener() {
    @Override
    public void onShareSuccess() {
        // Share successful
    }

    @Override
    public void onShareFailed(String reason) {
        // Share failed
    }
};


// Note: Before calling onCreate, you must instantiate all the interfaces used above first
FastSdk.onCreate(this);

InitResult

Parameter NameTypeDescription
gameIdintProduct ID
channelStringChannel Name
channelIdintChannel ID

LoginResult

Parameter NameTypeDescription
uidlongUser ID
accessTokenStringToken
nickNameStringNickname
channelUidStringChannel User ID
channelStringChannel Name
serverAreaStringExtension Parameter

GoodsInfo

Parameter NameTypeDescription
itemIdStringProduct ID
itemNameStringProduct Name
itemCountStringProduct Quantity
itemPriceStringProduct Price
showTagStringIgnored in domestic
currencyStringCurrency Type

Login ()

Note:

  • Ensure that the "game_init_result" event has been reported before calling this method. For more details, please refer to Game Custom Report
  • Do not call the login interface if the initialization callback has not been received or if initialization fails.
java
FastSdk.login();

Data Reporting ()

Note (Basic data reporting must be done before payment)

  • There are four types of event reports: character creation, entering the server, level up, and custom
  • When reporting character creation, entering the server, and level up events, the parameters roleId, roleName, serverId, and serverName must not be empty and must be provided; otherwise, it will affect the payment functionality! For other required data, please refer to the documentation.
java
//eventType values:
//Character Creation: EventType.CreateRole
//Entering Server: EventType.EnterServer
//Level Up: EventType.LevelUp
//The first three must be reported, the following is for custom event reporting based on operational needs
//Custom Event: EventType.CustomerEvent
//After setting the custom point, you must set setExtendAction("xx"), setPhylum("xx"), etc.

PlayerInfo playerInfo = new PlayerInfo();
playerInfo.setRoleId("32424"); // Role unique identifier, mandatory
playerInfo.setRoleName("Nickname"); // Mandatory, String
playerInfo.setRoleLevel("6"); // Mandatory, note that the string must be a number, e.g., "123"
playerInfo.setZoneId("1"); // Mandatory, if not available, default to "1", ensure the value in the string is numeric
playerInfo.setZoneName("Eastern China Zone"); // Mandatory, if not available, default to something like "Zone 1"
playerInfo.setServerId("1"); // Mandatory, if not available, default to "1", ensure the value in the string is numeric
playerInfo.setServerName("Zone Name"); // Mandatory, if not available, default to something like "Zone 1"
playerInfo.setBalance("66"); // Mandatory, if not available, use ""
playerInfo.setVip("5"); // Mandatory
playerInfo.setPartyname("Party Name"); // Optional

// New content: extra must carry data, refer to the special data reporting for client-side data reporting
// Extended information, format: key:value, key:value
playerInfo.setExtra("a:arm,b:bom,gameResourceUrl:xxx,gameLoginServerUrl:xxx");
playerInfo.setClassField(""); // Event result, can be left blank
// playerInfo.setPhylum(""); // When sending extended data, provide the event number as instructed, e.g., 1
// playerInfo.setExtendAction("Point name"); // For basic reporting, do not pass this; for custom reporting, pass it
FastSdk.report(EventType.EnterServer, playerInfo);

Note:

  • Those who need to integrate CLS network detection functionality, please take note! Click to view detailed rules.
  • This functionality should be reported as soon as possible after SDK initialization. It can be reported separately or along with other event points.
  • The detection address can be a domain name or IP.
  • Replace the colon in the detection address with @ to avoid formatting errors.
  • The detection address key must use the following three fields: gameResourceUrl, gameLoginServerUrl, gameServerUrl.

For example: playerInfo.setExtra("key:value,gameResourceUrl:http@xxx/open/init, gameServerUrl:tcp@111.222.333.6@8888");

EventType

Parameter NameDescriptionRequired
EventType.CreateRoleCreate RoleYes
EventType.EnterServerGame Enter ServerYes
EventType.LevelUpRole Level UpYes
EventType.CustomerEventCustom EventYes

PlayerInfo

Parameter NameTypeDescriptionRequired
roleIdStringRole IDYes
roleNameStringRole NameYes
roleLevelStringLevelYes
zoneIdStringZone ID (if no zone, use serverId)Yes
zoneNameStringZone Name (if no zone, use serverName)Yes
serverIdStringServer IDYes
serverNameStringServer NameYes
balanceStringBalanceYes
vipStringVIP LevelYes
partyNameStringGuild NameNo
appVersionStringApp VersionYes
appResVersionStringGame Resource VersionYes
extendActionStringEvent NameYes
roleCreateTimeStringRole Creation TimeYes
phylumStringEvent ID (refer to "Client Data Reporting" documentation)Yes
classFieldStringEvent Result (ok/fail)No
extraStringExtended Information (used for cls reporting)No

Logout

java
FastSdk.logout();

Payment (), must be called after reporting upon entering the game or creating a character; otherwise, payment cannot be initiated.

java
PayParams payParams = new PayParams();
payParams.setItemId("item60"); // Product ID, not mandatory
payParams.setItemName("60 Diamonds"); // Product Name, not mandatory
payParams.setAmount(100); // Product Amount, mandatory, unit: cents
payParams.setNotifyUrl(""); // Payment callback URL
// Extra information, returned as-is in the callback, since each channel may have different callback parameter restrictions.
// The callback parameters currently support a length of 255. Avoid using these symbols: "|", "=", "+", "/".
payParams.setCallbackInfo("Payment extra information, game transmission parameters");
payParams.setCurrency("CNY"); // Optional for domestic, or set to "CNY"
FastSdk.pay(payParams);

Note: Debouncing suggestions

  • The SDK already implements debouncing, triggering the first payment only within 3 seconds.
  • It is recommended that the client display a mask layer when the purchase event is triggered to prevent players from clicking repeatedly. After receiving the payment callback, close the mask. If no callback is received, set a timeout, and automatically close the mask after the timeout.
  • Caching payment data and checking after the payment is completed is unnecessary. If the player quickly clicks different payment tiers, the actual payment may be the first one, but the second one could be cached, causing the payment to be successful but the check to fail. The success dialog should rely on the SDK callback. ::: PayParams
Parameter NameTypeDescriptionRequired
itemIdStringProduct IDYes
itemNameStringProduct NameYes
amountintAmount, unit: centsYes
countintQuantity, default is 1Yes
callbackInfoStringPass-through fieldNo
notifyUrlStringPayment callback URLNo
currencyStringCurrency type, default: CNYYes
optMap<String, String>Extension parametersNo

Exit Program

java
FastSdk.exit();

Retrieve Product List Information

java
//Note: This method may not need to be integrated for domestic games.  
FastSdk.queryGoodsInfo();

Lifecycle Methods ()

java
@Override 
protected void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    FastSdk.onSaveInstanceState(outState); 
}

@Override 
protected void onStart() { 
    super.onStart(); 
    FastSdk.onStart(this); 
}

@Override 
protected void onResume() { 
    super.onResume(); 
    FastSdk.onResume(this); 
}

@Override 
protected void onStop() { 
    super.onStop(); 
    FastSdk.onStop(this); 
}

@Override 
protected void onPause() { 
    super.onPause(); 
    FastSdk.onPause(this); 
}

@Override 
protected void onRestart() { 
    super.onRestart(); 
    FastSdk.onRestart(this); 
}

@Override 
protected void onDestroy() { 
    super.onDestroy(); 
    FastSdk.onDestroy(this); 
}

@Override 
protected void onNewIntent(Intent intent) { 
    super.onNewIntent(intent);
    FastSdk.onNewIntent(intent); 
}

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data); 
    FastSdk.onActivityResult(this, requestCode, resultCode, data); 
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); FastSdk.onRequestPermissionsResult(requestCode, permissions, grantResults); 
}

@Override 
public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); FastSdk.onConfigurationChanged(this, newConfig, getResources()); 
}

@Override 
public void onBackPressed() { 
    FastSdk.exit(); 
}

@Override 
public Resources getResources() { 
    return FastSdk.getResources(super.getResources()); 
} 

// The following are updates
@Override 
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { 
    super.onRestoreInstanceState(savedInstanceState); 
    FastSdk.onRestoreInstanceState(savedInstanceState); 
}

@Override 
public void onWindowFocusChanged(boolean hasFocus) { 
    super.onWindowFocusChanged(hasFocus); 
    FastSdk.onWindowFocusChanged(hasFocus); 
}

Share

java
ShareParams params = new ShareParams();
params.setTitle("Title");
params.setContent("Content");
params.setPicPath("picPath"); // Local or network image link
// Share link (Dongqiudi fixed as: dongqiudi://share/circle/)
params.setShareUrl("http://xx.com");

// WeChat image params.setBmp(bmp); 

FastSdk.share(ShareType.WX, ShareType.ChildType.WX_CHAT, params);

ShareType

Parameter NameTypeDescription
QQShareTypeQQ share
WXShareTypeWeChat share
DQDShareTypeDongqiudi share
FACEBOOKShareTypeFacebook share
SYSTEMShareTypeSystem share

ShareType.ChildType

Parameter NameTypeDescription
WX_CHATShareType.ChildTypeWeChat chat
WX_CIRCLEShareType.ChildTypeWeChat Moments
WX_FAVORITESShareType.ChildTypeWeChat Favorites
QQ_TEXT_AND_QQZONEShareType.ChildTypeShare text and sync to QQ Zone
QQ_IMG_AND_QQZONEShareType.ChildTypeShare image and sync to QQ Zone
QQ_TEXTShareType.ChildTypeShare text
QQ_IMGShareType.ChildTypeShare image
DQDShareType.ChildTypeDongqiudi
FACEBOOK_LINKShareType.ChildTypeFacebook link
FACEBOOK_IMGShareType.ChildTypeFacebook image
FACEBOOK_VIDEOShareType.ChildTypeFacebook video
SYSTEM_TEXTShareType.ChildTypeSystem share text link
SYSTEM_IMGShareType.ChildTypeSystem share image
SYSTEM_FILEShareType.ChildTypeSystem share file

ShareParams

Parameter NameTypeDescriptionRequired
idStringDongqiudi ID, only for DongqiudiYes
wxTypeint1: Chat, 2: Moments, 3: FavoritesNo
qqTypeint1: Default, 2: Pure ImageNo
showQQZonebooleanWhether to display on QQ ZoneNo
titleStringTitleNo
contentStringContentNo
picPathStringImage URLNo
shareUrlStringShare URLNo
callbackUrlStringDongqiudi callback URL, only for DongqiudiYes
bmpBitmapWeChat image, only for WeChat image sharingYes

Permission request ()

Note:

As per policy requirements, after a sensitive permission request is denied, it cannot be requested multiple times. Therefore, the SDK limits the number of requests to one. When users request permission, they can choose whether to show a prompt (default is false). If set to true and the request is made again, a prompt will appear guiding the user to manually grant permission. This prompt is usually shown when denying permissions would affect the functionality of the business.

java
// Single dangerous permission request, returns the application result
boolean hasPermission = FastSdk.checkPermission(Activity activity, String permission, boolean showRefuseDialog);

// activity: Current page context
// permission: Permission name
// showRefuseDialog: Whether to show the dialog when the permission is denied, default is true, optional

boolean hasPermission = FastSdk.checkPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, true);
if (hasPermission) {
    // your code
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    FastSdk.onRequestPermissionsResult(requestCode, permissions, grantResults);

    // Permission request result callback, check if granted
    if (requestCode == Math.abs(Manifest.permission.WRITE_EXTERNAL_STORAGE.hashCode())) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Logger.i("Permission granted"); 
            // your code
        } else {
            Logger.i("Permission denied");
        }
    }
}

Fangzhou Reporting (optional; note that calling this interface will directly upload data to Fangzhou without going through the SDK)

java
String action = "game_xxx"; 
Map<String, Object> hashMap = new HashMap<>(); 
hashMap.put("xxx", 123); 
hashMap.put("ooo", "321"); 
FastSdk.gameDataReport(action, hashMap);

Fangzhou Parameters

Parameter NameTypeDescriptionRequired
actionStringEvent NameYes
hashMapMap<String, Object>Event ContentYes

CD-KEY (optional, gift code redemption)

java
AccessActivityDataInfo info = new AccessActivityDataInfo();
info.setCode("兑换码"); // Redemption code
FastSdk.accessParticipate(AccessActivityType.CD_KEY, info);

CD-KEY Parameters

Parameter NameTypeDescriptionRequired
typeAccessActivityTypeActivity typeYes
infoAccessActivityDataInfoRedemption code objectYes

Q&A

What do I do if initialization fails and displays a "non-legitimate app" message?

The signature file is incorrect. If there is no signature file, you can contact the operations team.

What if I have no account for logging in?

The account/password login method does not have a registration feature. You will need the operations team to provide an account/password.

How do I test payment features in a sandbox environment?

Confirm with the operations team to ensure that the sandbox environment is enabled, and that the account has been added to the testing whitelist.

What if I'm unable to initiate payment?

Check if the character creation or server entry event has been reported when entering the game.

What do I do if the payment was successful, but doesn't go through?

  1. Check if the provided payment callback URL is correct.

  2. Check the payment interface to see if the setNotifyUrl(url) has been called in the payment parameters. If it has, this URL will be prioritized for callbacks.

What do I do if the floating button is not hidden when logging out from the game in the channel package?

The game has not called FastSdk.logout(). It is recommended to create two buttons in the game’s “Settings” interface: “Switch Server” and “Switch Account”. When “Switch Account” is clicked, call FastSdk.logout(). When “Switch Server” is clicked, no logout is needed; just return to the server selection screen.

Hoolai Access SDK