React Native混合原生开发之集成RN到原生项目与RN首屏白屏优化
如何集成React Native到Android原生项目
可新建一个原生项目MyProject模拟原生的老项目,与一个新的React Native项目 RnProject,进行比对。
比对文件:app/build.gradle
和AndroidManifest.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全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论