Use Accelerate Kit to calculate PI number - Huawei Developers

Prerequisites
Android Studio 3.6
Android SDK
Kotlin 1.3.72
Required knowledge
C programming language
Multithreading programming
Integration steps
Add the native build support to your build.gradle file
Code:
externalNativeBuild {
cmake {
cppFlags ""
arguments "-DANDROID_STL=c++_shared"
}
}
ndk {
abiFilters "arm64-v8a","armeabi-v7a"
}
Download the SDK package from this link and decompress it
Copy the header files in the SDK to the resource library.
In the /app directory in the Android Studio project, create an include folder. Copy the files in the /include directory of the SDK to the newly created include folder.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Copy the .so files in the SDK to the resource library.
Create a libs folder in the /app directory and create arm64-v8a folder and armeabi-v7a folder in /app/libs directory.
Copy the libdispatch.so and libBlockRuntime.so in lib64 directory of the SDK to libs/arm64-v8a directory of Android Studio.
Copy the libdispatch.so and libBlockRuntime.so in the lib directory of the SDK to the libs/armeabi-v7a directory of Android Studio.
Create or modify the CMakeLists.txt file in the app/src/main/cpp directory as follows
Code:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
target_include_directories(
native-lib
PRIVATE
${CMAKE_SOURCE_DIR}/../../../include)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
add_library(
dispatch
SHARED
IMPORTED)
set_target_properties(
dispatch
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libdispatch.so)
add_library(
BlocksRuntime
SHARED
IMPORTED)
set_target_properties(
BlocksRuntime
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libBlocksRuntime.so)
add_custom_command(
TARGET native-lib POST_BUILD
COMMAND
${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libdispatch.so
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/
COMMAND
${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libBlocksRuntime.so
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/
)
target_compile_options(native-lib PRIVATE -fblocks)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
dispatch
BlocksRuntime
# Links the target library to the log library
# included in the NDK.
${log-lib} )
From Android Studio, select Build → Linked C++ projects
Download the NDK if not configured and install it then sync with your projects
PI Formula
We will use the two below formula to calculate PI number
Nilakantha series
Coding steps
Open your application class and add the below code to load the native library when the application is created
Code:
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
On your demo activity, remember to add the external functions as below
Code:
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
private external fun doubleFromJNI(): Double
private external fun doubleFromJNI2(): Double
Then defined these functions in app/src/main/native-lib.cpp as below ( I will explain the dispatch functions later)
Code:
extern "C"
JNIEXPORT jdouble JNICALL
Java_com_pushdemo_jp_huawei_activity_AccelerateDemoActivity_doubleFromJNI(JNIEnv *env,
jobject thiz) {
dispatch_autostat_enable(env);
return dispatch1();
}
extern "C"
JNIEXPORT jdouble JNICALL
Java_com_pushdemo_jp_huawei_activity_AccelerateDemoActivity_doubleFromJNI2(JNIEnv *env,
jobject thiz) {
dispatch_autostat_enable(env);
return dispatch2();
}
For each formula, we will implement each function to calculate and divide them into subthread to execute to save time
For Nilakantha series, we will divide into 2 subthreads, and values of i set as follows:
[2, 6, 10, 14, ...]
[4, 8, 12, 16, ...]
Code:
double calcPiByStep(long start, long end, int step)
{
double total = 0.0;
int sign;
for (long i = start; i <= end; i += step)
{
long frag = i * (i + 1) * (i + 2);
sign = i % 4 == 0 ? -1 : 1;
double val = 4.0 * sign / frag;
total += val;
}
return total;
}
Then we will implement the dispatch1 function to summarize the result. First, create the concurrent queue, the serial queue, and the group queue. Then asynchronously add the subtask calcPiByStep to the concurrent queue, and associate the subtask with the group queue.
Code:
double dispatch1()
{
int i;
int step = 4;
long start = 2;
long limit = 1000000000;
__block double pi_total = 3;
dispatch_queue_t concurr_q = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serial_q = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
for (i = 0; i < step; i += 2) {
dispatch_group_async(group, concurr_q, ^{
double pi = calcPiByStep(start + i, limit, step);
dispatch_sync(serial_q, ^{
pi_total += pi;
});
});
}
dispatch_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
dispatch_release(serial_q);
return pi_total;
} 
For Gregory and Leibniz series, we will divide into 4 subthreads, and values of i set as follows:
[0, 4, 8, 12, ...]
[1, 5, 9, 13, ...]
[2, 6, 10, 14, ...]
[3, 7, 11, 15, ...]
Code:
double calcPiByStep2(long start, long end, int step)
{
double total = 0.0;
int sign;
for (long i = start; i <= end; i+= step)
{
long frag = 2 * i + 1;
sign = i % 2 == 0 ? 1 : -1;
total += 4.0 * sign / frag;
}
return total;
}
Then we will execute 4 subthreads concurrently as below
Code:
double dispatch2() {
__block double pi = 0;
int mod = 4;
int i = 0;
long limit = 1000000000;
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_apply(mod, DISPATCH_APPLY_AUTO, ^(size_t i) {
double _sum = calcPiByStep2(i, limit, mod);
dispatch_sync(serial, ^{
pi += _sum;
});
});
dispatch_release(serial);
return pi;
}
Result
For HMS devices, the result is quite fast. It took around 1.7 seconds to execute the dispatch1 and 1.4 seconds to execute the dispatch2
But for non-HMS devices, the calculation took longer times: around 6.7 seconds for dispatch1 and 3.5 seconds for dispatch2
Conclusion
The performance depends on the chipset. For HMS devices (using Kirin chip), the performance is good but for non-HMS devices (using other chip), the performance needs to be improved.

Related

The Ultimate Scanning App From Huawei

Have you ever wanted to be able to scan and create 13+ different types of barcodes from your app? Hopefully you have, otherwise I'm not sure why you're reading this. If you do, then Huawei has an SDK for you: Scan Kit.
Scan Kit allows you to scan and generate various different one-dimensional and two-dimensional barcodes with ease, and you don't even need a Huawei device to use it.
Interested? No? Well, get interested. Now? Good. Let's get started.
Preparation
First up, make sure you have a Huawei Developer Account. This process can take a couple days, and you'll need one to use this SDK, so be sure to start that as soon as possible. You can sign up at https://developer.huawei.com.
Next, you'll want to obtain the SHA-256 representation of your app's signing key. If you don't have a signing key yet, be sure to create one before continuing. To obtain your signing key's SHA-256, you'll need to use Keytool which is part of the JDK installation. Keytool is a command-line program. If you're on Windows, open CMD. If you're on Linux, open Terminal.
On Windows, you'll need to "cd" into the directory containing the Keytool executable. For example, if you have JDK 1.8 v231 installed, Keytool will be located at the following path:
Code:
C:\Program Files\Java\jdk1.8.0_231\bin\
Once you find the directory, "cd" into it:
Code:
C: #Make sure you're in the right drive
cd C:\Program Files\Java\jdk1.8.0_231\bin\
Next, you need to find the location of your keystore. Using Android's debug keystore as an example, where the Android SDK is hosted on the "E:" drive in Windows, the path will be as follows:
Code:
E:\AndroidSDK\.android\debug.keystore
(Keytool also supports JKS-format keystores.)
Now you're ready to run the command. On Windows, it'll look something like this:
Code:
keytool -list -v -keystore E:\AndroidSDK\.android\debug.keystore
On Linux, the command should be similar, just using UNIX-style paths instead.
Enter the keystore password, and the key name (if applicable), and you'll be presented with something similar to the following:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Make note of the SHA256 field.
SDK Setup
Now we're ready to add the Scan Kit SDK to your Android Studio project. Go to your Huawei Developer Console and click the HUAWEI AppGallery tile. Agree to the terms of use if prompted.
Click the "My projects" tile here. If you haven't already added your project to the AppGallery, add it now. You'll be asked for a project name. Make it something descriptive so you know what it's for.
Now, you should be on a screen that looks something like the following:
Click the "Add app" button. Here, you'll need to provide some details about your app, like its name and package name.
Once you click OK, some SDK setup instructions will be displayed. Follow them to get everything added to your project.
You'll also need to add one of the following to the "dependencies" section of your app-level build.gradle file:
Code:
implementation 'com.huawei.hms:scan:1.2.0.301'
implementation 'com.huawei.hms:scanplus:1.2.0.301'
The difference between these dependencies is fairly simple. The basic Scan Kit SDK is only about 0.8MB in size, while the Scan Kit Plus SDK is about 3.3MB. The basic SDK will function identically to the Plus SDK on Huawei devices. On non-Huawei devices, you'll lose the enhanced recognition capabilities with the basic SDK.
If your app has size restrictions, and you only need basic QR code recognition, then the basic SDK is the best choice. Otherwise, pick the Plus SDK.
If you ever need to come back to these instructions, you can always click the "Add SDK" button after "App information" on the "Project setting" page.
Now you should be back on the "Project setting" page. Find the "SHA-256 certificate fingerprint" field under "App information," click the "+" button, and paste your SHA-256.
Now, if you're using obfuscation in your app, you'll need to whitelist a few things for HMS to work properly.
For ProGuard:
Code:
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
For AndResGuard:
Code:
"R.string.hms*",
"R.string.agc*",
"R.string.connect_server_fail_prompt_toast",
"R.string.getting_message_fail_prompt_toast",
"R.string.no_available_network_prompt_toast",
"R.string.third_app_*",
"R.string.upsdk_*",
"R.layout.hms*",
"R.layout.upsdk_*",
"R.drawable.upsdk*",
"R.color.upsdk*",
"R.dimen.upsdk*",
"R.style.upsdk*
That's it! The Scan Kit SDK should now be available in your project.
Basic Usage
In order to properly use Scan Kit, you'll need to declare some permissions in your app:
XML:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
These are both dangerous-level permissions, so make sure you request access to them at runtime on Marshmallow and later.
You should also declare a couple required device features, to make sure distribution platforms can hide your app on incompatible devices:
XML:
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
Now that you've got the permissions and features declared, it's time to start scanning. There are four modes you can use: Default View, Customized View, Bitmap, and MultiProcessor.
Default View
The Default View is the quickest way to get set up and running. Scan Kit provides the UI and does all the scanning work for you. This mode is able to scan barcodes live through the camera viewfinder, or by scanning existing images.
The first thing you'll need to do to use this is to declare the Scan Kit's scanner Activity in your Manifest:
XML:
<activity android:name="com.huawei.hms.hmsscankit.ScanKitActivity" />
Next, simply call the appropriate method to start the Activity. This must be done from an Activity context, since it makes use of Android's onActivityResult() API.
Code:
//The options here are optional. You can simply pass null
//to the startScan() method below.
//This is useful if you're only looking to scan certain
//types of codes. Scan Kit currently supports 13. You can
//see the list here: https://developer.huawei.com/consumer/en/doc/HMSCore-Guides-V5/barcode-formats-supported-0000001050043981-V5
val options = HmsScanAnalyzerOptions.Creator()
.setHmsScanTypes(
HmsScan.QRCODE_SCAN_TYPE,
HmsScan.DATAMATRIX_SCAN_TYPE
)
.create()
ScanUtil.startScan(activity, REQUEST_CODE, options)
Now you just need to handle the result:
Code:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != Activity.RESULT_OK || data == null) {
//Scanning wasn't successful. Nothing to handle.
return
}
if (requestCode == REQUEST_CODE) {
//Scan succeeded. Let's get the data.
val hmsScan = data.getParcelableExtra<HmsScan>(ScanUtil.RESULT)
if (hmsScan != null) {
//Handle appropriately.
}
}
}
We'll talk more about handling the result later.
Customized View
Customized View is similar to Default View in that Scan Kit will handle the scanning logic for you. The difference is that this mode lets you create your own UI. You're also limited to using the live viewfinder.
The following code should be placed inside your Activity's onCreate() method:
Code:
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scan)
//A ViewGroup to hold the scanning View that HMS will create
val container = findViewById<FrameLayout>(R.id.scanning_container)
//The position/size of the scanning area inside the scanning
//View itself. This is optional.
val scanBounds = Rect()
//The scanning View itself.
val scanView = RemoteView.Builder()
.setContext(this)
.setBoundingBox(scanBounds) //optional
.setFormat(HmsScan.QR_SCAN_TYPE) //optional, accepts a vararg of formats
.build()
//Initialize the scanning View.
scanView.onCreate(savedInstanceState)
container.addView(scanView)
scanView.setOnResultCallback { result ->
//`result` is an Array<HmsScan>
//Handle accordingly.
}
}
We'll talk about how to handle the HmsScan object later.
In a real application, the "scanView" variable should be a global variable. It's lifecycle-aware, so it has all the lifecycle methods, which you should probably call:
Code:
override fun onStart() {
super.onStart()
scanView.onStart()
}
//etc
Bitmap
This mode is useful if you have a Bitmap and you want to decode it. You can get that Bitmap however you want. All that matters is that you have one.
The example below shows how to obtain a Bitmap either from a camera capture frame or from an existing image in internal storage, and how to pass that Bitmap to Scan Kit for processing.
Code:
val img = YuvImage(
//A byte array representation of an image.
data,
//Depends on your image type: https://developer.android.com/reference/android/graphics/ImageFormat
ImageFormat.NV21,
//The width of the image data.
width,
//The height of the image data.
height
)
val stream = ByteArrayOutputStream()
//Copy the data to an output stream.
img.compressToJpeg(
//Size of the JPEG, in a Rect representation.
Rect(0, 0, width, height),
//Quality.
100,
//Output stream.
stream
)
//Create a Bitmap
val bmp = BitmapFactory.decodeByteArray(
stream.asByteArray(),
0,
stream.asByteArray().size
)
//If you want to use a pre-existing image, just obtain
//a Bitmap for that. This example would be placed in
//onActivityResult() after using ACTION_GET_CONTENT for an image.
val bmp = MediaStore.Images.Media.getBitmap(contentResolver, data.data)
//Set up the options.
val options = HmsScanAnalyzerOptions.Creator()
//Scan types are optional.
.setHmsScanTypes(
HmsScan.QRCODE_SCAN_TYPE,
HmsScan.DATAMATRIX_SCAN_TYPE
)
//If you're retrieving a Bitmap from an existing photo,
//set this to `true`.
.setPhotoMode(false)
.create()
//Send the scanning request and get the result.
val scans: Array<HmsScan> = ScanUtil.decodeWithBitmap(context, bmp, options)
if (!scans.isNullOrEmpty()) {
//Scanning was successful.
//Check individual scanning results.
scans.forEach {
//Handle.
//If zoomValue != 1.0, make sure you update
//the focal length of your camera interface,
//if you're using a camera. See Huawei's
//documentation for details on this.
//https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-bitmap-camera-0000001050042014
val zoomValue = it.zoomValue
}
}
We'll go over how to handle the HmsScan later on.
MultiProccessor
The MultiProcessor mode supports both existing images and using a viewfinder. It can take image frames and analyze them using machine learning to simultaneously detect multiple barcodes, along with other objects, like faces.
There are two ways to implement this mode: synchronously, and asychronously.
Here's an example showing how to implement it synchronously.
Code:
//Create the options (optional).
val options = HmsScanAnalyzerOptions.Creator()
.setHmsScanTypes(
HmsScan.QRCODE_SCAN_TYPE,
HmsScan.DATAMATRIX_SCAN_TYPE
)
.create()
//Set up the barcode detector.
val barcodeDetector = HmsScanAnalyzer(options /* or null */)
//You'll need some sort of Bitmap reference.
val bmp = /* However you want to obtain it */
//Convert that Bitmap to an MLFrame.
val frame = MLFrame.fromBitmap(bmp)
//Analyze and handle result(s).
val results: SparseArray<HmsScan> = barcodeDetector.analyseFrame(frame)
results.forEach {
//Handle accordingly.
}
To analyze asynchronously, just change the method called on "barcodeDetector":
Code:
barcodeDetector.analyzInAsyn(frame)
.addOnSuccessListener { scans ->
//`scans` is a List<HmsScan>
scans?.forEach {
//Handle accordingly.
}
}
.addOnFailureListener { error ->
//Scanning failed. Check Exception.
}
Parsing Barcodes
It's finally time to talk about parsing the barcodes! Yay!
The HmsScan object has a bunch of methods you can use to retrieve information about barcodes scanned by each method.
Code:
val scan: HmsScan = /* some HmsScan instance */
//The value contained in the code. For instance,
//if it's a QR code for an SMS, this value will be
//something like: "smsto:1234567890:Hi!".
val originalValue = scan.originalValue
//The type of code. For example, SMS_FORM, URL_FORM,
//or WIFI_CONNTENT_INFO_FORM. You can find all the forms
//here: https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/scan-hms-scan4-0000001050167739-V5.
val scanTypeForm = scan.scanTypeForm
//If it's a call or SMS, you can retrieve the
//destination phone number. This will be something
//like "1234567890".
val destPhoneNumber = scan.destPhoneNumber
//If it's an SMS, you can retrieve the message
//content. This will be something like "Hi!".
val smsContent = scan.smsContent
There are plenty more methods in the HmsScan class to help you parse various barcodes. You can see them all in Huawei's API reference.
Barcode Generation
Finally, let's talk about how to make your own barcodes using Scan Kit. This example will show you how to make a QR code, although you should be able to create any of the formats supported by Scan Kit.
Code:
//The code's content.
val content = "Some Content"
//Can be any supported type.
val type = HmsScan.QRCODE_SCAN_TYPE
//Output dimensions.
val width = 400
val height = 400
//Create the options.
val options = HmsBuildBitmapOption.Creator()
//The background of the barcode image. White is the default.
.setBitmapBackgroundColor(Color.RED)
//The color of the barcode itself. Black is the default
.setBitmapColor(Color.BLUE)
//Margins around the barcode. 1 is the default.
.setBitmapMargin(3)
.create()
try {
//Build the output Bitmap.
val output = ScanUtil.buildBitmap(content, type, width, height, options)
//Save it to internal storage, upload it somewhere, etc.
} catch (e: WriterException) {
//Creation failed. This can happen if one of the arguments for `buildBitmap()` is invalid.
}
Conclusion
And that's it!
Scan Kit is a powerful SDK that doesn't even require a Huawei device to work. Hopefully, this guide helped you get up and running. For more details on implementation, along with the API reference, be sure to check out Huawei's full documentation.

