ReactNative從零到完整項目-嵌入到安卓原生應用

2022-01-06 安卓巴士Android開發者門戶

項目連接:https://github.com/93Laer/HelloRN HelloRN

把React Native組件植入到Android應用中官方步驟:

首先,我有一句MMP,不知當講不當講,這是玩RN以來遇到的巨坑,當你看到這篇文章的時候,從開始完成這個項目到現在已經過去2天半了,我已經按照官方文檔集成在5次以上,已經瀏覽文章30篇以上,包括在facebook/react-native的issues看了很多相關的問題,最終走出來了

1.  首先當然要了解你要植入的React Native組件。
2.  在Android項目根目錄中使用npm來安裝`react-native` ,這樣同時會創建一個`node_modules/`的目錄。
3.  創建js文件,編寫React Native組件的js代碼。
4.  在`build.gradle`文件中添加`com.facebook.react:react-native:+`,以及一個指向`node_nodules/`目錄中的`react-native`預編譯庫的`maven`路徑。
5.  創建一個React Native專屬的`Activity`,在其中再創建`ReactRootView`。
6.  啟動React Native的Packager服務,運行應用。
7.  根據需要添加更多React Native的組件。
8.  在真機上[運行](https://reactnative.cn/docs/0.40/running-on-device-android.html)、[調試](https://reactnative.cn/docs/0.40/debugging.html)。
9.  [打包](https://reactnative.cn/docs/0.40/signed-apk-android.html)。
10.  發布應用,升職加薪,走向人生巔峰!

算然官網給出了方法步驟,但是文檔過於簡單,而且還有很多巨坑,所以本文還是有一定價值的,當然在上面的步驟中有一些我們沒必要關注,但是主要步驟還是有的,接下來就按照這個步驟去完成把RN嵌入到android原生項目中

把React Native組件植入到Android應用第一步:引入react-native

在androidstudio的Terminal窗口中輸入 npm init,接著會提示你輸入一些東西(除了項目名字其他都可直接回車使用默認值),如下圖

當我們輸完的時候將工程切換到project模式下,可以看到工程多了一個package.json的文件

{
"name": "hellorn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
},
"author": "賴天兵",
"license": "ISC"
}

看到這個文件有一種很熟悉但又陌生的感覺,但是它的作用都應該猜到了,這個和我們build.gradle中的配置是一樣的作用,其實就是配置工程的一些屬性

第二步:添加react和react_native模塊

在Terminal窗口中輸入:npm install --save react react-native並執行,然後就是靜靜的等待,如果有報錯,自己手動敲"--"這個符號,因為在不同的系統下「--」可能是不一樣的,完成後就可以看到工程多了一個node_modules模塊

第三步:在命令行中運行curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig(這步對Windows來說真的坑)

直接運行會提示你:'curl' 不是內部或外部命令,也不是可運行的程序

所以看來我的首先處理Windows運行curl命令的問題了

curl是利用URL語法在命令行方式下工作的開源文件傳輸工具。它被廣泛應用在Unix、多種Linux發行版中,並且有DOS和Win32、Win64下的移植版本。
所以首先要解決Windows下支持curl命令的問題Windows下安裝使用curl命令
提示有可能你找不到| curl-7.33.0-win64-ssl-sspi.zip

再提示一下當你按照上面Windows下安裝使用curl命令走到了下圖步驟時

這時候應該在命令窗口中輸入curl -v -X OPTIONS https://www.baidu.com/,這是文檔沒有說的,而是直接輸入到命令行裡了,第一次看如果不知道這個,那會很懵逼的
不過遺憾的是我按照上門的連接文檔一步步走下來還是無法在任何地方使用curl命令(我的電腦win10 64位)

所以我用了最簡單的方法,直接複製我們下載的curl.exe到工程根目錄,這樣在本工程的根目錄就能運行curl命令了

但是:真想爆粗口,還是生成不了.flowconfig文件,

所以真正最直接的解決方式來了:自己在項目的根目錄下創建.flowconfig文本文件,然後打開 .flowconfig連接複製文本到剛剛創建的文件中即可

走到這裡突然想感嘆一句,我饒了一大圈到底是為了啥

第三步:在package.json文件中的scripts裡面配置啟動腳本"start": "node node_modules/react-native/local-cli/cli.js start",

{
  "name": "hellorn",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "bundle-android": "react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false"
  },
  "author": "賴天兵",
  "license": "ISC",
  "dependencies": {
    "react": "^16.2.0",
    "react-native": "^0.53.3"
  }
}

