美文网首页
原生项目中引入ReactNative介绍篇

原生项目中引入ReactNative介绍篇

作者: 展翅高飞鹏程万里 | 来源:发表于2020-10-25 23:54 被阅读0次

原生项目中引入ReactNative介绍篇

工程目录结构介绍

项目结构介绍

  • MyDemon
    • demon_android
    • demon_rn

叙述

工程名称:MyDemon ,包含两个子项目:安卓(demon_android), RN(demon_rn)。

强调:一般网文介绍的RN混编项目目录结构

  • RN项目名称
    • android文件夹
    • ios文件夹
    • node_module
    • ....
    • index.js
    • package-lock.json
    • package.json

这种项目结构适用于以RN界面为核心,原生为辅的移动应用,最上面文章说的项目结构适用于以原生界面为核心,RN为辅的移动应用。所以各位看官还需弄清楚定位,如果不适还请酌情观看,当然也有可借鉴的地方,谢谢赏脸。

接入过程

参考博客或者文档链接

ReactNative中文官方文档

迁移Androidx的文档介绍

Androidx与Support库之间的类映射

AndroidX 版本

此处接入的核心库版本:androidx.appcompact:appcompact:1.0.2, com.facebook.react:react-native:0.63.3

安卓项目配置(demon_android)

app的build.gradle文件配置

def useIntJsc = false

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
    
    //需要加载的react_native库
    implementation('com.facebook.react:react-native:+')
    
    //此处出现了herm需要加载的so库,但找不到的异常的情况
    if (useIntJsc) {
        implementation('org.webkit:android-jsc-intl:+')
    } else {
        implementation('org.webkit:android-jsc:+')
    }

}

app的AndroidMainifest.xml的文件配置

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wnp.hybrid">

    <application android:networkSecurityConfig="@xml/network_security_config">

        <activity
            android:name="com.wnp.hybrid.rn.MyReactActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar" />

        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

    </application>
</manifest>

app的MyReactActivity配置

package com.wnp.hybrid.rn;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;

import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;

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.JSBundleLoader;
import com.facebook.react.bridge.JSBundleLoaderDelegate;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.shell.MainReactPackage;
import com.wnp.hybrid.BuildConfig;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

/**
 * @Author : xiaowei
 * @Date 2020/10/23 7:26
 */
public class MyReactActivity extends FragmentActivity implements DefaultHardwareBackBtnHandler {
    /**
     * 以下两个变量作为传递数据到RN的key, so需要与RN团队协调传值key的规则
     */
    private static final String ARGS_JSON = "args_json";
    private static final String ROUTE_PATH = "route_path";

    //设置开发者开发信息
    private static final String DEVELOPER_HOST = "192.168.0.110:8081";
    private static final String DEV_URL = "http://" + DEVELOPER_HOST + "/index.bundle?platform=android";

    public static class Launcher {
        final static String ROUTE = "route";
        final static String ARGS = "args";

        public static void launch(Context packageContext, String routePath, String argsJson) {
            Intent intent = new Intent(packageContext, MyReactActivity.class);
            intent.putExtra(ROUTE, routePath);
            intent.putExtra(ARGS, argsJson);
            packageContext.startActivity(intent);
        }
    }


    /**
     * react—native 视图容器
     */
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);
        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder();
        builder.setApplication(getApplication())
                .setCurrentActivity(this)
                .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index")
                .addPackages(getPackages())
                .setUseDeveloperSupport(getUseDeveloperSupport())
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();


        builder.setJSBundleLoader(new JSBundleLoader() {
            @Override
            public String loadScript(JSBundleLoaderDelegate delegate) {
                String path = null;
                if (getUseDeveloperSupport()) {
                    //开发者模式
                    path = DEV_URL;
                    delegate.setSourceURLs(null, DEV_URL);
                } else {
                    //正式环境模式
                    path = "file://android_asset/xxxBundle";
                    delegate.loadScriptFromAssets(getAssets(), "file://android_asset/xxxBundle", true);
                }
                return path;
            }
        });


        mReactRootView.startReactApplication(mReactInstanceManager = builder.build(), getMainComponentName(), getLaunchOptions());
        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }

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

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

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

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @androidx.annotation.Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        mReactInstanceManager.onActivityResult(this, requestCode, resultCode, data);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (getUseDeveloperSupport()
                && keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
            event.startTracking();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        return super.onKeyUp(keyCode, event);
    }

    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
        return super.onKeyLongPress(keyCode, event);
    }

    @Override
    public void onBackPressed() {
        mReactInstanceManager.onBackPressed();
    }

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

    /**
     * 返回主Component注册的名字
     *
     * @return
     */
    protected String getMainComponentName() {
        return "react-example";//需要与index.js文件第一个注册的Component名字相同
    }

    /**
     * 用于传递数据给MainComponent
     * 一般传递的数据包含:
     * 1.routePath -- 页面的路由路径
     * 2.argsJson -- 传递给页面的数据
     */
    @Nullable
    protected Bundle getLaunchOptions() {
        Bundle bundle = new Bundle();
        bundle.putString(ROUTE_PATH, getIntent().getStringExtra(Launcher.ROUTE));
        bundle.putString(ARGS_JSON, getIntent().getStringExtra(Launcher.ARGS));
        return bundle;
    }

    /**
     * 作用:用于在开发期间,产生的错误及时显示在界面上
     *
     * @return
     */
    protected boolean getUseDeveloperSupport() {
        return BuildConfig.DEBUG;
    }


    protected List<ReactPackage> getPackages() {
        List<ReactPackage> list = new ArrayList<>();
        list.add(new MainReactPackage());
        return list;
    }
}

