React native 問題/技巧記錄 [不定期更新]

[以下問題適用于 RN 6.0 以上版本,其他版本未測試,不定期更新]

方法數(shù)大于 65536


cannot fit requested classes in a single dex file
The number of method references in a .dex file cannot exceed 64K

嚴(yán)格來講,這是 android 的問題,RN 默認(rèn)設(shè)置觸發(fā)了這個(gè)錯(cuò)誤,可在 multidex 找到答案,簡單說一下

Android 5.0(API級別小于21)以下不支持多個(gè) dex (android 打包后的庫文件),而單個(gè) dex 默認(rèn)情況下最多只能有 65536 個(gè)方法,所以 app 的依賴較多的話,就會大于這個(gè)數(shù),就出現(xiàn)這個(gè)問題。而RN默認(rèn)為16(android/build.gradle 中的 minSdkVersion),【根據(jù) 更新記錄,RN 在 0.64 之后不再支持 API 16-20,默認(rèn) minSdkVersion 改為 21,所以不再需要做任何處理】,如果必須要兼容低版本 Android,則只能使用較低版本的 RN,解決方法如下

1: 不兼容 5.0 了,修改 minSdkVersion 版本為 21,該方案也許還要等幾年 (衰
2: 想辦法減少依賴,不現(xiàn)實(shí)
3: 做一些配置修改,具體見: multidex
4: 啟用R8壓縮,在RN中就是設(shè)置 android/app/build.gradleenableProguardInReleaseBuilds=true

啟用 R8 生成 apk 會特慢,所以建議是 debug 時(shí)使用方案1或方案3,release 嘗試使用方案4,但考慮到一般情況下,項(xiàng)目都是在做增量,所以哪怕是啟用R8優(yōu)化,總有一天會還是會超過 65536,還是老老實(shí)實(shí)用方案 3 吧??梢詫?android/app/build/outputs 目錄下生產(chǎn)的 apk 拖到 這個(gè)網(wǎng)站 查看總方法數(shù)

方案 3 在官方文檔中已經(jīng)寫的很清楚了,這里記錄一下,省的以后翻官方文檔了。RN 使用了 androidx,如果是沒有使用 andoridx 的項(xiàng)目, 會略有不同,請查閱官方文檔

  1. 修改 android/app/build.gradle
android {
    defaultConfig {
        multiDexEnabled true  // 開啟 multiDex 支持
    }
}
dependencies {
    implementation 'androidx.multidex:multidex:2.0.1' // 引入 multiDex 庫
}
  1. 修改 android/app/src/main/[project]/MainApplication.java
// 修改
import android.app.Application;
// 為
import androidx.multidex.MultiDexApplication;

// 修改
public class MainApplication extends Application implements ReactApplication {
// 為
public class MainApplication extends MultiDexApplication implements ReactApplication {

RN 開啟 R8 debug 問題


Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found or could not be created

該問題 答案,在 android/app/proguard-rules.pro 中添加

-keep class com.facebook.react.devsupport.** { *; }
-dontwarn com.facebook.react.devsupport.**

啟用 R8 優(yōu)化,你可能碰到的不止這一個(gè)問題,尤其是安裝插件多的時(shí)候,指不定哪個(gè)地方就暴露問題了。要特別小心的檢查,如果插件明說了需要添加哪些混淆規(guī)則,一定要加上

Expiring Daemon because JVM heap space is exhausted

開啟R8,在優(yōu)化混淆的編譯過程中需要內(nèi)存,默認(rèn)的不夠用就出現(xiàn)這個(gè)情況了,一個(gè)簡單的 答案

// 在 android/gradle.properties 添加
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8


// 在 android/app/build.gradle 添加
android {
    ...
    dexOptions {
       javaMaxHeapSize "3g"
    }
   ...
}

生成 apk 太大


默認(rèn)配置是生成了一個(gè)全平臺兼容的 apk,內(nèi)部包含了 arm/x86 等的 lib,只需配置 android/app/build.gradle

修改后,將為各平臺生成不同的 apk,不出意外,包體積大概為原來的 1/4

// 該值 改為 true
def enableSeparateBuildPerCPUArchitecture = true

android {
    splits {
        abi {
            ...
            // 若仍需要一個(gè)全平臺兼容apk,這里改為 true 即可
            universalApk false 
        }
    }
}

babel 優(yōu)化


RN 的編譯用的 babel,配置文件為項(xiàng)目根目錄的 babel.config.js,使用 react init project 載入的項(xiàng)目都會有這個(gè)。這與一般的 babel 項(xiàng)目沒什么不同,所以可以利用 babel 插件對 rn 項(xiàng)目進(jìn)行優(yōu)化。

提前備注:若配置 babel 插件后未生效,可嘗試

watchman watch-del-all
yarn start --reset-cache

一、自定義別名

對于安裝好的第三方應(yīng)用,可以很舒服的 import * from "moudule",但對于自己項(xiàng)目的源碼,引入路徑就成了可能就是這樣子

import helper from './../../utils/helper'

我們可以使用 babel 的 module-resolver 為了讓自己的代碼引入也更加清爽,使用方法比較簡單

安裝 yarn add babel-plugin-module-resolver

module.exports = {

  // 添加以下配置, 在 alias 中配置別名
  plugins: [
    ['module-resolver', {
      // 這里根據(jù)自己項(xiàng)目用的, 也可能是 .ts | .native | .tsx 等
      extensions: [".ios.js", ".android.js", ".js", ".json"],
      alias: {
        '@pages': './src/pages',
        '@res': './src/res',
        '@utils': './src/utils',
      },
    }],
  ],

};

弄好這個(gè),在項(xiàng)目中就可以 import helper from '@utils/helper', 嗯,舒服多了。

二、console 優(yōu)化

開發(fā)中常用 console debug 或 依賴的第三方組件也會有 console,這些在 product 模式下是影響性能的一個(gè)因素,并且還會增加最終打包 js bundle 的體積,增加 app 運(yùn)行時(shí)的內(nèi)存占用。

使用 remove-console 插件在編譯時(shí)移除 console 語句。

安裝 yarn add babel-plugin-transform-remove-console

module.exports = {

  // 添加以下配置
  plugins: [
    "transform-remove-console"
  ],

};

三、propTypes 優(yōu)化

具體原因可參見這個(gè) issue,去除 propTypes 可減小代碼體積,甚至可以提升性能。目前發(fā)現(xiàn)了 babel-plugin-transform-react-remove-prop-types 這個(gè)拓展,還沒來得及測試。

vscode 配置


  1. 不使用 vscode 開發(fā) java,感覺有點(diǎn)用但又不夠用,還是 android studio 好用,就在插件中找 java 擴(kuò)展,把他給禁用了,省的不小心點(diǎn)開個(gè) Java 文件他就自動(dòng)編譯,還編譯不好

android Studio 配置


  1. avd 模擬器默認(rèn)放到系統(tǒng)用戶目錄了,磁盤不夠用了,在 系統(tǒng)環(huán)境變量中添加 ANDROID_SDK_HOME 指定一個(gè)目錄,將會做為 avd 存儲目錄,原來的目錄可以刪除掉。

相冊訪問


1,react-native-cameraroll

官方組件,但新版 rn 已從核心中移除該插件,若仍需要,可手動(dòng)安裝

yarn add @react-native-community/cameraroll

該擴(kuò)展主要用于保存圖片,另外提供了一個(gè)無界面讀取圖片列表 api,若有心,其實(shí)可以用 jsx 做一個(gè)界面出來,但對于界面問題,其實(shí)有更好的選擇

2,react-native-image-picker

yarn add react-native-image-picker ( 詳細(xì) 安裝文檔 )

該擴(kuò)展自帶選擇圖片的UI界面,同時(shí)具有拍攝功能,可以用于 上傳或拍攝 的應(yīng)用場景

3,react-native-image-crop-picker

yarn add react-native-image-crop-picker

該擴(kuò)展與2類似,但在提供了 UI 界面的同時(shí),額外提供了一個(gè) 圖片裁剪 的功能

-------權(quán)限--------

以上擴(kuò)展需要使用相冊/相機(jī)/麥克風(fēng),所以需要添加權(quán)限

android: 修改 android/app/src/main/AndroidManifest.xml

<manifest ...>
     <!--  添加以下權(quán)限 -->
     <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest >

iOS,修改 ios/[project]/Info.plist, 添加以下鍵值對

...
<dict>
     ....
    <key>NSPhotoLibraryUsageDescription</key>
    <string>需要您的同意, $(PRODUCT_NAME) 才能訪問相冊</string>
    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>需要您的同意, $(PRODUCT_NAME) 才能保存圖片</string>

    <key>NSCameraUsageDescription</key>
    <string>需要您的同意, $(PRODUCT_NAME) 才能訪問相機(jī)</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>需要您的同意, $(PRODUCT_NAME) 才能使用麥克風(fēng)錄制視頻</string>
</dict>

PNG 自動(dòng)優(yōu)化


android 使用 gradle 編譯,默認(rèn)會優(yōu)化 png 文件,這本來是個(gè)好事,但在使用熱更,尤其是增量更新,這反而成了壞事了,因?yàn)樽詣?dòng)優(yōu)化會改變文件 hash,導(dǎo)致后續(xù)版本無法正確比對文件,生成增量

禁用該功能參見 官方文檔,在 app 的 build.gradle 添加

android {
    …
    buildTypes {
        release {
            crunchPngs false
        }
    }
}

js 環(huán)境


這個(gè)略坑,需要特別注意。

開啟 debug 的情況下使用 chrome v8 引擎,實(shí)際運(yùn)行使用的是 js 環(huán)境可能就是 JavaScriptCore 引擎,測試沒有問題不代表實(shí)際沒問題,比如 atob btoa 這對 base64 互轉(zhuǎn)的函數(shù),在 v8 下是 ok 的,但在 rn 的 js 環(huán)境下可能是沒有的。

為什么說可能,一是因?yàn)?JavaScriptCore 也可能升級,二是 android 可以定制引擎,比如 rn 其實(shí)自帶了一個(gè) hermes 引擎,社區(qū)實(shí)現(xiàn)的 react-native-v8 引擎。

