The background,

First of all, our RN project needs to be put into the app of other teams, and other teams don’t want our code to invade and increase the development workload. Therefore, we considered to package iOS RN code into framework files and the function of loading our own bundle files in it. The App team members only need to call our initialization method to load the bundle file. The Android side is packaged as an AAR file, and the way we load the bundle file is also wrapped in it. Android partner also calls our initialization implementation to load our RN project. The overall process is shown in the figure below:

Second, SDK development

This program can be searched, iQiyi, Baidu, but others have no open source code. I can only cross the river by feeling my way.

1. Android

Extract relevant files from RN projects and put them in the module(Library) newly created by RN projects, and then package aar files through Module (Library), and finally provide them directly to the general integration APP.

The generation steps are as follows:

(1) Create Module (Library)

(2) Package the generated file of RN and copy it to the corresponding directory

(3) Add RN core dependencies to project dependencies

(4) Modify the engineering code

Here we focus on modifying the project code part:

A. If you look at the Android code generated by RN scaffolding, you’ll see that

The Android project must have an Application, which is required to inherit the ReactActivity class, otherwise the code will report an error. But we inherit from someone else’s App, we can’t change the Application so we load our RN pages the old-fashioned way. The code is as follows:

package com.example.ehrbunny;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.facebook.react.BuildConfig;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactInstanceManagerBuilder;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.Promise;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionAwareActivity;
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.shell.MainReactPackage;
import com.reactnative.ivpusic.imagepicker.PickerPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.swmansion.reanimated.ReanimatedPackage;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;

import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;


public class MyReactActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler.PermissionAwareActivity {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    public static Promise rnPromise;
    private PermissionListener permissionListener;

    @Nullable
    protected String getMainComponentName(a) {
        return null;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
        if(getSupportActionBar() ! =null) {
            getSupportActionBar().hide();
        }
        loadBundleFromFilePath("index.android.bundle"."");
    }



    private void loadBundleFromFilePath(String bundleFile, String moduleName) {
        mReactRootView = null;
        mReactRootView = new RNGestureHandlerEnabledRootView(this);
        String bundlePath = "assets://index.android.bundle";
        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setCurrentActivity(this)
                .addPackages(getPackages())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .setJSBundleFile(bundlePath);
        mReactInstanceManager = builder.build();
        mReactRootView.startReactApplication(mReactInstanceManager, moduleName, null);
        setContentView(mReactRootView);
    }

    private List<ReactPackage> getPackages(a) {
        return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                new MyReactPackage(),
                new PickerPackage(),
                new RNGestureHandlerPackage(),
                new ReanimatedPackage()
        );
    }

    @TargetApi(Build.VERSION_CODES.M)
    public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
        permissionListener = listener;
        requestPermissions(permissions, requestCode);
    }

    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if(permissionListener ! =null && permissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
            permissionListener = null; }}@Override
    public void onPause(a) {
        super.onPause();
        if(mReactInstanceManager ! =null) { mReactInstanceManager.onHostPause(); }}@Override
    public void onResume(a) {
        super.onResume();
        if(mReactInstanceManager ! =null) {
            mReactInstanceManager.onHostResume(this.this); }}@Override
    protected void onDestroy(a) {
        super.onDestroy();
        if(mReactInstanceManager ! =null) {
            mReactInstanceManager.onHostDestroy(this); }}// Physical return event pass
    @Override
    public void onBackPressed(a) {
        super.onBackPressed();
        if(mReactInstanceManager ! =null) { mReactInstanceManager.onBackPressed(); }}@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode,resultCode,data);
        if(mReactInstanceManager ! =null) {
            mReactInstanceManager.onActivityResult(this,requestCode, resultCode, data); }}@Override
    public void invokeDefaultOnBackPressed(a) {
        super.onBackPressed(); }}Copy the code

At the time of inheritance AppCompatActivity this class must implement DefaultHardwareBackBtnHandler, PermissionAwareActivity these two classes, or some third party dependence is not effective, Such as physical return events passing messages, permissions, and so on.

B. Solve SDK native communication problems

We use the factory pattern in our code to create shared instances. Create an instance when loading the SDK and create instance methods. The SDK calls the instance methods where needed, natively listening for the state of the instance and returning values to the SDK. Specific code can not be pasted.

C. The SDK communicates with RN

   @ReactMethod
    public String sendMsg(String msg, Promise promise) {
        return "I'm a message from Android.";
    }
Copy the code

To this Android SDK is basically complete, can load RN pages, can communicate with native, can communicate with RN.

2. iOS

Compared to the Android SDK, iOS is more cumbersome.

A. First create a framework project

B. Extract the method used to load the bundle

C. Publish headers of methods that need to be exposed

Focus on the implementation of SDK:

(1) Load RN page

@param options NSDictionary @{@"userInfo": NSDictionary, @"debug":@YES} */ @interface ErRnView : UIView -(instancetype)initWithFrame:(CGRect)frame andOptions:(NSDictionary *) options; @endCopy the code

(2) SDK communicates with RN

RCT_EXPORT_METHOD(addEventOne:(NSString *)name){NSLog(@" NSString+NSString: %@", name); }Copy the code

(3) SDK and native communication

We use the delegate proxy, storing the delegate generated by the method call in the method, so that the SDK and the native can communicate with the delegate object at the same time. Source code also cannot paste. Reference: How to use delegate for one-to-many communication

Third, summary

Many source codes cannot be posted due to company restrictions. Encountered a lot of difficulties in the development, itself as a front-end development, native OC and Java code is not very familiar, while looking at the document, while shameless to ask others. Finally being able to implement the SDK is also a growth for myself.