project的build.gradle配置

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.31'

    repositories {
        maven {
            url "$rootDir/../demo-rn/node_modules/react-native/android"
        }
        maven {
            // Android JSC is installed from npm
            url("$rootDir/../demo-rn/node_modules/jsc-android/dist")
        }
        google()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        maven {
            url "$rootDir/../demo-rn/node_modules/react-native/android"
        }
        maven {
            // Android JSC is installed from npm
            url("$rootDir/../demo-rn/node_modules/jsc-android/dist")
        }
        google()
        jcenter()
        mavenCentral()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

RN项目配置(demon_rn)

创建package.json

{
  "name": "MyDemo",
  "version": "1.0.0",
  "description": "react_native",
  "main": "index.js",
  "scripts": {
    //注意:需另外加上
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "xiaowei",
  "license": "ISC",
  "dependencies": {
    "react": "^17.0.1",
    "react-native": "^0.63.3"
  }
}

创建index.js, 界面显示HelloWorld文本

'use strict';

import React from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

class HelloWorld extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.hello}>Hello, World</Text>
      </View>
    )
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});

AppRegistry.registerComponent('react-example', () => HelloWorld);

项目执行流程 —— 关键执行命令

执行命令: 在demon_rn文件夹目录下,执行'yarn add react-native',等待数分钟生成一个node_module文件夹用于提供依赖

启动node.js服务器,执行命令:'react-native start'

编译打包demon_android项目并打包生成APK安装到手机上,跳转到MyReactActivity, 就会正常显示HelloWorld文本

接入流程注意点

基于以原生为主,混编为辅的项目结构,使得开发效率得到最大的提升,在研究一番之后做出如下调整,(文档未说明)

builder.setJSBundleLoader(new JSBundleLoader() {
            @Override
            public String loadScript(JSBundleLoaderDelegate delegate) {
                String path = null;
                if (getUseDeveloperSupport()) {
                    //开发者模式
                    path = DEV_URL;
                    delegate.setSourceURLs(null, DEV_URL);
                } else {
                    //正式环境模式
                    path = "file://android_asset/xxxBundle";
                    delegate.loadScriptFromAssets(getAssets(), "file://android_asset/xxxBundle", true);
                }
                return path;
            }
        });

上面的代码用了两种模式加载RN的代码.模式一:在开发模式下,直接使用远程服务器调试界面代码.模式二:在生产环境下,使用Asset文件夹下的android_bundle包加载RN代码,当然可以用以任何路径下文件的形式,提供框架加载RN代码

多种项目目录结构介绍,对应git的使用

基于第一种项目结构,git使用
因为RN与原生项目相互独立,不互相影响,那么git就按常规使用即可

基于第二种的项目结构,git使用
因为RN项目包含原生项目,相互影响,那么git在使用过程中需要注意:
1.如果希望原生项目与RN项目分开提交git记录,那么需要用到'git submodule init' 命令,用于初始化原生的项目,目的使得各自的代码提交记录分开。
2.如果不在乎原生项目与RN项目git记录混为一起,那么可忽略上面第一点

相关文章

网友评论

      本文标题:原生项目中引入ReactNative介绍篇

      本文链接:https://www.haomeiwen.com/subject/hqpomktx.html