其實在官方文檔中第三步已經完了,但是畢竟我是踏過了很多巨坑的,看了很多篇博客的我告訴你這步還沒完,照常理這裡還應該配置打包用index.android.js生成index.andriod.bundle的配置(你也可以嘗試不做,絕對會出現一些經典的bug,這裡就不提了,我會有一篇專門的文章記錄這些bug)

配置生成發布、打包時所需要bundle文件的配置

在很多博客中會這樣解決這個問題,像上面完整package.json中的scripts代碼一樣添加
, "bundle-android": "react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false"
這一句在官方文檔中沒有說明,但是最好加上(雖然我加上後,至少我這裡沒有卵用,據說是新版本的RN不支持自動生成打包所需的bundle文件了),但是這裡需要修改一下,很多博客都直接是
"bundle-android": "react-native bundle –platform android –dev false –entry-file index.android.js –bundle-output android/app/src/main/assets/index.android.bundle –sourcemap-output android/app/src/main/assets/index.android.map –assets-dest android/app/src/main/res/"
但是這是錯的,打包的時候我們都要根據自己項目目錄的結果做一些調整,不過在上面package.json中我已經做過調整了,但是還是無法自動完成打包bundle

解決:我們先在main下面創建asserts文件夾,然後進入工程根目錄打開cmd輸入
react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false(注意-output後面的參數根據自己的項目目錄結構來寫)

成功後的項目

你或許看過很多在安卓原始項目中嵌套RN,並按照他們步驟一步步完成了,但是最後就是看不到效果,這裡就是其中一個重要的原因

如果這步完成了,那麼恭喜你,你基本上能看到效果了,後面可能還要出bug,但是都是些容易解決的固定的了

第四步:在項目根目錄中創建index.android.js文件

在index.android.js中編寫我們的代碼

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

class HelloWorldApp extends React.Component {
  render() {
return (
  <View style={styles.container}>
<Text style={styles.hello}>Hello world! I am from ReactNattive!!</Text>
  </View>
)
  }
}
var styles = StyleSheet.create({
  container: {
flex: 1,
justifyContent: 'center',
  },
  hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
  },
});

AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);

提示:在本系列RN博客的第二篇創建HelloWorld的時候說過,registerComponent()註冊時名字必須和項目名字保持一致

第五步:添加ReactNative依賴

