How to Improve the Resource Download Speed for Mobile Games

How to Improve the Resource Download Speed for Mobile Games

Mobile Internet has now become an integral part of our daily lives, which has also spurred the creation of a myriad of mobile apps that provide various services. How to make their apps stand out from countless other apps is becoming a top-priority matter for many developers. As a result, developers often conduct various marketing activities on popular holidays, for example, shopping apps offering large product discounts and travel apps providing cheap bookings during national holidays, and short video and photography apps offering special effects and stickers that are only available on specific holidays, such as Christmas.

Many mobile games also offer new skins and levels on special occasions, such as national holidays, which usually requires the release of a new game version meaning that users may often have to download a large number of new resource files. As a result, the update package is often very large and takes a long time to download, which negatively affects app promotion and user experience. Wouldn't it be great if there was a way for apps to boost download speed? Fortunately, HMS Core Network Kit can help apps do just that.

As a basic network service suite, the kit utilizes Huawei's experience in far-field network communications, scenario-based RESTful APIs, and file upload and download APIs, in order to provide apps with easy-to-use device-cloud transmission channels featuring low latency, high throughput, and robust security. In addition to improving the file upload/download speed and success rate, the kit can also improve the URL network access speed, reduce wait times when the network signals are weak, and support smooth switchover between networks.

The kit incorporates the QUIC protocol and Huawei's large file congestion control algorithms, and utilizes efficiently concurrent data streams to improve the throughput on weak signal networks. Smart slicing sets different slicing thresholds and slicing quantities for different devices to improve the download speed. In addition, the kit supports concurrent execution and management of multiple tasks, which helps improve the download success rate. The aforementioned features make the kit perfect for scenarios such as app update, patch installation, loading of map and other resources, and downloading of activity images and videos.

Development Procedure

Before starting development, you'll need to follow instructions here to make the relevant preparations.

The sample code for integrating the SDK is as follows:

dependencies {
    // Use the network request function of the kit.
    implementation 'com.huawei.hms:network-embedded: 6.0.0.300'
    // Use the file upload and download functions of the kit.
    implementation 'com.huawei.hms:filemanager: 6.0.0.300'
}

Network Kit utilizes the new features of Java 8, such as lambda expressions and static methods in APIs. To use the kit, you need to add the following Java 8 compilation options for Gradle in the compileOptions block:

android{
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

File Upload

The following describes the procedure for implementing file upload. To learn more about the detailed procedure and sample code, please refer to the file upload and download codelab and sample code, respectively.

i. Dynamically apply for the phone storage read and write permissions in Android 6.0 (API Level 23) or later. (Each app needs to successfully apply for these permissions once only.)

if (Build.VERSION.SDK_INT >= 23) {
    if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1000);
    requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001);
    }
}

ii. Initialize the global upload manager class UploadManager.

UploadManager upManager = (UploadManager) new UploadManager
        .Builder("uploadManager")
        .build(context);

iii. Construct a request object. In the sample code, the file1 and file2 files are used as examples.

Map<String, String> httpHeader = new HashMap<>();
httpHeader.put("header1", "value1");
Map<String, String> httpParams = new HashMap<>();
httpParams.put("param1", "value1");
// Set the URL to which the files are uploaded.
String normalUrl = "https://path/upload";
// Set the path of file1 to upload.
String filePath1 = context.getString(R.string.filepath1);
// Set the path of file2 to upload.
String filePath2 = context.getString(R.string.filepath2);


// Construct a POST request object.
try{
    BodyRequest request = UploadManager.newPostRequestBuilder()
            .url(normalUrl)
            .fileParams("file1", new FileEntity(Uri.fromFile(new File(filePath1))))
            .fileParams("file2", new FileEntity(Uri.fromFile(new File(filePath2))))
            .params(httpParams)
            .headers(httpHeader)
            .build();
}catch(Exception exception){
    Log.e(TAG,"exception:" + exception.getMessage());
}

iv. Create the request callback object FileUploadCallback.

FileUploadCallback callback = new FileUploadCallback() {
    @Override
    public BodyRequest onStart(BodyRequest request) {
        // Set the method to be called when file upload starts.
        Log.i(TAG, "onStart:" + request);
        return request;
    }


    @Override
    public void onProgress(BodyRequest request, Progress progress) {
        // Set the method to be called when the file upload progress changes.
        Log.i(TAG, "onProgress:" + progress);
    }


    @Override
    public void onSuccess(Response<BodyRequest, String, Closeable> response) {
        // Set the method to be called when file upload is completed successfully.
        Log.i(TAG, "onSuccess:" + response.getContent());
    }


    @Override
    public void onException(BodyRequest request, NetworkException exception, Response<BodyRequest, String, Closeable> response) {
        // Set the method to be called when a network exception occurs during file upload or when the request is canceled.
        if (exception instanceof InterruptedException) {
            String errorMsg = "onException for canceled";
            Log.w(TAG, errorMsg);
        } else {
            String errorMsg = "onException for:" + request.getId() + " " + Log.getStackTraceString(exception);
            Log.e(TAG, errorMsg);
        }
    }
};

