現(xiàn)有原生iOS和安卓項(xiàng)目集成React-Native

一、創(chuàng)建工程
到自己創(chuàng)建項(xiàng)目的文件夾下輸入命令:

react-native init 項(xiàng)目名

當(dāng)然如果你想指定安裝react-native版本的話你也可以這樣
后面的0.57.1可以自定義成自己的版本

react-native init 項(xiàng)目名 --version 0.57.1

目錄結(jié)構(gòu)還這樣的


image.png

這里android和ios 文件夾你可以刪掉換成自己的現(xiàn)有iOS和安卓項(xiàng)目.
刪掉android和iOS文件夾后
添加React Native所需要的依賴
目錄結(jié)構(gòu)如下

RN項(xiàng)目
├── Android項(xiàng)目
├── iOS項(xiàng)目
├── package.json
├── node_modules
└── .gitignore

我們來(lái)先說(shuō)安卓怎么配置

第一步:配置maven

接下來(lái)我們需要為已經(jīng)存在的RN項(xiàng)目添加 React Native依賴,在RN項(xiàng)目/Android項(xiàng)目/app/build.gradle文件中添加如下

dependencies {
    compile 'com.android.support:appcompat-v7:+'
    ...
    compile "com.facebook.react:react-native:+" // From node_modules
}
image.png

然后,我們?yōu)镽N項(xiàng)目配置使用的本地React Native maven目錄,在RN項(xiàng)目/Android項(xiàng)目/build.gradle文件中添加如下代碼:

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

image.png

這里需要注意的是url "$rootDir/../node_modules/react-native/android"路徑的問(wèn)題
如果你是把package.json文件放在Android項(xiàng)目目錄下的話
也就是說(shuō)RN項(xiàng)目/Android項(xiàng)目/package.json
那么這里你要這么寫(xiě)提示:為確保你配置的目錄正確,可以通過(guò)在Android Studio中運(yùn)行Gradle sync 看是否有 “Failed to resolve: com.facebook.react:react-native:0.x.x” 的錯(cuò)誤出現(xiàn),沒(méi)有錯(cuò)誤則說(shuō)明配置正確,否則說(shuō)明配置路由有問(wèn)題。

url "$rootDir/node_modules/react-native/android

第二步:配置權(quán)限

接下來(lái)我們?yōu)锳PP運(yùn)行配置所需要的權(quán)限:檢查你項(xiàng)目中的AndroidManifest.xml文件中看是否有如下權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
另外,如果你需要用到RN的Dev Settings功能:

image.png

則需要在AndroidManifest.xml文件中添加如下代碼:

<activity android:name="com.facebook.react.devsupport.DevSettingsAc

第三步:指定要ndk需要兼容的架構(gòu)(重要)

Android不能同時(shí)加載多種架構(gòu)的so庫(kù),現(xiàn)在很多Android第三方sdks對(duì)abi的支持比較全,可能會(huì)包含armeabi, armeabi-v7a,x86, arm64-v8a,x86_64五種abi,如果不加限制直接引用會(huì)自動(dòng)編譯出支持5種abi的APK,而Android設(shè)備會(huì)從這些abi進(jìn)行中優(yōu)先選擇某一個(gè),比如:arm64-v8a,但如果其他sdk不支持這個(gè)架構(gòu)的abi的話就會(huì)出現(xiàn)crash。
在app/gradle 文件中添加如下代碼:
這地方也有個(gè)坑
NDK的架構(gòu)是有自己項(xiàng)目定義的,有些項(xiàng)目并沒(méi)有X86
所以這里一定要填寫(xiě)上,不然項(xiàng)目會(huì)直接崩潰 或者 模擬器無(wú)法啟動(dòng)RN 頁(yè)面

defaultConfig {
....?
    ndk {
        abiFilters "armeabi-v7a", "x86"
    }
}

上面這些Android就簡(jiǎn)單的配置完成了
然后修改RN項(xiàng)目的index.js文件
如果你項(xiàng)目比較老的話,那么這個(gè)文件將是index.ios.js和index.android.js請(qǐng)自行選擇

有些同學(xué)不會(huì)導(dǎo)入包裹
import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('App1', () => App);

App1就是以后你要跳轉(zhuǎn)到的RN 項(xiàng)目的頁(yè)面
另外,在上述代碼中我們引用了一個(gè)App.js文件

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

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          我是RN頁(yè)面
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
 });
為React Native創(chuàng)建一個(gè)Activity來(lái)作為容器

創(chuàng)建RNPageActivity
首先我們需要?jiǎng)?chuàng)建一個(gè)Activity來(lái)作為React Native的容器,

//有些同學(xué)不知道包名是什么這里我都直接導(dǎo)入復(fù)制進(jìn)來(lái)
import android.app.Activity;
import android.databinding.Bindable;
import android.os.Bundle;
import android.view.KeyEvent;