首先在APP的build.gradle中添加
dependencies { ... compile "com.facebook.react:react-native:+" // From node_modules. }

其次在工程的build.gradle中添加進入本地ReactNative倉庫的路徑,但是官方源文檔寫的路徑是
$rootDir/../node_modules/react-native/android

如果你直接複製使用,恭喜你你又入坑了
因為我們通過之前第二步添加的添加react和react_native模塊,默認是項目的根目錄下,而官方文檔給出的路徑其實是多了「../」所以這就是一個坑(說到這裡提一句,網上很多文章都是直接用的官方路徑,但是項目結構又和我的一樣,我真懷疑他們寫那個文章的時候只是copy別人文章,自己根本沒有成功,這也是很多人按照別人文章一步步到最後還是報錯的又一個原因),這裡路徑應該對應自己工程中module的路徑
我的工程目錄

所以我的路徑應該是

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

添加完成後記得同步以下哦,不過等待的時間過於漫長(最好開VPN,我是開了VPN才同步完成的),可以先往後繼續看文章

第六步:在清單文件中添加網絡請求權限,必須添加的

官方原話Next, make sure you have the Internet permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
以及調試需要用到的權限

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

第七步:在清單文件中註冊DevSettingsActivity,此步驟可以省略,功能就是重載JavaScript

如果您需要訪問DevSettingsActivity添加到您的AndroidManifest.xml:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
這只在開發伺服器重新加載JavaScript時才真正用於開發模式,因此,如果需要,可以在發布版本中將其剝離。

第八步:把之前項目自動創建的MainActivity中代碼改成如下代碼,但是注意當APP需要支持5.0以下的機型那麼需要使用com.android.support:appcompat包中的AppCompatActivity類,而不能直接使用Activity

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

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;


public class MainActivity 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.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        
        mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);

        setContentView(mReactRootView);
    }

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

接下來(這個步驟是可以跳過的),我們需要將一些活動生命周期回調傳遞給ReactInstanceManager:

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

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onPause();
        }
    }

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

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

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

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onDestroy();
        }
    }

注意:如果你直接copy官網中代碼的話,是會出錯的,因為ReactNative版本更新了,方法名字更改和安卓更加同步了

也是可跳過的)我們還需要將按鈕事件傳遞給React Native:

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

作用:這允許JavaScript控制用戶按下硬體後退按鈕時發生的情況(例如,實現導航)。當JavaScript不處理背按時,您的invokeDefaultOnBackPressed方法將被調用。默認情況下,這只是完成你的Activity。

最後(這個步驟也是可以跳過的),我們需要連接開發菜單。默認情況下,這是通過(憤怒)激發設備來激活的,但這在模擬器中並不是很有用。所以我們在按下硬體菜單按鈕時顯示它(Ctrl + M如果您使用的是Android Studio模擬器,請使用它):

 @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU &amp;&amp; mReactInstanceManager != null) {
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }

第九步(此步驟可以跳過):配置權限以便開發中的紅屏錯誤能正確顯示

如果您的應用定位到Android API level 23或更高版本,請確保您已overlay為開發版本啟用權限。你可以檢查它Settings.canDrawOverlays(this);。這在開發版本中是必需的,因為必須在所有其他窗口之上顯示原始開發錯誤。由於在API級別23中引入了新的權限系統,用戶需要批准它。這可以通過將以下代碼添加到onCreate()方法中的Activity文件中來實現。OVERLAY_PERMISSION_REQ_CODE是將負責將結果傳遞迴活動的類的欄位。

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);
    }
}

最後,onActivityResult()必須重寫該方法(如下面的代碼所示)以處理一致UX的權限Accepted或Denied。

@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)) {
                
            }
        }
    }
}

最後在明確下index.android.js和mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null)和package.json及項目名字,到底那幾個保持一致(我也是集成了5次以上,總結出來的,如果有不對請留言指出)

必須一樣的:工程名字(HelloRN)和AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);以及mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);三者必須一樣。
當你出現以下錯誤,基本就是這個原因了

而package.json中的"name": "hellorn",算然給我的感覺應該也和其他一樣才對,但是確實可以不一樣,而且當你在第一步的時候輸入項目名字,如果輸了大寫,還提示你必須小寫。而且我們在初始化的時候"main": "index.js",這一步默認是index.js,從欄位來看我們後面創建的index.android.js名字也應該叫「index.js」才對,畢竟package.json類似於我們的配置文件,但是我們沒這樣做,發現也沒有任何問題。

運行應用

npm start
或者:
react-native start

效果

當出現這個界面時別提多嗨皮了,哭去了

如果報錯

或是

解決:在app的build.gradle 中添加

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

喜歡請點讚,或是關注,後續將完善發布更多的文章,你的鼓勵就是我的動力(程式設計師最大的動力莫過於同行的鼓勵)