v. Send a request to upload the specified files, and check whether the upload starts successfully.

If the result code obtained through the getCode() method in the Result object is the same as that of static variable Result.SUCCESS, this indicates that file upload has started successfully.

Result result = upManager.start(request, callback);
// Check whether the result code returned by the getCode() method in the Result object is the same as that of static variable Result.SUCCESS. If so, file upload starts successfully.
if (result.getCode() != Result.SUCCESS) {
    Log.e(TAG, result.getMessage());
}

vi. Check the file upload status.

Related callback methods in the FileUploadCallback object created in step 4 will be called according to the file upload status.

  • The onStart method will be called when file upload starts.

  • The onProgress method will be called when the file upload progress changes. In addition, the Progress object can be parsed to obtain the upload progress.

  • The onException method will be called when an exception occurs during file upload.

vii. Verify the upload result.

The onSuccess method in the FileUploadCallback object created in step 4 will be called when file upload is completed successfully.

File Download

The following describes the procedure for implementing file download. The method for checking the detailed procedure and sample code is the same as that for file upload.

i. Dynamically apply for the phone storage read and write permissions in Android 6.0 (API Level 23) or later. (Each app needs to successfully apply for these permissions once only.)

if (Build.VERSION.SDK_INT >= 23) {
    if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1000);
    requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001);
    }
}

ii. Initialize the global download manager class DownloadManager.

DownloadManager downloadManager = new DownloadManager.Builder("downloadManager")
             .build(context);

iii. Construct a request object.

// Set the URL of the file to download.
String normalUrl = "https://gdown.baidu.com/data/wisegame/10a3a64384979a46/ee3710a3a64384979a46542316df73d4.apk";
// Set the path for storing the downloaded file on the device.
String downloadFilePath = context.getExternalCacheDir().getPath() + File.separator + "test.apk";
// Construct a GET request object.
GetRequest getRequest = DownloadManager.newGetRequestBuilder()
        .filePath(downloadFilePath)
        .url(normalUrl)
        .build();

iv. Create the request callback object FileRequestCallback.

FileRequestCallback callback = new FileRequestCallback() {
    @Override
    public GetRequest onStart(GetRequest request) {
        // Set the method to be called when file download starts.
        Log.i(TAG, "activity new onStart:" + request);
        return request;
    }

    @Override
    public void onProgress(GetRequest request, Progress progress) {
        // Set the method to be called when the file download progress changes.
        Log.i(TAG, "onProgress:" + progress);
    }

    @Override
    public void onSuccess(Response<GetRequest, File, Closeable> response) {
        // Set the method to be called when file download is completed successfully.
        String filePath = "";
        if (response.getContent() != null) {
            filePath = response.getContent().getAbsolutePath();
        }
        Log.i(TAG, "onSuccess:" + filePath);
    }

    @Override
    public void onException(GetRequest request, NetworkException exception, Response<GetRequest, File, Closeable> response) {
        // Set the method to be called when a network exception occurs during file download or when the request is paused or canceled.
        if (exception instanceof InterruptedException) {
            String errorMsg = "onException for paused or canceled";
            Log.w(TAG, errorMsg);
        } else {
            String errorMsg = "onException for:" + request.getId() + " " + Log.getStackTraceString(exception);
            Log.e(TAG, errorMsg);
        }
    }
};

v. Use DownloadManager to start file download, and check whether file download starts successfully.

If the result code obtained through the getCode() method in the Result object is the same as that of static variable Result.SUCCESS, this indicates that file download has started successfully.

Result result = downloadManager.start(getRequest, callback);
if (result.getCode() != Result.SUCCESS) {
    // If the result is Result.SUCCESS, file download starts successfully. Otherwise, file download fails to be started.
    Log.e(TAG, "start download task failed:" + result.getMessage());
}

vi. Check the file download status.

Related callback methods in the FileRequestCallback object created in step 4 will be called according to the file download status.

  • The onStart method will be called when file download starts.

  • The onProgress method will be called when the file download progress changes. In addition, the Progress object can be parsed to obtain the download progress.

  • The onException method will be called when an exception occurs during file download.

vii. Verify the download result.

The onSuccess method in the FileRequestCallback object created in step 4 will be called when file download is completed successfully. In addition, you can check whether the file exists in the specified download path on your device.

Conclusion

Mobile Internet is now becoming an integral part of our daily lives and has spurred the creation of a myriad of mobile apps that provide various services. In order to provide better services for users, app packages and resources are getting larger and larger, which makes downloading such packages and resources more time consuming. This is especially true for games whose packages and resources are generally very large and take a long time to download.

In this article, I demonstrated how to resolve this challenge by integrating a kit. The whole integration process is straightforward and cost-efficient, and is an effective way to improve the resource download speed for mobile games.