android 原生app嵌入reactnative頁面

話說原生Android app嵌入reactnative頁面的網上資料也不少了,而且也不錯,但是自己照着做的時候還是問題多多,難道真的是坑太多嗎?摸索着最終還是實現了,唉,眼淚嘩嘩的,先記錄一下吧。

第一步讓我暈的就是目錄結構,看了官方文檔一直說項目根目錄,結果百度了一下根目錄,一哥們說是src的上級目錄就是app所在的目錄,感覺也是,結果就做下去了,結果就悲催了,折騰了一下午也沒搞定,然後看了江清清的文章,這把知道目錄了,要在項目的同級目錄裏面,也就是app的上級目錄,src的上上級目錄做。這把做下去歷經不知道多少劫難,成功啦,開心!但是。。。爲毛項目名稱一定要是Android啊,這不坑嗎,難道我以後的項目名字都有叫這個啊,這顯然不行,結果我新建了一個項目,名字隨便起的叫ReactNativeOne,結果就報錯了:Android project not found. Maybe run react-native android first?,這時候看了網上一哥們的解答,試了一下,確實可以,就是先用android stdio先運行app,首先我們的app是一個原生的,as啓動自然沒問題。點擊綠色小三角運行app啓動了,然後運行命令react-native start或者npm start,就可以了,然後跳轉RN頁面,設置ip和端口,重新加載一下就ok了。下面是demo的主要實現不步驟,和網上有很多雷同,就不一一給出鏈接了。

1、E:盤下新建目錄ReactNativeProject目錄,as新建工程ReactNativeOne,一個原生android就出來了,目錄如下:


然後cmd進入到ReactNativeProject目錄下面,執行npm init命令,提示輸入name的時候就輸入reactnativeproject,要小寫。其他的一路enter鍵下去就好了。然後再執行命令npm install --save react react-native,等待一會執行成功,目錄結構如下:


npm install --save react react-native運行完我這提示[email protected] requires a peer of [email protected] but none was installed,打開package.json看看,裏面react版本不是16這個,這時候是react-native和react版本不一致問題,一定要解決,否則packager服務起不來,解決辦法是輸入命令npm i -S [email protected]。再看看package裏面react版本就變了。接下來修改package.json文件,package.json裏面scripts替換"start": "node node_modules/react-native/local-cli/cli.js start"。然後上圖目錄裏面加入index.android.js文件,代碼如下:

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

class Blink extends Component {
  constructor(props) {
    super(props);
    this.state = { showText: true };

    // 每50毫秒對showText狀態做一次取反操作
    setInterval(() => {
      this.setState({ showText: !this.state.showText });
    }, 50);
  }

  render() {
    // 根據當前showText的值決定是否顯示text內容
    let display = this.state.showText ? this.props.text : ' ';
    return (
      <Text style={styles.bigblue,styles.red}>{display}</Text>
    );
  }
}

class BlinkApp extends Component {
  render() {
    return (
      <View>
        <Blink text='I love to blink' />
        <Blink text='Yes blinking is so great' />
        <Blink text='Why did they ever take this out of HTML' />
        <Blink text='Look at me look at me look at me' />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  bigblue: {
    color: 'blue',
    fontWeight: 'bold',
    fontSize: 30,
  },
  red: {
    color: 'red',
  },
});

AppRegistry.registerComponent('ReactNativeProject', () => BlinkApp);

目錄如下:


接下來修改as工程,打開MainActivity頁面,加入一個按鈕,然後新建一個ReactNativeActivity頁面,按鈕跳轉到該頁面。然後再建立工程,app裏面的build.gradle裏面導入compile "com.facebook.react:react-native:+" ,這個要在npm install --save react react-native命令後執行,如果順序反了就在as裏面無法正確導入react-native包,可能導入的是老版本的而不是你本地安裝的,這個地方要注意一下,否則as就報錯了。

gradle裏面最低sdk版本要改爲至少16,即minSdkVersion16,否則報錯。

然後還是在app裏面的build.gradle裏面的build.gradle裏面android{}加入如下代碼:

 configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
    }
我這最終代碼如下:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.example.user.reactnativeone"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
    }
}
不然會報如下錯誤:
Conflict with dependency 'com.google.code.findbugs:jsr305'. 
Resolved versions for app (3.0.0) and test app (2.0.1) differ. 
See http://g.co/androidstudio/app-test-app-conflict for details.

接下來AndroidManifest裏面加入權限:

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

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
和DevSettingsActivity聲明,不然到時候點擊Dev Setting就崩啦

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

最後就是修改DevSettingsActivity裏面的代碼了:

public class ReactNativeActivity extends Activity implements DefaultHardwareBackBtnHandler {

    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    public static int OVERLAY_PERMISSION_REQ_CODE = 1234;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeProject", null);
        setContentView(mReactRootView);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (!Settings.canDrawOverlays(this)) {
                    // SYSTEM_ALERT_WINDOW permission not granted...
                }
            }
        }
    }

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

    @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();
        }
    }
}
好了,這下基本上工作完事,準備跑起來吧。

但是...記得開篇說的注意事項了嗎,先跑as,模擬器運行處app後,然後在命令行下執行react-native start或者npm start命令。


模擬器跑出來app後進入android原生頁面


點擊按鈕進入react-native頁面,ctrl+M調出菜單,點擊Dev Setting設置ip和端口


最後as重新運行app就好了,這之前可能要重新運行nmp start命令,最終RN頁面如下,是一個帶有閃啊閃啊字的頁面,截圖個靜態的頁面:


結束啦。。。並沒有。。。

參考網上一個哥們文章,說的有道理,我們爲啥要把js和json文件放在工程外面呢,這不合理啊,他們屬於工程內部文件啊,那咱把他們移到工程裏面,目錄如下:


然後開啓packager服務的命令要修改成如下樣子了:


其他的都一樣了,至於node_modules文件夾爲啥不一起移過去,那樣據說會很卡...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章