相關焦點

  • 分享 50 個完整的 React Native 項目
    可是如果叫《43 個完整》好像不太好聽吧?嘿嘿~ 不過我是每個月 15 號左右去更新的,所以下個月肯定就超過 50 個了。順便說下,最近買了一個的域名:http://www.marno.cn 。不過暫時還沒想好放什麼內容,就先用 Github Pages 搭了一個簡單的網站,連結到了我整理的 React Native 優秀開源項目大全上。
  • 入門 | 從Android到React Native開發
    react-native init 你的項目名字 來創建工程的,創建的工程會從網絡端,同步到你終端所在路徑的本地,生成一個和android project類似的項目,如下圖。註:有時候還需要運行react-native link 或 react-native link xxx,這是因為有些第三方庫是通過原生代碼加React Native實現的,通過這個命令,可以自動把相關的配置代碼,自動添加到android和ios工程中。
  • 現有Android項目引入ReactNative--九步大法
    為什麼寫這篇文章,因為很多時候我們是需要在原Android工程中添加ReactNative,而不是直接react-native init hello來創建工程,而且官網的說明不是很詳細,不是完全針對安卓的,所以本文的必要性不言而喻。新建Android原生工程,這裡就不詳細敘述了,如下圖:
  • React Native 與原生代碼混編
    原生代碼混編我們遇到的問題是:我們要在 React Native 項目當中添加原生實現的 IM 模塊(Leanchat-ios)。React Native 官方文檔給出了使用原生代碼擴展功能的方法:Native ModulesNative UI Components首先,IM 模塊是一個完整的功能模塊而不是一個簡單的 UI 組件,很多的功能代碼集中在各種 UIViewController 裡。
  • 從零到一:用ReactNative開發的第一個跨平臺app
    ": "16.0.0-alpha.6","react-native": "0.44.2","react-native-camera": "^0.6.0",(掃碼)"react-native-deprecated-custom-components": "^0.1.0","react-native-easy-toast": "^1.0.6",
  • React Native 已死?
    20 日晚,Airbnb 在 Medium 上發博文宣布,「由於許多技術上和組織上的問題,我們決定放棄 React Native,將所有精力投入到原生應用上。」那時,Airbnb 有原生的 iOS 和 Android 應用,在 Web 上則使用 React。由於有著豐富的 React 經驗,他們決定採用 React Native 來加速原生應用的開發。這次移植造成的影響就是帶來了大量的額外工作,而這個影響剛開始時並不明顯。他們花費了大量時間去研究如何編寫輔助功能(如原生橋梁、封裝代碼等)以便讓 JavaScript 支持已有的原生功能。
  • 用JavaScript開發移動原生應用,Facebook正式開源React Native!
    在經過前一天Messenger應用平臺、Parse物聯網開發者工具等驚喜的轟炸,Facebook於今天凌晨在F8開發者大會上正式開源了React Native。不過目前,只有iOS版,Android版還需要再等一段時間,這是最新的用JavaScript語言開發原生App的嘗試,其示例代碼相當簡潔,內置控制項也不少。
  • ReactNative學習資源大匯集
    native中文網,人工翻譯,官網完全同步) http://react-native.cn/docs/getting-started.htmlreact-native第一課 http://html-js.com/article/2783深入淺出 React Native:使用 JavaScript 構建原生應用http://zhuanlan.zhihu.com/FrontendMagazine
  • Facebook 發布 React Native for Android
    開發技術擴展到了 Google 的流行移動平臺。React Native 讓開發者使用 JavaScript 和 React 編寫應用,利用相同的核心代碼就可以創建 Web,iOS 和 Android 平臺的原生應用。React Native 的宗旨是,學習一次,高效編寫跨平臺原生應用。
  • 十大最受歡迎的 React Native 應用開發編輯器
    React-native-css 將有效的 CSS、SASS轉換為 CSS 的 Facebook 子集。react-native-snippets - 該包是 Atom和 Nuclide 的 React Native 片段。zenchat-snippets - 它是react-native、redux 和 ES6 的片段集合。
  • React Native 從入門到原理
    從之前的描述也能看出,它專注於 UI 部分,對應到 MVC 結構中就是 View 層。要想實現完整的 MVC 架構,還需要 Model 和 Controller 的結構。在前端開發時,我們可以採用 Flux 和 Redux 架構,它們並非框架(Library),而是和 MVC 一樣都是一種架構設計(Architecture)。
  • 最火移動端跨平臺方案盤點:React Native、weex、Flutter
    react native 用了 react 的設計模式,但UI渲染、動畫效果、網絡請求等均由原生端實現。開發者編寫的js代碼,通過 react native 的中間層轉化為原生控制項和操作,比ionic等跨平臺應用,大大提高了的用戶體驗。總結起來其實就是:React Native是利用 JS 來調用 Native 端的組件,從而實現相應的功能。
  • 5000字的React-native源碼解析
    yarn ios 如果yarn ios後無法看到Simulator有APP,使用xCode找到這個項目的ios目錄的.xcworkspace❝注意 0.60 版本之後的主項目文件是.xcworkspace,不是.xcodeproj。❞然後用xCode打開build,成功後模擬器就會出現APP,打開即可進入
  • 怎麼理解React Native的新架構?
    切換到以上架構圖的部分來看,Native Module 的作用就是打通了前端到原生端的 API 調用,前端代碼運行在 JSC 的環境中,採用 C++ 實現,為了打通到 native 調用,需要在運行前注入到 global
  • React Native開發基礎入門之搭建開發環境
    完整原生環境Follow these instructions if you need to build native code in your project.例如react-native init MyApp --version 0.44.3。注意版本號必須精確到兩個小數點。Windows 用戶請注意,請不要在某些權限敏感的目錄例如 System32 目錄中 init 項目!會有各種權限限制導致不能運行!
  • 推薦11 款 React Native 開源移動 UI 組件
    React Native 是近期 Facebook 基於 MIT 協議開源的原生移動應用開發框架,已經用於 Facebook 的生產環境。React Native 可以使用最近非常流行的 React.js 庫來開發 iOS 和 Android 原生 APP。
  • React Native實戰:配置和起步
    >文檔提到:react-native init AwesomeProject初始化一個項目,其中 AwesomeProject 是項目名字,這個隨意。但是不像 iOS,Android 開發平時更多是直接用真機進行開發和調試,如何運行部署到真機,下面會提到。運行命令:react-native run-android然後就會部署到模擬器,修改 index.android.js ,調出模擬器菜單鍵,選擇重新載入 js 即可看到變化。
  • React Native:從入門到原理
    從之前的描述也能看出,它專注於 UI 部分,對應到 MVC 結構中就是 View 層。要想實現完整的 MVC 架構,還需要 Model 和 Controller 的結構。在前端開發時,我們可以採用 Flux 和 Redux 架構,它們並非框架(Library),而是和 MVC 一樣都是一種架構設計(Architecture)。
  • 使用React Native Testing庫進行組件測試(案例詳解)
    為此,我們需要在項目終端中運行以下命令:npm install --save-dev @testing-library/react-native該庫有一個react-test-renderer的peerDependencies列表。對於測試人員,我們將在這裡使用 jest。
  • 我們用Flutter重寫了一個React Native應用
    雖然 JavaScript 最初是為 Web 開發而生,但到了今天,JavaScript 生態系統已經變得如此龐大,以至於很多地方都能看到它的身影。React Native 在運行時將動態 JavaScript 代碼編譯為原生視圖,其餘代碼則通過嵌在應用程式內部的虛擬機來運行。Dart 是一門由谷歌開發的通用程式語言。