React Native混合原生开发之集成RN到原生项目与RN首屏白屏优化

如何集成React Native到Android原生项目

可新建一个原生项目MyProject模拟原生的老项目,与一个新的React Native项目 RnProject,进行比对。

比对文件:app/build.gradleAndroidManifest.xml,将缺失的配置加入原生项目

(1)修改 app/build.gradle

增加依赖:compile "com.facebook.react:react-native:+" // From node_modules

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile "com.facebook.react:react-native:+"  // From node_modules
}

(2)修改 AndroidManifest.xml

在清单文件里添加:权限 和 注册新的 Activity

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

创建Activity继承于ReactActivity

创建准备集成 React Native 的 Activity,继承于 ReactActivity,并实现未实现的方法,一定记得在 AndroidManifest.xml 清单文件中进行注册。

public class RnActivity extends ReactActivity {
    @Override
    protected String getMainComponentName() {
        return "myproject";  //名称必须小写
    }

    @Override
    protected boolean getUseDeveloperSupport() {
        return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
                new MainReactPackage()
        );
    }
}
<activity
    android:name=".RnActivity"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
    >
</activity>

添加js依赖(React Native库)

在项目目录下(如MyProject下)执行命令:

npm init

该命令会创建一个package.json文件,其他选项都是默认即可

C:\Users\rnapp\MyProject>npm init  //执行指令
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (MyProject)
Sorry, name can no longer contain capital letters.
name: (MyProject) myproject  //输入项目名称(小写)
version: (1.0.0)
description: Android project to include React Native  //输入介绍
entry point: (index.js)
test command:
git repository:
keywords:
author: RNAPP.CC  //输入作者
license: (ISC)
About to write to C:\Users\yezhou\MyProject\package.json:

{
  "name": "myproject",
  "version": "1.0.0",
  "description": "Android project to include React Native",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "RNAPP.CC",
  "license": "ISC"
}

Is this ok? (yes)

C:\Users\yezhou\MyProject>

注意:Sorry, name can no longer contain capital letters. 表示项目名称不能含大写字母

修改生成package.json文件的scripts以及添加react和react native依赖

{
  "name": "myproject",
  "version": "1.0.0",
  "description": "Android project to include React Native",
  "main": "index.js",
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "react": "15.3.1",
    "react-native": "0.32.0"
  },
  "author": "RNAPP.CC",
  "license": "ISC"
}

然后在项目根目录下执行npm install安装依赖模块,当然也可以直接复制 React Native 项目目录下的node_modules

npm install

修改 build.gradle

增加repositories的配置,

repositories {
    mavenLocal()
    jcenter()
    maven {
        // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
        url "$rootDir/node_modules/react-native/android"
    }
}

特别注意:原生项目的 url 路径与 React Native 的 url 路径是不同的

注意:增加repositories的配置并同步之后ReactActivity里面的getPackages方法的Override报错

解决:在ReactApplication子类中实现getPackages方法

新建或修改Application入口类

public class MainApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        protected boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }
}
public class RnActivity extends ReactActivity {
    @Override
    protected String getMainComponentName() {
        return "myproject";
    }
}

注意在 AndroidManifest.xml 清单文件中进行声明

<application
    android:name=".MainApplication"

    <activity .../>
</application>

创建js文件

创建或复制:index.android.js

注意入口名称要与RnActivity中的getMainComponentName返回的名称及package.json中的项目名称一致。

name必须在三个地方一致:index.js、继承ReactActivity的Activity、package.json,并且都是小写的。

运行项目

首先开启服务器

npm start

最后编译运行原生 Android项目

注:如果有so文件需要配置:在defaultConfig { }里面添加

ndk {
    abiFilters "armeabi-v7a", "x86"
}

相关源码

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void goReactNative(View view) {
        Toast.makeText(this, "点击进入 React Native 页面", Toast.LENGTH_LONG).show();
        Intent intent = new Intent(this, RnActivity.class);
        startActivity(intent);
    }
}

index.android.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

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

class RNAPP extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        </Text>
        <Text style={styles.author}>
          Powered by RNAPP.CC
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  author: {
    textAlign: 'center',
    color: '#3BC1FF',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('myproject', () => RNAPP);

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="rnapp.cc.myproject.MainActivity">

    <Button
        android:onClick="goReactNative"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#3BC1FF"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:text="点击进入 React Native 页面"
        android:textAllCaps="false"
        android:textColor="#FFFFFF"
        />

</RelativeLayout>

React Native 首屏白屏优化处理:

白屏原因

一般优化速度问题,首先就是要找到时间分布,然后根据二八原则,先优化耗时最长的部分。在官方的 ReactActivity 中的 onCreate 方法中,最慢的就是这两行代码,占了90%以上的时间。

mReactRootView = createRootView();
mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      getMainComponentName(),
      getLaunchOptions());

这两行代码就是把 JsBundle 文件读入到内存中,并进行执行,然后初始化各个对象。

优化思路

内存换时间(在APP启动时候,就将mReactRootView初始化出来,并缓存起来,在使用的时候直接setContentView(mReactRootView),达到秒开。预加载,把JsBundle文件先加载到内存中)步骤如下:

创建缓存rootView管理器 RNCacheViewManager

注意:mRootView.startReactApplication(mManager, "myproject", null)使用的模块名称也必须与项目名称一致!

public class RNCacheViewManager {
    private static ReactRootView mRootView = null;
    private static ReactInstanceManager mManager = null;

    public static ReactRootView getmRootView() {
        return mRootView;
    }

    protected static ReactNativeHost getReactNativeHost(Activity activity) {
        return ((ReactApplication) activity.getApplication()).getReactNativeHost();
    }

    public static void init(Activity activity) {
        if (mManager == null) {
            mManager = getReactNativeHost(activity).getReactInstanceManager();
        }
        mRootView = new ReactRootView(activity);
        mRootView.startReactApplication(mManager, "myproject", null);
    }

    public static void onDestroy() {
        try {
            ViewParent parent = getmRootView().getParent();
            if (parent != null)
                ((ViewGroup) parent).removeView(getmRootView());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

重写ReactActivity类

创建抽象类BasicReactActivity继承Activity,实现接口DefaultHardwareBackBtnHandler PermissionAwareActivity

RnActivity的继承类由ReactActivity修改为BasicReactActivity

public class RnActivity extends BasicReactActivity {
    @Override
    protected String getMainComponentName() {
        return "myproject";
    }
}

在需要预加载的地方执行初始化操作

在即将进入React Native页面的前一个页面中初始化

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    RNCacheViewManager.init(this);  //预加载
}

导致问题

由于进行了预加载,目前已知的问题是调试窗口 Modal 无法显示 —— 因为 Modal 在 Android 的实现使用了 Dialog,而该 View 将创建 ReactRootView 的 context 作为参数传给了 Dialog,而不是实际运行时所在的 Activity context。查看源码可以验证(com.facebook.react.views.modal)。

作为规避方案,目前使用MutableContextWrapper进行 context 替换。

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/react-native-hybrid-native-development-integrate-rn-to-native-projects-and-optimize-rn-first-screen-with-white-screen/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
React Native混合原生开发之集成RN到原生项目与RN首屏白屏优化
如何集成React Native到Android原生项目 可新建一个原生项目MyProject模拟原生的老项目,与一个新的React Native项目 RnProject,进行比对。 比对文件:app/bu……
<<上一篇
下一篇>>
文章目录
关闭
目 录