import com.facebook.react.ReactActivity;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
import com.lanto.goodfix.base.SimpleActivity;
public class RNPageActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
            .setApplication(getApplication())
            .setBundleAssetName("index.android.bundle")
            .setJSMainModulePath("index")
            .addPackage(new MainReactPackage())
            .setUseDeveloperSupport(BuildConfig.DEBUG)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build();
        // 這個(gè)"App1"名字一定要和我們?cè)趇ndex.js中注冊(cè)的名字保持一致AppRegistry.registerComponent()
        mReactRootView.startReactApplication(mReactInstanceManager, "App1", null);

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
}
//為ReactInstanceManager添加Activity的生命周期回調(diào)
//一個(gè) ReactInstanceManager可以被多個(gè)activities或fragments共享,
//所以我們需要在Activity的//生命周期中回調(diào)ReactInstanceManager的對(duì)于的方法。
@Override
protected void onPause() {
    super.onPause();

    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostPause(this);
    }
}

@Override
protected void onResume() {
    super.onResume();

    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostResume(this, this);
    }
}

@Override
public void onBackPressed() {
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onBackPressed();
    } else {
        super.onBackPressed();
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostDestroy(this);
    }
    if (mReactRootView != null) {
        mReactRootView.unmountReactApplication();
    }
}
@Override
public void invokeDefaultOnBackPressed() {
    super.onBackPressed();
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (getUseDeveloperSupport()) {
        if (keyCode == KeyEvent.KEYCODE_MENU) {//Ctrl + M 打開(kāi)RN開(kāi)發(fā)者菜單
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
    }
    return super.onKeyUp(keyCode, event);
}

參數(shù)說(shuō)明

setBundleAssetName:打包時(shí)放在assets目錄下的JS bundle包的名字,App release之后會(huì)從該目錄下加載JS bundle;
setJSMainModulePath:JS bundle中主入口的文件名,也就是我們上文中創(chuàng)建的那個(gè)index.js文件;
addPackage:向RN添加Native Moudle,在上述代碼中我們添加了new MainReactPackage()這個(gè)是必須的,另外,如果我們創(chuàng)建一些其他的Native Moudle也需要通過(guò)addPackage的方式將其注冊(cè)到RN中。需要指出的是RN除了這個(gè)方法外,也提供了一個(gè)addPackages方法用于批量向RN添加Native Moudle;
setUseDeveloperSupport:設(shè)置RN是否開(kāi)啟開(kāi)發(fā)者模式(debugging,reload,dev memu),比如我們常用開(kāi)發(fā)者彈框;
setInitialLifecycleState:通過(guò)這個(gè)方法來(lái)設(shè)置RN初始化時(shí)所處的生命周期狀態(tài),一般設(shè)置成LifecycleState.RESUMED就行,和下文講的Activity容器的生命周期狀態(tài)關(guān)聯(lián);
mReactRootView.startReactApplication:它的第一個(gè)參數(shù)是mReactInstanceManager,第二個(gè)參數(shù)是我們?cè)趇ndex.js中注冊(cè)的組件的名字,第三個(gè)參數(shù)接受一個(gè)Bundle來(lái)作為RN初始化時(shí)傳遞給JS的初始化數(shù)據(jù)
在中AndroidManifest.xml注冊(cè)一個(gè)RNPageActivity
<activity
    android:name=".RNPageActivity"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
    android:windowSoftInputMode="adjustResize"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
運(yùn)行React Native

找到你package.json的所在文件夾輸入
npm start
然后我們打開(kāi)AndroidStudio,點(diǎn)擊運(yùn)行按鈕或者通過(guò)快捷鍵Ctrl+R來(lái)將項(xiàng)目安裝到模擬器上:

image.png

添加更多React Native的組件

import { AppRegistry } from 'react-native';
import App from './App';
import App2 from './App2';

AppRegistry.registerComponent('App1', () => App);
AppRegistry.registerComponent('App2', () => App);
//這里可以添加多個(gè),只要對(duì)應(yīng)就可以了
//目前這種方法是可以自定義比較靈活的一種集成方式
//祝大家好運(yùn),到此安卓就集成完成了

下面再說(shuō)幾句
如果你是在模擬器上運(yùn)行,那么這樣就可以了
如果是真機(jī),你要這樣輸入
先安裝index.android.bundle

//先創(chuàng)建下面路徑的assets文件夾
react-native bundle --platform android --dev false --entry-file index.js --bundle-output 安卓項(xiàng)目名/app/src/main/assets/index.android.bundle --assets-dest app/src/main/res/

//一定要注意你的路徑

//然后終端輸入
adb reverse tcp:8081 tcp:8081
npm start

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^分割線^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

下面就是iOS項(xiàng)目的集成了

iOS項(xiàng)目我們采用pod來(lái)集成,相對(duì)簡(jiǎn)單,不會(huì)出現(xiàn)各種添加錯(cuò)誤的問(wèn)題

首先在pod Podfile文件中添加
這里需要主語(yǔ)rn_path 路徑問(wèn)題,寫(xiě)你自己的路徑

