{{announcement.body}}
{{announcement.title}}

React Native Made Easy Ep. 4 — Native Bridge (Feat. Scan Kit) Pt. 3

DZone 's Guide to

React Native Made Easy Ep. 4 — Native Bridge (Feat. Scan Kit) Pt. 3

We will walk you through how to create an android native bridge to connect your RN app with HMS Kits, and Scan Kit will be used as an example here.

· Web Dev Zone ·
Free Resource

Introduction

React Native is a convenient tool for cross-platform development, and though it has become more and more powerful through the updates, there are limits to it, for example, its capability to interact with and using the native components. Bridging native code with Javascript is one of the most popular and effective ways to solve the problem. Best of both worlds!

Currently, not all HMS Kits has official RN support yet, this article will walk you through how to create an android native bridge to connect your RN app with HMS Kits, and Scan Kit will be used as an example here.

The tutorial is based on https://github.com/clementf-hw/rn_integration_demo/tree/4b2262aa2110041f80cb41ebd7caa1590a48528a, you can find more details about the sample project in this article: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201230857831870061&fid=0101187876626530001.

Prerequisites

  • Basic Android development
  • Basic React to Native development
  • These areas have been covered immensely already on RN’s official site, this forum, and other sources
  • HMS properly configured
  • You can also reference the above article for this matter
  • Major dependencies
  • RN Version: 0.62.2 (released on 9th April 2020)
  • Gradle Version: 5.6.4
  • Gradle Plugin Version: 3.6.1

This tutorial is broken into three parts:

Pt. 1: Create a simple native UI component as intro and warm-up

Pt. 2: Bridging HMS Scan Kit into React Native

Pt. 3: Make Scan Kit into a stand-alone React Native Module that you can import into other projects or even upload to npm.

Making your Scan Kit Module

It may seem a lot of work to do to make a brand new native module on your own - but fear not, we have help! There are a couple of libraries available to help you initialize a module and do the mundane task for you:

https://github.com/brodybits/create-react-native-module - endorsed by and listed on official RN document

https://github.com/react-native-community/bob - community-supported tool

https://github.com/brodybits/react-native-module-init - based on create-react-native-module, simple to use and interactive. We'd use this one in this tutorial.

react-native-module-init

Open command prompt/terminal to install it

  1. yarn add react-native-module-init

Run it: npx react-native-module-init

And an empty module is created!

At this point, we have already done the heavy lifting in part 2, so now we'd just have to copy & paste the codes from our older ReactNativeHmsScanModule.java and ReactNativeHmsScanPackage.java

They should look like this now:

ReactNativeHmsScanModule.java

Java
 




x
82


 
1
package com.demo.cfhw;
2
import android.app.Activity;
3
import android.content.Intent;
4
import android.text.TextUtils;
5
 
          
6
import androidx.annotation.NonNull;
7
 
          
8
import com.facebook.react.bridge.ActivityEventListener;
9
import com.facebook.react.bridge.BaseActivityEventListener;
10
import com.facebook.react.bridge.Promise;
11
import com.facebook.react.bridge.ReactApplicationContext;
12
import com.facebook.react.bridge.ReactContextBaseJavaModule;
13
import com.facebook.react.bridge.ReactMethod;
14
import com.huawei.hms.hmsscankit.ScanUtil;
15
import com.huawei.hms.ml.scan.HmsScan;
16
import com.huawei.hms.ml.scan.HmsScanAnalyzerOptions;
17
 
          
18
public class ReactNativeHmsScanModule extends ReactContextBaseJavaModule {
19
    private static final String REACT_CLASS = "ReactNativeHmsScan";
20
    private static ReactApplicationContext reactContext;
21
    private Promise mScannerPromise;
22
    private static final int REQUEST_CODE_SCAN = 567;
23
    private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
24
    private static final String E_SCANNER_CANCELLED = "E_SCANNER_CANCELLED";
25
    private static final String E_FAILED_TO_SHOW_SCANNER = "E_FAILED_TO_SHOW_SCANNER";
26
    private static final String E_INVALID_CODE = "E_INVALID_CODE";
27
 
          
28
    public ReactNativeHmsScanModule(ReactApplicationContext context) {
29
        super(context);
30
        reactContext = context;
31
        reactContext.addActivityEventListener(mActivityEventListener);
32
    }
33
 
          
34
    @NonNull
35
    @Override
36
    public String getName() {
37
        return REACT_CLASS;
38
    }
39
 
          
40
    private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
41
        @Override
42
        public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
43
            if (requestCode == REQUEST_CODE_SCAN) {
44
                if (mScannerPromise != null) {
45
                    if (resultCode == Activity.RESULT_CANCELED) {
46
                        mScannerPromise.reject(E_SCANNER_CANCELLED, "Scanner was cancelled");
47
                    } else if (resultCode == Activity.RESULT_OK) {
48
                        Object obj = intent.getParcelableExtra(ScanUtil.RESULT);
49
                        if (obj instanceof HmsScan) {
50
                            if (!TextUtils.isEmpty(((HmsScan) obj).getOriginalValue())) {
51
                                mScannerPromise.resolve(((HmsScan) obj).getOriginalValue().toString());
52
                            } else {
53
                                mScannerPromise.reject(E_INVALID_CODE, "Invalid Code");
54
                            }
55
                            return;
56
                        }
57
                    }
58
                }
59
            }
60
        }