總之一句話:在使用一些比較新的 js global 對象時(shí),要注意測試關(guān)閉 debug 是否仍然可運(yùn)行。

android 狀態(tài)欄


假設(shè)要做一個(gè)全屏的應(yīng)用,使用沉浸式狀態(tài)欄效果比較好,此時(shí)整個(gè)屏幕都可以使用,那么頁面高度設(shè)置為屏幕高度即可(若頂部有重要內(nèi)容,還要考慮劉海屏的問題,可能不合適);但沉浸式在 android 5.0 以下不支持,不用額外安裝插件,即可差異化實(shí)現(xiàn)。

import { Platform, Dimensions, StatusBar, StyleSheet, View} from 'react-native';

const {width:screenWidth, height:screenHeight} = Dimensions.get('window');
const viewHeight = Platform.OS === 'android' && Platform.Version < 21 
  ? screenHeight - StatusBar.currentHeight : screenHeight;

const styles = StyleSheet.create({
  wrapper: {
    width:screenWidth, 
    height:viewHeight,
  },
});

export default class extends Component {
  render() {
    return (<View style={styles.wrapper}>
      <StatusBar backgroundColor="transparent" translucent/>
    </View>)
  }
}

android 路徑大全


  1. file://

絕對路徑,通常為 APP 私有目錄 或 系統(tǒng)共用目錄

  1. content:// / android.resource://