rn_path = '../node_modules/react-native'
    pod 'React', :path => '../node_modules/react-native', :subspecs => [
    'Core',
    'ART',
    'CxxBridge',
    'RCTActionSheet',
    'RCTGeolocation',
    'RCTImage',
    'RCTAnimation’,
    'RCTNetwork',
    'RCTPushNotification',
    'RCTSettings',
    'RCTText',
    'RCTVibration',
    'RCTWebSocket',
    'RCTLinkingIOS',
    'DevSupport'
    ]
    pod 'yoga', path: "#{rn_path}/ReactCommon/yoga"
    pod 'glog', :podspec => "#{rn_path}/third-party-podspecs/glog.podspec"
#
    # React Native third party dependencies podspecs
    pod 'DoubleConversion', :podspec => "#{rn_path}/third-party-podspecs/DoubleConversion.podspec"
    pod 'Folly', :podspec => "#{rn_path}/third-party-podspecs/Folly.podspec"

然后到podfile所在文件夾 再次
pod install

如果運(yùn)行起來(lái)后報(bào)這個(gè)錯(cuò)誤的話


image.png

請(qǐng)用以下解決
在podfile 文件中添加如下代碼

def change_lines_in_file(file_path, &change)
    print "Fixing #{file_path}...\n"
    
    contents = []
    
    file = File.open(file_path, 'r')
    file.each_line do | line |
        contents << line
    end
    file.close
    
    File.open(file_path, 'w') do |f|
        f.puts(change.call(contents))
    end
end

post_install do |installer|
    # https://github.com/facebook/yoga/issues/711#issuecomment-381098373
    change_lines_in_file('./Pods/Target Support Files/yoga/yoga-umbrella.h') do |lines|
        lines.reject do | line |
            [
            '#import "Utils.h"',
            '#import "YGLayout.h"',
            '#import "YGNode.h"',
            '#import "YGNodePrint.h"',
            '#import "YGStyle.h"',
            '#import "Yoga-internal.h"',
            ].include?(line.strip)
        end
    end
    
    # https://github.com/facebook/yoga/issues/711#issuecomment-374605785
    change_lines_in_file('../node_modules/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceSizeMeasureMode.h') do |lines|
        unless lines[27].include?("#ifdef __cplusplus")
            lines.insert(27, "#ifdef __cplusplus")
            lines.insert(34, "#endif")
        end
        lines
    end
    
    # https://github.com/facebook/react-native/issues/13198
    change_lines_in_file('../node_modules/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h') do |lines|
        lines.map { |line| line.include?("#import <RCTAnimation/RCTValueAnimatedNode.h>") ? '#import "RCTValueAnimatedNode.h"' : line }
    end
    
    # https://github.com/facebook/react-native/issues/16039
    change_lines_in_file('../node_modules/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m') do |lines|
        lines.map { |line| line.include?("#import <fishhook/fishhook.h>") ? '#import "fishhook.h"' : line }
    end
end

接下來(lái)你需要配置index.js文件 這里就省略了 同安卓那邊的,可以自己看
也就是這步

import { AppRegistry } from 'react-native';
import App from './App';
import App2 from './App2';

AppRegistry.registerComponent('App1', () => App);
AppRegistry.registerComponent('App2', () => App);

接下來(lái)在你需要跳轉(zhuǎn)的地方OC代碼里加入

 NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
        
        
        RCTRootView *rootView =
        
        [[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                    moduleName: @"QiXiu"
                             initialProperties:
         @{
           @"name" : @[
                   @{
                       @"name" : @"Alex",
                       @"value": @"42"
                       },
                   @{
                       @"name" : @"Joel",
                       @"value": @"10"
                       }
                   ]
           }
                                 launchOptions: nil];
        UIViewController *vc = [[SHCustomerViewController alloc] init];
        vc.view = rootView;
        //[self presentViewController:vc animated:YES completion:nil];
        [self.navigationController pushViewController:vc animated:nil];
        vc.title = @"我是RN頁(yè)面";

注意這行代碼[[RCTRootView alloc] initWithBundleURL: jsCodeLocation moduleName: @"App1" initialProperties:
這里的 App1 就是你配置的頁(yè)面

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評(píng)論 25 709
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,913評(píng)論 2 59
  • 期待已久的新課上線啦!解鎖React Native開(kāi)發(fā)新姿勢(shì),一網(wǎng)打盡React Native最新與最熱技術(shù),點(diǎn)我...
    CrazyCodeBoy閱讀 20,349評(píng)論 3 32
  • 表?yè)P(yáng)教育是家庭教育中常用的一種方法。表?yè)P(yáng)教育可以激發(fā)孩子不斷的進(jìn)步,而且對(duì)各個(gè)年齡階段的孩子都有積極作用。首...
    阿寶的育兒寶典閱讀 701評(píng)論 0 0
  • 7月15日世界人民的目光聚焦在俄羅斯足球世界杯的賽場(chǎng)上,同一時(shí)刻在新東方校園大禮堂里人頭攢動(dòng),2018年第15屆新...
    曹曹老師親子工作坊閱讀 252評(píng)論 0 1

友情鏈接更多精彩內(nèi)容