61
    };
62
 
          
63
    @ReactMethod
64
    public void startScan(final Promise promise) {
65
        Activity currentActivity = getCurrentActivity();
66
 
          
67
        if (currentActivity == null) {
68
            promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
69
            return;
70
        }
71
 
          
72
        // Store the promise to resolve/reject when picker returns data
73
        mScannerPromise = promise;
74
 
          
75
        try {
76
            ScanUtil.startScan(currentActivity, REQUEST_CODE_SCAN, new HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.ALL_SCAN_TYPE).create());
77
        } catch (Exception e) {
78
            mScannerPromise.reject(E_FAILED_TO_SHOW_SCANNER, e);
79
            mScannerPromise = null;
80
        }
81
    }
82
}



ReactNativeHmsScanPackage.java

Java
 




xxxxxxxxxx
1
22


1
packagecom.demo.cfhw;
2
 
          
3
importjava.util.Arrays;
4
importjava.util.Collections;
5
importjava.util.List;
6
 
          
7
importcom.facebook.react.ReactPackage;
8
importcom.facebook.react.bridge.NativeModule;
9
importcom.facebook.react.bridge.ReactApplicationContext;
10
importcom.facebook.react.uimanager.ViewManager;
11
 
          
12
publicclassReactNativeHmsScanPackage implementsReactPackage {
13
    @Override
14
    publicList<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
15
        returnArrays.<NativeModule>asList(newReactNativeHmsScanModule(reactContext));
16
    }
17
 
          
18
    @Override
19
    publicList<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
20
        returnCollections.emptyList();
21
    }
22
}



Also, add the Gradle stuff back to the new module's build.gradle

Java
 




xxxxxxxxxx
1
40


 
1
...
2
buildscript {    
3
   // The Android Gradle plugin is only required when opening the android folder stand-alone.    
4
   // This avoids unnecessary downloads and potential conflicts when the library is included as a    
5
   // module dependency in an application project.    
6
   // ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies    
7
   if (project == rootProject) {    
8
       repositories {    
9
           google()    
10
           jcenter()    
11
           maven {url 'http://developer.huawei.com/repo/'}    
12
       }    
13
       dependencies {    
14
           classpath 'com.android.tools.build:gradle:3.4.1'    
15
           classpath 'com.huawei.agconnect:agcp:1.2.1.301'    
16
       }    
17
   }    
18
}    
19
...
20
 
          
21
repositories {
22
    // ref: https://www.baeldung.com/maven-local-repository
23
    mavenLocal()
24
    maven {
25
        // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
26
        url "$rootDir/../node_modules/react-native/android"
27
    }
28
    maven {
29
        // Android JSC is installed from npm
30
        url "$rootDir/../node_modules/jsc-android/dist"
31
    }
32
    google()
33
    jcenter()
34
    maven {url 'http://developer.huawei.com/repo/'}
35
}
36
 
          
37
dependencies {
38
    //noinspection GradleDynamicVersion
39
    implementation 'com.facebook.react:react-native:+'  // From node_modules
40
    implementation 'com.huawei.hms:scanplus:1.1.3.300'



Now the module is ready!

If you are following through from part 2, follow these steps to remove the old one

Remove from {PROJECT_DIR}\android\app\build.gradle

  • implementation 'com.huawei.hms:scanplus:1.1.3.300

Remove the old work from {PROJECT_DIR}\android\app\src\main\java\com\{package dir}

\ReactNativeHmsScanModule.java

\ReactNativeHmsScanPackage.java

Remove this from MainApplication.java

importcom.cfdemo.d001rn.ReactNativeHmsScanPackage
...
packages.add(newMyReactNativePackage());

This is the only step you need to take if you don't want to delete the files just yet/test the native module. Move on if you want to remove all traces of the old one.

Remove from RN side:

const RNHMSScan = NativeModules.ReactNativeHmsScan

Now if you run your app it would cause error cause RNHMSScan can no longer be found

Using the native module

Now let's start importing our new module

(assuming that you created the module in the root of your project) yarn add "./react-native-HMS-scan"

Note: import of AppGallery's plugin etc are still required in your base project's {Project_Dir}\android\build.gradle

Note that due to the Autolinking feature starting from RN0.60.0 you no longer need to add its package in MainApplication.java, it is already taken care of when you build the app.

Add in RN side

  • import ReactNativeHmsScan from 'react-native-hms-scan'

Replace 

  • const data = await RNHMSScan.startScan()

with

  • const data = await ReactNativeHmsScan.startScan()

Note: In fact, you can name the module RNHMSScan when initiating and skip this step, here this is only for clarity purpose so the 2 modules can be distinguished by names easily.

And done! Your independent native module is up and running!

For the next step, you can keep it a local dependency, or upload it to https://www.npmjs.com/, it's another story. As mentioned, the heavy lifting is already done when we developed the native code, making it an independent module isn't as hard a job, but is very useful in terms of code re-use and management. If you want to see a complete demo, you can refer to https://github.com/clementf-hw/rn_integration_demo. If you want to use the HMS Scan Kit in RN directly, feel free to clone and use this repo: https://github.com/clementf-hw/react-native-hms-scan. 

The 3-part tutorial on how to maneuver between RN and native Android is finished, if you have any questions or suggestions, please don't hesitate to leave a comment here or raise an issue on Github.

Topics:
android, integration, java, mobile, opensource, web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}