文件符,這類路徑背后其實(shí)映射了一個(gè) file:// 路徑,通常為跨 APP 共享文件的路徑,比如 APP 讀取 相冊這個(gè)APP 的文件,相冊的文件是私有的,直接給路徑,不安全,系統(tǒng)層面不讓讀取,在用戶授權(quán)的情況下,相冊會生成一個(gè) content:// 文件路徑給其他 APP 使用。

  1. asset://

android/src/main/assets 目錄下的文件,RN 默認(rèn)是沒這個(gè)目錄的,需要的話可以自行創(chuàng)建一個(gè),通常用來存放一些非常規(guī)類型的資源文件,比如靜態(tài) dat 文件,PDF 獲取其他文檔文件,一般用的較少,因?yàn)榇祟愇募ǔ?lián)網(wǎng)獲取。

該目錄下文件會被打包到 APK 中,不能使用 RN 的熱更進(jìn)行替換。

  1. res://

android/src/main/res 目錄下文件,與 asset:// 類似,一般存放圖片類型文件,會被 android 索引為資源,即 drawable,但也可以放一些非常規(guī)文件到 android/src/main/res/raw, 此類文件也會被 android 索引.

在原生層面使用這類文件時(shí),通常是直接調(diào)用文件名即可,無需前綴、后綴,所以這也就要求該目錄下文件不可能重名,另外對于 drawable,可以根據(jù) android 的規(guī)范針對不同分辨率創(chuàng)建多個(gè)目錄,存放同名文件,運(yùn)行時(shí)會自動(dòng)選取最優(yōu)路徑。

在 RN 中, 使用 require('./file') 這種方式調(diào)用的文件在最終編譯后,會被自動(dòng)打包進(jìn) drawableraw,在 Debug 模式下, require('./file') 在 RN 內(nèi)部會解析為 http://,指向運(yùn)行時(shí)的調(diào)試地址,可以動(dòng)態(tài)更新;對于圖片,可使用 @2x 這種格式對應(yīng)不同分辨率;

由于使用了 require, 所以 RN 可以在中間決定指向的根目錄,并提供了自定義指向的接口,若未定義,則指向 android/src/main/res/,所以這種方式的文件可以很方便的進(jìn)行熱更。

require 本地文件


在 js 文件使用 require(filepath) 來引用本地文件,最常用的是 Image 組件,但可能有第三方拓展也需要這么用,比如 webview / video 等;有可能出現(xiàn) js 錯(cuò)誤的問題,這是因?yàn)?RN 對引用本地文件做了限制,僅支持某些后綴,具體支持的后綴可參見 這個(gè)文件,如果這些后綴不滿足要求,可根據(jù)該 文檔 進(jìn)行設(shè)置。

即在 rn 項(xiàng)目根目錄的 metro.config.js 配置 (該配置會完全覆蓋 metro 默認(rèn)值,要全量配置)

module.exports = {
  resolver: {
    assetExts: [
      'png',
      'gif',
      ....
      'm3u8',
      'dat'
    ],
  },
  ...
};

中文名崩潰


RN 0.71 Android 命名中文崩潰,參考 問題,該問題出現(xiàn)在 Debug 版本,主要是依賴的 FLIPPER 導(dǎo)致,只需修改 android/gradle.properties

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

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