Implementing HMS Audio Kit into your app

Playing audio on Android can be tricky, especially if you have to manage both online and offline services. Luckily, for apps targeting Huawei devices, there's HMS Audio Kit.
Audio Kit makes managing audio tracks and playlists a bit easier, by unifying the process for both online and offline sources. It allows you to aggregate songs into playlists, control those playlists, and all the other stuff you might want to do with your audio.
If you're making an app that plays audio, even if it isn't user-selected, and you're developing for Huawei devices, this library is worth a look.
Let's see how to implement it.
Preparation
First up, make sure you have a Huawei Developer Account. This process can take a couple days, and you'll need one to use this SDK, so be sure to start that as soon as possible. You can sign up at https://developer.huawei.com.
Next, you'll want to obtain the SHA-256 representation of your app's signing key. If you don't have a signing key yet, be sure to create one before continuing. To obtain your signing key's SHA-256, you'll need to use Keytool which is part of the JDK installation. Keytool is a command-line program. If you're on Windows, open CMD. If you're on Linux, open Terminal.
On Windows, you'll need to "cd" into the directory containing the Keytool executable. For example, if you have JDK 1.8 v231 installed, Keytool will be located at the following path:
Code:
C:\Program Files\Java\jdk1.8.0_231\bin\
Once you find the directory, "cd" into it:
Code:
C: #Make sure you're in the right drive
cd C:\Program Files\Java\jdk1.8.0_231\bin\
Next, you need to find the location of your keystore. Using Android's debug keystore as an example, where the Android SDK is hosted on the "E:" drive in Windows, the path will be as follows:
Code:
E:\AndroidSDK\.android\debug.keystore
(Keytool also supports JKS-format keystores.)
Now you're ready to run the command. On Windows, it'll look something like this:
Code:
keytool -list -v -keystore E:\AndroidSDK\.android\debug.keystore
On Linux, the command should be similar, just using UNIX-style paths instead.
Enter the keystore password, and the key name (if applicable), and you'll be presented with something similar to the following:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Make note of the SHA256 field.
SDK Setup
Now we're ready to add the Audio Kit SDK to your Android Studio project. Go to your Huawei Developer Console and click the HUAWEI AppGallery tile. Agree to the terms of use if prompted.
Click the "My projects" tile here. If you haven't already added your project to the AppGallery, add it now. You'll be asked for a project name. Make it something descriptive so you know what it's for.
Now, you should be on a screen that looks something like the following:
Click the "Add app" button. Here, you'll need to provide some details about your app, like its name and package name.
Once you click OK, some SDK setup instructions will be displayed. Follow them to get everything added to your project. You'll also need to add the following to the "dependencies" section of your app-level build.gradle file:
Code:
implementation 'com.huawei.hms:audiokit-player:1.0.0.302'
If you ever need to come back to these instructions, you can always click the "Add SDK" button after "App information" on the "Project setting" page.
Now you should be back on the "Project setting" page. Find the "SHA-256 certificate fingerprint" field under "App information," click the "+" button, and paste your SHA-256.
Now, if you're using obfuscation in your app, you'll need to whitelist a few things for HMS to work properly.
For ProGuard:
Code:
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
For AndResGuard:
Code:
"R.string.hms*",
"R.string.agc*",
"R.string.connect_server_fail_prompt_toast",
"R.string.getting_message_fail_prompt_toast",
"R.string.no_available_network_prompt_toast",
"R.string.third_app_*",
"R.string.upsdk_*",
"R.layout.hms*",
"R.layout.upsdk_*",
"R.drawable.upsdk*",
"R.color.upsdk*",
"R.dimen.upsdk*",
"R.style.upsdk*
That's it! The Audio Kit SDK should now be available in your project.
Basic Usage
Before we get into actually using the API, there are a few permissions you'll need to declare and have granted.
Add these to your AndroidManifest.xml and request access as needed:
XML:
<!-- Use these if you're going to play audio files from the internet -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Use these if you're going to play/save audio files from/to the device's storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_STORAGE" />
Now let's start implementing! Below is an example of how to set up the class instances you'll need.
Code:
//Playback control
lateinit var hwAudioPlayerManager: HwAudioPlayerManager
//Playback configuration
lateinit var hwAudioConfigManager: HwAudioConfigManager
//Queue control
lateinit var hwAudioQueueManager: HwAudioQueueManager
fun setUpAudioManager(context: Context) {
//Use this config to set various player parameters.
//Check the API docs for other config methods.
val config = HwAudioPlayerConfig(context)
//Set the cache size for the player in Mebibytes.
//Default is 200.
config.playCacheSize = 500
//Enable debug mode for verbose logging.
config.debugMode = true
HwAudioManagerFactory.createHwAudioManager(config, object : HwAudioConfigCallBack() {
override fun onSuccess(audioManager: HwAudioManager) {
//Setup succeeded. Let's try getting our instances.
try {
hwAudioPlayerManager = audioManager.playerManager
hwAudioConfigManager = audioManager.configManager
hwAudioQueueManager = audioManager.queueManager
} catch (e: Exception) {
//Something went wrong
}
}
override fun onError(errorCode: Int) {
//Initialization failed. Check the error code.
}
})
}
Now that we have the relevant instances, we can start playing audio. The following example shows how to create a playlist and play it.
Code:
//An online music file.
val firstSongUrl = "https://lfmusicservice.hwcloudtest.cn:18084/HMS/audio/Taoge-chengshilvren.mp3"
//A local music file.
val secondSongPath = "/storage/emulated/0/Download/Some_song.mp3"
//The item representing the first song.
val firstSong = HwAudioPlayItem()
//Set the song's title.
firstSong.audioTitle = "Some Online Song"
//Set the song's artist.
firstSong.singer = "Some Artist"
//Set the song's album art, in different sizes.
//These might be able to take local content URLs,
//but documentation is unclear.
firstSong.smallImageUrl = "https://some.image.url/image_small.jpg"
firstSong.midImageUrl = "https://some.image.url/image_mid.jpg"
firstSong.bigImageUrl = "https://some.image.url/image_big.jpg"
//Set an ID for this song
firstSong.audioId = firstSongUrl.hashCode()
//Since this track is online, set the proper flag.
firstSong.online = 1
//Set the URL.
firstSong.onlinePath = firstSongUrl
//Set the current seek, in milliseconds
firstSong.playPosition = 10000
//The item representing the second song.
val secondSong = HwAudioPlayItem()
secondSong.audioTitle = "Some Local Song"
secondSong.singer = "Some Other Artist"
secondSong.smallImageUrl = "content://some.image.uri/image_small.jpg"
secondSong.midImageUrl = "content://some.image.uri/image_mid.jpg"
secondSong.bigImageUrl = "content://some.image.uri/image_big.jpg"
secondSong.audioId = secondSongPath.hashCode()
//Set the file path.
secondSong.filePath = secondSongPath
//Create a playlist.
val playlist = ArrayList<HwAudioPlayItem>()
//Add items to list.
playlist.add(firstSong)
playlist.add(secondSong)
//Play the playlist.
hwAudioPlayerManager.playList(
playlist,
/* The index of the playlist to start at */ 0,
/* The seek position to start at, in seconds */ 0
)
//Pause playback.
hwAudioPlayerManager.pause()
//Stop playback completely (i.e. shutting down app).
hwAudioPlayerManager.stop()
//Resume playback.
hwAudioPlayerManager.play()
//Play the song at the specified index in the playlist.
hwAudioPlayerManager.play(1)
//Play the song at the specific index in the playlist
//with a given seek time, in seconds.
hwAudioPlayerManager.play(1, 10)
//Go to previous audio track.
hwAudioPlayerManager.playPre()
//Go to the next audio track.
hwAudioPlayerManager.playNext()
//Check if music is currently playing.
val isPlaying: Boolean = hwAudioPlayerManager.isPlaying()
//Check if music is currently buffering.
val isBuffering: Boolean = hwAudioPlayerManager.isBuffering()
//Get the buffer progress.
val bufferPercent: Int = hwAudioPlayerManager.bufferPercent
//Seek to the specified time on the current track (in seconds).
hwAudioPlayerManager.seekTo(10)
//Get the current seek (in seconds)
val seek: Int = hwAudioPlayerManager.offsetTime
//Set the mode for playing the playlist.
//Use 0 for sequential playback
//Use 1 to shuffle the list
//Use 2 to repeat the playlist
//Use 3 to repeat the current song
hwAudioPlayerManager.playMode = 0
You can also manage a playlist after you've already pushed it to be played, using HwAudioQueueManager.
Code:
//Check the current playlist position.
val index: Int = hwAudioQueueManager.currentIndex
//Get the currently playing song.
val currentlyPlaying: HwAudioPlayItem = hwAudioQueueManager.currentPlayItem
//Get the entire playlist.
val currentPlaylist: List<HwAudioPlayItem> = hwAudioQueueManager.allPlaylist
//Remove a song from the playlist, by index.
hwAudioQueueManager.removeListByIndex(1)
//Remove an HwAudioPlayItem from the playlist.
hwAudioQueueManager.removeListByIndex(currentlyPlaying)
//Add a song to the playlist, at the specified position.
hwAudioQueueManager.addPlayItem(/* HwAudioPlayItem */ song, /* position */ 2)
//Add a list of songs to the playlist, starting at the
//specified position.
hwAudioQueueManager.addPlayItemList(
/* List<HwAudioPlayItem> */ songs,
/* position */ 3
)
//Set a new playlist.
hwAudioQueueManager.setPlayList(currentPlaylist)
Conclusion
And that's it! Audio Kit is a pretty simple SDK that makes it easier to manage playing music from various sources. If you're looking for a simple way to play audio on Huawei devices, trye this library out.
Make sure to read Huawei's documentation for more details on implementation and the API.

React Native Startup Speed Optimization - Native Chapter (Including Source Code Analysis) 0. React Native Startup Process React Native is a web fron

0. React Native Startup Process
React Native is a web front-end friendly hybrid development framework that can be divided into two parts at startup:
Running of Native Containers​
Running of JavaScript code​
The Native container is started in the existing architecture (the version number is less than 1.0.0). The native container can be divided into three parts:
Native container initialization​
Full binding of native modules​
Initialization of JSEngine​
After the container is initialized, the stage is handed over to JavaScript, and the process can be divided into two parts:
Loading, parsing, and execution of JavaScript code​
Construction of JS components​
Finally, the JS Thread sends the calculated layout information to the Native end, calculates the Shadow Tree, and then the UI Thread performs layout and rendering.
I have drawn a diagram of the preceding steps. The following table describes the optimization direction of each step from left to right:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Note: During React Native initialization, multiple tasks may be executed concurrently. Therefore, the preceding figure only shows the initialization process of React Native and does not correspond to the execution sequence of the actual code.
1. Upgrade React Native
The best way to improve the performance of React Native applications is to upgrade a major version of the RN. After the app is upgraded from 0.59 to 0.62, no performance optimization is performed on the app, and the startup time is shortened by 1/2. When React Native's new architecture is released, both startup speed and rendering speed will be greatly improved.
2. Native container initialization
Container initialization must start from the app entry file. I will select some key code to sort out the initialization process.
iOS source code analysis
1.AppDelegate.m
AppDelegate.m is the entry file of the iOS. The code is simple. The main content is as follows:
Code:
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1. Initialize a method for loading jsbundle by RCTBridge.
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// 2. Use RCTBridge to initialize an RCTRootView.
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"RN64"
initialProperties:nil];
// 3. Initializing the UIViewController
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
// 4. Assigns the value of RCTRootView to the view of UIViewController.
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
In general, looking at the entry document, it does three things:
Initialize an RCTBridge implementation method for loading jsbundle.
Use RCTBridge to initialize an RCTRootView.
Assign the value of RCTRootView to the view of UIViewController to mount the UI.
From the entry source code, we can see that all the initialization work points to RCTRootView, so let's see what RCTRootView does.
2.RCTRootView
Let's take a look at the header file of RCTRootView first. Let's just look at some of the methods we focus on:
Code:
// RCTRootView.h
@interface RCTRootView : UIView
// Initialization methods used in AppDelegate.m
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(nullable NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;
From the header file:
RCTRootView inherits from UIView, so it is essentially a UI component;
When the RCTRootView invokes initWithBridge for initialization, an initialized RCTBridge must be transferred.
In the RCTRootView.m file, initWithBridge listens to a series of JS loading listening functions during initialization. After listening to the completion of JS Bundle file loading, it invokes AppRegistry.runApplication() in JS to start the RN application.
We find that RCTRootView.m only monitors various events of RCTBridge, but is not the core of initialization. Therefore, we need to go to the RCTBridge file.
3.RCTBridge.m
In RCTBridge.m, the initialization invoking path is long, and the full pasting source code is long. In short, the last call is (void)setUp. The core code is as follows:
Code:
- (Class)bridgeClass
{
return [RCTCxxBridge class];
}
- (void)setUp {
// Obtains the bridgeClass. The default value is RCTCxxBridge.
Class bridgeClass = self.bridgeClass;
// Initializing the RTCxxBridge
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
// Starting RTCxxBridge
[self.batchedBridge start];
}
We can see that the initialization of the RCTBridge points to the RTCxxBridge.
4.RTCxxBridge.mm
RTCxxBridge is the core of React Native initialization, and I looked at some material, and it seems that RTCxxBridge used to be called RCTBatchedBridge, so it's OK to crudely treat these two classes as the same thing.
Since the start method of RTCxxBridge is called in RCTBridge, let's see what we do from the start method.
Code:
// RTCxxBridge.mm
- (void)start {
// 1. Initialize JSThread. All subsequent JS codes are executed in this thread.
_jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
[_jsThread start];
// Creating a Parallel Queue
dispatch_group_t prepareBridge = dispatch_group_create();
// 2. Register all native modules.
[self registerExtraModules];
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 3. Initializing the JSExecutorFactory Instance
std::shared_ptr<JSExecutorFactory> executorFactory;
// 4. Initializes the underlying instance, namely, _reactInstance.
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 5. Loading the JS Code
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self
loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
}
onProgress:^(RCTLoadingProgress *progressData) {
}
];
// 6. Execute JS after the native module and JS code are loaded.
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
}
The preceding code is long, which uses some knowledge of GCD multi-threading. The process is described as follows:
1. Initialize the JS thread_jsThread.
2. Register all native modules on the main thread.
3. Prepare the bridge between JS and Native and the JS running environment.
4. Create the message queue RCTMessageThread on the JS thread and initialize _reactInstance.
5. Load the JS Bundle on the JS thread.
6. Execute the JS code after all the preceding operations are complete.
In fact, all the above six points can be drilled down, but the source code content involved in this section is enough. Interested readers can explore the source code based on the reference materials and the React Native source code.
Android source code analysis
1.MainActivity.java & MainApplication.java
Like iOS, the startup process starts with the entry file. Let's look at MainActivity.java:
MainActivity inherits from ReactActivity and ReactActivity inherits from AppCompatActivity:
Code:
// MainActivity.java
public class MainActivity extends ReactActivity {
// The returned component name is the same as the registered name of the JS portal.
@Override
protected String getMainComponentName() {
return "rn_performance_demo";
}
}
Let's start with the Android entry file MainApplication.java:
Code:
// MainApplication.java
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
// Return the ReactPackage required by the app and add the modules to be loaded,
// This is where a third-party package needs to be added when a dependency package is added to a project.
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
return packages;
}
// JS bundle entry file. Set this parameter to index.js.
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// SoLoader:Loading the C++ Underlying Library
SoLoader.init(this, /* native exopackage */ false);
}
}
The ReactApplication interface is simple and requires us to create a ReactNativeHost object:
Code:
public interface ReactApplication {
ReactNativeHost getReactNativeHost();
}
From the above analysis, we can see that everything points to the ReactNativeHost class. Let's take a look at it.
2.ReactNativeHost.java
The main task of ReactNativeHost is to create ReactInstanceManager.
Code:
public abstract class ReactNativeHost {
protected ReactInstanceManager createReactInstanceManager() {
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
ReactInstanceManagerBuilder builder =
ReactInstanceManager.builder()
// Application Context
.setApplication(mApplication)
// JSMainModulePath is equivalent to the JS Bundle on the application home page. It can transfer the URL to obtain the JS Bundle from the server.
// Of course, this can be used only in dev mode.
.setJSMainModulePath(getJSMainModuleName())
// Indicates whether to enable the dev mode.
.setUseDeveloperSupport(getUseDeveloperSupport())
// Redbox callback
.setRedBoxHandler(getRedBoxHandler())
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
.setUIImplementationProvider(getUIImplementationProvider())
.setJSIModulesPackage(getJSIModulePackage())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
// Add ReactPackage
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
// Obtaining the Loading Path of the JS Bundle
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
ReactInstanceManager reactInstanceManager = builder.build();
return reactInstanceManager;
}
}
3.ReactActivityDelegate.java
Let's go back to ReactActivity. It doesn't do anything by itself. All functions are implemented by its delegate class ReactActivityDelegate. So let's see how ReactActivityDelegate implements it.
Code:
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
String mainComponentName = getMainComponentName();
mReactDelegate =
new ReactDelegate(
getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions()) {
@Override
protected ReactRootView createRootView() {
return ReactActivityDelegate.this.createRootView();
}
};
if (mMainComponentName != null) {
// Loading the app page
loadApp(mainComponentName);
}
}
protected void loadApp(String appKey) {
mReactDelegate.loadApp(appKey);
// SetContentView() method of Activity
getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}
}
OnCreate() instantiates a ReactDelegate. Let's look at its implementation.
4.ReactDelegate.java
In ReactDelegate.java, I don't see it doing two things:
Create ReactRootView as the root view​
Start the RN application by calling getReactNativeHost().getReactInstanceManager()​
Code:
public class ReactDelegate {
public void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
// Create ReactRootView as the root view
mReactRootView = createRootView();
// Starting the RN Application
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
}
}
Basic Startup Process The source code content involved in this section is here. Interested readers can explore the source code based on the reference materials and React Native source code.
Optimization Suggestions
For applications with React Native as the main body, the RN container needs to be initialized immediately after the app is started. There is no optimization idea. However, native-based hybrid development apps have the following advantages:
Since initialization takes the longest time, can we initialize it before entering the React Native container?
This method is very common because many H5 containers do the same. Before entering the WebView web page, create a WebView container pool and initialize the WebView in advance. After entering the H5 container, load data rendering to achieve the effect of opening the web page in seconds.
The concept of the RN container pool is very mysterious. It is actually a map. The key is the componentName of the RN page (that is, the app name transferred in AppRegistry.registerComponent(appName, Component)), and the value is an instantiated RCT RootView/ReactRootView.
After the app is started, it is initialized in advance. Before entering the RN container, it reads the container pool. If there is a matched container, it directly uses it. If there is no matched container, it is initialized again.
Write two simple cases. The following figure shows how to build an RN container pool for iOS.
Code:
@property (nonatomic, strong) NSMutableDictionary<NSString *, RCTRootView *> *rootViewRool;
// Container Pool
-(NSMutableDictionary<NSString *, RCTRootView *> *)rootViewRool {
if (!_rootViewRool) {
_rootViewRool = @{}.mutableCopy;
}
return _rootViewRool;
}
// Cache RCTRootView
-(void)cacheRootView:(NSString *)componentName path:(NSString *)path props:(NSDictionary *)props bridge:(RCTBridge *)bridge {
// initialization
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:componentName
initialProperties:props];
// The instantiation must be loaded to the bottom of the screen. Otherwise, the view rendering cannot be triggered
[[UIApplication sharedApplication].keyWindow.rootViewController.view insertSubview:rootView atIndex:0];
rootView.frame = [UIScreen mainScreen].bounds;
// Put the cached RCTRootView into the container pool
NSString *key = [NSString stringWithFormat:@"%@_%@", componentName, path];
self.rootViewRool[key] = rootView;
}
// Read Container
-(RCTRootView *)getRootView:(NSString *)componentName path:(NSString *)path props:(NSDictionary *)props bridge:(RCTBridge *)bridge {
NSString *key = [NSString stringWithFormat:@"%@_%@", componentName, path];
RCTRootView *rootView = self.rootViewRool[key];
if (rootView) {
return rootView;
}
// Back-to-back logic
return [[RCTRootView alloc] initWithBridge:bridge moduleName:componentName initialProperties:props];
}
Android builds the RN container pool as follows:
Code:
private HashMap<String, ReactRootView> rootViewPool = new HashMap<>();
// Creating a Container
private ReactRootView createRootView(String componentName, String path, Bundle props, Context context) {
ReactInstanceManager bridgeInstance = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
ReactRootView rootView = new ReactRootView(context);
if(props == null) {
props = new Bundle();
}
props.putString("path", path);
rootView.startReactApplication(bridgeInstance, componentName, props);
return rootView;
}
// Cache Container
public void cahceRootView(String componentName, String path, Bundle props, Context context) {
ReactRootView rootView = createRootView(componentName, path, props, context);
String key = componentName + "_" + path;
// Put the cached RCTRootView into the container pool.
rootViewPool.put(key, rootView);
}
// Read Container
public ReactRootView getRootView(String componentName, String path, Bundle props, Context context) {
String key = componentName + "_" + path;
ReactRootView rootView = rootViewPool.get(key);
if (rootView != null) {
rootView.setAppProperties(newProps);
rootViewPool.remove(key);
return rootView;
}
// Back-to-back logic
return createRootView(componentName, path, props, context);
}
Each RCTRootView/ReactRootView occupies a certain memory. Therefore, when to instantiate, how many containers to instantiate, how to limit the pool size, and when to clear containers need to be practiced and explored based on services.
3. Native Modules Binding
iOS source code analysis
The iOS Native Modules has three parts. The main part is the _initializeModules function in the middle:
Code:
// RCTCxxBridge.mm
- (void)start {
// Native modules returned by the moduleProvider in initWithBundleURL_moduleProvider_launchOptions when the RCTBridge is initialized
[self registerExtraModules];
// Registering All Custom Native Modules
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// Initializes all native modules that are lazily loaded. This command is invoked only when Chrome debugging is used
[self registerExtraLazyModules];
}
Let's see what the _initializeModules function does:
Code:
// RCTCxxBridge.mm
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
// trigger the lazy initialization process. If the module cannot safely be
// set up on the current thread, it will instead be async dispatched
// to the main thread to be set up in _prepareModulesWithDispatchGroup:.
(void)[moduleData instance];
}
}
_moduleSetupComplete = YES;
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
According to the comments in _initializeModules and _prepareModulesWithDispatchGroup, the iOS initializes all Native Modules in the main thread during JS Bundle loading (in the JSThead thread).
Based on the previous source code analysis, we can see that when the React Native iOS container is initialized, all Native Modules are initialized. If there are many Native Modules, the startup time of the Android RN container is affected.
Android source code analysis
For the registration of Native Modules, the mainApplication.java entry file provides clues:
Code:
// RCTCxxBridge.mm
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
// trigger the lazy initialization process. If the module cannot safely be
// set up on the current thread, it will instead be async dispatched
// to the main thread to be set up in _prepareModulesWithDispatchGroup:.
(void)[moduleData instance];
}
}
_moduleSetupComplete = YES;
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
Since auto link is enabled in React Native after 0.60, the installed third-party Native Modules are in PackageList. Therefore, you can obtain the modules of auto link by simply gettingPackages().
In the source code, in the ReactInstanceManager.java file, createReactContext() is run to create a ReactContext. One step is to register the registry of nativeModules.
Code:
// ReactInstanceManager.java
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
// Registering the nativeModules Registry
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
}
According to the function invoking, we trace the processPackages() function and use a for loop to add all Native Modules in mPackages to the registry:
Code:
// ReactInstanceManager.java
private NativeModuleRegistry processPackages(
ReactApplicationContext reactContext,
List<ReactPackage> packages,
boolean checkAndUpdatePackageMembership) {
// Create JavaModule Registry Builder, which creates the JavaModule registry,
// JavaModule Registry Registers all JavaModules to Catalyst Instance
NativeModuleRegistryBuilder nativeModuleRegistryBuilder =
new NativeModuleRegistryBuilder(reactContext, this);
// Locking mPackages
// The mPackages type is List<ReactPackage>, which corresponds to packages in the MainApplication.java file
synchronized (mPackages) {
for (ReactPackage reactPackage : packages) {
try {
// Loop the ReactPackage injected into the application. The process is to add the modules to the corresponding registry
processPackage(reactPackage, nativeModuleRegistryBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
NativeModuleRegistry nativeModuleRegistry;
try {
// Generating the Java Module Registry
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
return nativeModuleRegistry;
}
Finally, call processPackage() for real registration:
Code:
// ReactInstanceManager.java
private void processPackage(
ReactPackage reactPackage,
NativeModuleRegistryBuilder nativeModuleRegistryBuilder
) {
nativeModuleRegistryBuilder.processPackage(reactPackage);
}
As shown in the preceding process, full registration is performed when Android registers Native Modules. If there are a large number of Native Modules, the startup time of the Android RN container will be affected.
Optimization Suggestions
To be honest, full binding of Native Modules is unsolvable in the existing architecture: regardless of whether the native method is used or not, all native methods are initialized when the container is started. In the new RN architecture, TurboModules solves this problem (described in the next section of this article).
If you have to talk about optimization, you have another idea. Do you want to initialize all the native modules? Can I reduce the number of Native Modules? One step in the new architecture is Lean Core, which is to simplify the React Native core. Some functions/components (such as the WebView component) are removed from the main project of the RN and delivered to the community for maintenance. You can download and integrate them separately when you want to use them.
The main benefits of this approach are as follows:
The core is more streamlined, and the RN maintainer has more energy to maintain main functions.
Reduce the binding time of Native Modules and unnecessary JS loading time, and reduce the package size, which is more friendly to initialization performance. (After the RN version is upgraded to 0.62, the initialization speed is doubled, which is basically thanks to Lean Core.)
Accelerate iteration and optimize development experience.
Now that Lean Core's work is almost complete, see the official issue discussion section for more discussion. We can enjoy Lean Core's work as long as we upgrade React Native.
4. How to optimize the startup performance of the new RN architecture
The new architecture of React Native has been skipping votes for almost two years. Every time you ask about the progress, the official response is "Don't rush, don't rush, we're doing it."
​I personally looked forward to it all year last year, but didn't wait for anything, so I don't care when the RN will update to version 1.0.0. Although the RN official has been doing some work, I have to say that their new architecture still has something. I have watched all the articles and videos on the new architecture in the market, so I have an overall understanding of the new architecture.
Because the new architecture has not been officially released, there must be some differences in details. The specific implementation details will be subject to the official React Native.
JSI
The full name of JSI is JavaScript Interface, a framework written in C++ that allows JS to call native methods directly instead of communicating asynchronously through Bridge.
How do I understand how JavaScript directly invokes Native? Let's take a simple example. When an API such as setTimeout document.getElementById is invoked on a browser, Native Code is directly invoked on the JavaScript side. You can verify the function on the browser console.
For example, I executed an order:
Code:
let el = document.createElement('div')
The variable el does not hold a JS object, but an object that is instantiated in C++. For the object held by el, set the following attributes:
Code:
el.setAttribute('width', 100)
In this case, JS synchronously invokes the setWidth method in C++ to change the width of the element.
The JSI in the new architecture of React Native is used for this purpose. With the JSI, we can use JS to directly obtain the reference of C++ objects (Host Objects), control the UI, and directly invoke methods of Native Modules, saving the overhead of bridge asynchronous communication.
Let's take a small example of how Java/OC uses JSI to expose synchronous invocation methods to JS.
Code:
#pragma once
#include <string>
#include <unordered_map>
#include <jsi/jsi.h>
// SampleJSIObject inherits from HostObject and represents an object exposed to JS
// For JS, JS can directly invoke the properties and methods on the object synchronously
class JSI_EXPORT SampleJSIObject : public facebook::jsi::HostObject {
public:
// The first step
// Exposes window.__SampleJSIObject to JavaScript
// This is a static function that is generally called from ObjC/Java during application initialization
static void SampleJSIObject::install(jsi::Runtime &runtime) {
runtime.global().setProperty(
runtime,
"__sampleJSIObject",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__SampleJSIObject"),
1,
[binding](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) {
// Returns the content of a call to window.__SampleJSIObject
return std::make_shared<SampleJSIObject>();
}));
}
// Similar to a getter, this method is used each time the JS accesses the object. The function is similar to a wrapper
// For example, if we call window.__sampleJSIObject.method1(), this method will be called
jsi::Value TurboModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
// Invoking method name
// For example, when window.__sampleJSIObject.method1() is called, propNameUtf8 is method1
std::string propNameUtf8 = propName.utf8(runtime);
return jsi::Function::createFromHostFunction(
runtime,
propName,
argCount,
[](facebook::jsi::Runtime &rt, const facebook::jsi::Value &thisVal, const facebook::jsi::Value *args, size_t count) {
if (propNameUtf8 == 'method1') {
// Function processing logic when method1 is invoked
}
});
}
std::vector<PropNameID> getPropertyNames(Runtime& rt){
}
}
The above example is short. To learn more about JSI, read the React Native JSI Challenge article or read the source code directly.
TurboModules
According to the previous source code analysis, in the current architecture, native modules are fully loaded during native initialization. As services are iterated, the number of native modules increases, which takes a longer time.
TurboModules solves this problem all at once. In the new architecture, native modules are loaded in lazy mode. That is, the loading is initialized only when you invoke the corresponding native modules. This solves the problem that initializing full loading takes a long time.
The calling path of TurboModules is as follows:
Use JSI to create a top-level Native Modules Proxy, which is called global.__turboModuleProxy.
Access a Native Module. For example, to access the SampleTurboModule, execute require('NativeSampleTurboModule') on the JavaScript side.
In the NativeSampleTurboModule.js file, we call TurboModuleRegistry.getEnforcing() and then call global.__turboModuleProxy("SampleTurboModule").
When global.__turboModuleProxy is invoked, the Native method exposed by the JSI in step 1 is invoked. In this case, the C++ layer finds the ObjC/Java implementation through the input string "SampleTurboModule". Finally, a corresponding JSI object is returned.
Now that we have the JSI object of SampleTurboModule, we can use JavaScript to synchronously invoke the properties and methods of the JSI object.
Through the preceding steps, we can see that with TurboModules, Native Modules are loaded only when they are invoked for the first time. This completely eliminates the time required for fully loading Native Modules during initialization of the React Native container. In addition, we can use JSI to implement synchronous invoking of JS and Native, which takes less time and improves efficiency.
Summary
This document analyzes the startup process of the existing architecture of React Native from the perspective of Native and summarizes the performance optimization points of the Native layer. Finally, we briefly introduce the new architecture of React Native. In the next article, I'll explain how to start with JavaScript and optimize React Native's startup speed.
By Halogenated Hydrocarbons
Original Link: https://segmentfault.com/a/1190000039797508
is RN container same as google map?

Learn how to Integrate Huawei Remote Configuration services using React Native

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
In this article, we will learn added to implement Remote Configuration service provided by Huawei. Remote Configuration is a cloud service where the behavior or features of an app can be changed remotely without having to publish an app update.
Requirements
1. JDK version: 1.7 or later
2. Android Studio version: 3.X or later
3. minSdkVersion: 21 or later
4. targetSdkVersion: 31 (recommended)
5. compileSdkVersion: 29 (recommended)
6. Gradle: 4.1 or later (recommended)
7. Must have a Huawei Developer Account
8. Must have a Huawei phone with HMS 4.0.0.300 or later and running EMUI 4.0 or later.
9. React Native environment with Android Studio, Node Js and Visual Studio code.
Integration process
1. Create a react native project using below command:
Code:
react-native init RemoteConfigReact
2. Generate a Signature File. First navigate to bin folder of java, then enter following command:
Code:
keytool -genkey -keystore <application_project_dir>\android\app\<signing_certificate_fingerprint_filename>.jks -storepass <store_password> -alias <alias> -keypass <key_password> -keysize 2048 -keyalg RSA -validity 36500.
This command creates the keystore file in application_project_dir/android/app.
3. Now generate SHA 256 key by following command.
Code:
keytool -list -v -keystore <keystore-file path>
4. Create an app in App Gallery by following instructions in Creating an AppGallery Connect Project and Adding an App to the Project. Provide the generated SHA256 Key in App Information Section.
5. Download the Huawei Remote config plugin by following command.
Code:
npm i @react-native-agconnect/remoteconfig.
6. Open react native project in Visual studio code.
7. Navigate to android > build.gradle and paste the below maven url inside the repositories of build script and all projects.
Code:
maven { url 'http://developer.huawei.com/repo/' }
And paste the following under dependencies.
Code:
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
8. Open build.gradle file which is located under the project.dir > android > app directory. Configure following dependency.
Code:
implementation project(': react-native-agconnect-remoteconfig')
Also add apply plugin:
Code:
'com.huawei.agconnect'
in app build.gradle file.
9. Add the below lines under signingConfigs in app build.gradle.
Code:
release {
storeFile file(‘<keystore_filename.jks>’)
storePassword '<store_password>'
keyAlias '<alias key name>'
keyPassword '<alias_password>
}
10. Navigate to Project settings and download the agconnect-services. json file and paste it inside android > app
11. Add the following lines to the android/settings.gradle file in your project.
include :
Code:
'react-native-agconnect-remoteconfig'
Code:
project(':react-native-agconnect-remoteconfig').projectDir = new File(rootProject.projectDir,'../node_modules/@react-native-agconnect/remoteconfig/android')
Development Process
Open AGC, choose Growing > Remote Configuration, then click Enable now.
Now, we have to set the Parameters and Conditions.
-Parameter management is used to define key-value parameters. These parameters are used to configure the value in application.
-Condition management is used to define rules and if that rule criteria is met, value can be configured in the application. These rules are used based on App version, OS version, language, country, region etc.
1. Navigate to Growing > Remote Configuration and Click on Condition management tab and then click Add condition.
2. In the displayed dialog box, set Condition.
3. Click the Parameter management tab and then click Add parameter.
4. Enter a parameter name in Default parameter name based on your design. Enter a default value for the parameter in Default value.
5. After the parameters are added, click Release.
Code Implementation
Setting In-app Default Parameter Values
Call AGCRemoteConfig.applyDefault to set the default parameter values that will be displayed.
Code:
let map = new Map();
map.set("Know_more", ‘’);
AGCRemoteConfig.applyDefault(map);
Fetch the values from Cloud
Call AGCRemoteConfig.fetch(intervalSeconds) API to fetch the latest parameter values from the cloud and then call the AGCRemoteConfig.applyLastFetched API to make the values take effect.
Code:
AGCRemoteConfig.fetch(0)
.then(() => {
console.log("fetch result success");
AGCRemoteConfig.applyLastFetched();
})
Source of a Parameter Value
You can call AGCRemoteConfig.getSource to obtain the source of a parameter value to determine whether the parameter is a local one or is fetched.
Code:
AGCRemoteConfig.getSource("key").then((source) => {
//success
});
There are three types of source. STATIC, DEFAULT and REMOTE.
Get the Parameter values
After default parameter values are set or parameter values are fetched from cloud, you can call AGCRemoteConfig.getValue to obtain the parameter values through key values to use in your app.
Code:
AGCRemoteConfig.getValue("Know_more").then((data) => {
console.log("default_key_string ==" + (data));
});
Get all the Values
We can obtain all parameter values by calling getMergedAll API instead of key values. It will return all values obtained after the combination of the local values and values fetched from the cloud.
Code:
AGCRemoteConfig.getMergedAll().then((map) => {
map.forEach(function (value, key) {
console.log("key-->" + key + ",value-->" + value);
});
});
Resetting Parameter Values
We can clear all existing parameter values by calling clearAll API.
Code:
AGCRemoteConfig.clearAll();
For Example,
Code:
import * as React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import AGCRemoteConfig, { SOURCE} from '@react-native-agconnect/remoteconfig';
const Separator = () => {
return <View style={styles.separator} />;
}
export default function ConfigScreen() {
return (
<View style={{ marginTop: 30, paddingHorizontal: 20 }}>
<Button
Title= “Know_more "
onPress={() => {
//add your code here
}}
/>
<Separator />
Testing
Enter the below command to run the project.
Code:
react-native run-android
Run the below command in the android directory of the project to create the signed apk.
Code:
gradlew assembleRelease
Output
You can check every change in your log
Tips and Tricks
1. Set minSdkVersion to 19 or higher.
2. The default update interval of the AGCRemoteConfig.fetch API is 12 hours. You can call the intervalSeconds API to customize the fetch interval. Calling fetch again within the interval does not trigger data synchronization from the cloud.
3. Once you have set the Parameters for remote configuration in AGC console, don’t forget to click on release.
Conclusion
In this article, we have learned how to integrate HMS Remote Configuration in React native. Remote Configuration is very useful for delivering the right user experience to users. Remote Configuration service can be used in your application as per your requirement.
Reference
Remote Configuration: Documentation
Remote Configuration: Training Video

How to Improve the Resource Download Speed for Mobile Games

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
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:
Code:
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:
Code:
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.
1. 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.)
Code:
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);
}
}
2. Initialize the global upload manager class UploadManager.
Code:
UploadManager upManager = (UploadManager) new UploadManager
.Builder("uploadManager")
.build(context);
3. Construct a request object. In the sample code, the file1 and file2 files are used as examples.
Code:
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());
}
4. Create the request callback object FileUploadCallback.
Code:
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);
}
}
};
5. 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.
Code:
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());
}
6. 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.​
7. 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.
1. 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.)
Code:
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);
}
}
2. Initialize the global download manager class DownloadManager.
Code:
DownloadManager downloadManager = new DownloadManager.Builder("downloadManager")
.build(context);
3. Construct a request object.
Code:
// 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();
4. Create the request callback object FileRequestCallback.
Code:
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);
}
}
};
5. 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.
Code:
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());
}
6. 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.​
7. 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.

Categories

Resources