從 Android 到 React Native 開發(四、打包流程解析和發佈爲 Maven 庫 )

1、從 Android 到 React Native 開發(一、入門)
2、從 Android 到 React Native 開發(二、通信與模塊實現)
3、從 Android 到 React Native 開發(三、自定義原生控件支持)
4、從 Android 到 React Native 開發(四、打包流程和發佈爲 Maven庫 )

作爲失蹤人口,本篇是對前三篇 React Native 文章的番外補充,主要實現把 React Native 項目,打包爲完整aar庫發佈到maven,提供庫支持的功能,算是小衆化的需求吧,不過通過本篇你可以瞭解:

  • React Native的資源的打包流程。
  • React Native原生依賴結構。
  • 本地多aar文件的合併實現。
  • 進一步的Gradle腳本理解。
  • 如何發佈一個React Native的Maven庫。

OK,Let’t do it (-_^)。

通過前幾篇,你已經對React Native的項目結構、通信交互方式有了一定了解,不瞭解也沒關係((⊙_⊙)?), 我們知道,發佈一個maven庫,首先你要先有一個lib模塊。

你需要在項目的android目錄下,即app這個module的同級目錄下,創建一個Android Library的 module:rn-library 。(當然你也可以修改 app下的 apply plugin: "com.android.application"apply plugin: 'com.android.library' ,再屏蔽applicationId)。

一、引用

使用過React Native的應該知道,依賴的庫都是通過npm install安裝,安裝後的所有源碼存在於node_modules文件夾中,如果依賴的庫需要原生代碼的支持,需要通過react-native link 實現原生代碼模塊的引用註冊。

而手動針對Android添加過link的應該熟悉,react-native link 實際上是通過腳本,在 setting.gradle 文件中引入模塊在node_modules原生路徑,然後在 app 的module的build.gradle中,通過compile project(':react-native-fs')引用模塊,最後在ApplicationgetPackages()方法添加模塊註冊。所以這裏我們明確了一點,項目引用的原生模塊都是通過本地project module的引用。(這很重要( ̄へ ̄))

setting.gradle

//在setting中指定模塊的位置
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')

二、創建

看過系列篇章二的應該知道,React Native項目其實是通過ReactInstanceManager,實現對js的bundle文件加載的。所以要呈現一個React Native頁面,我們可以通過ReactInstanceManager,在任意自定義Activity或者Fragment中,實現頁面的顯示渲染(當然你也可以直接繼承ReactActivity)。這裏只列關鍵點點代碼,即ReactInstanceManager的創建和加載,如下發代碼(更多可見篇章二):

 mReactInstanceManager = ReactInstanceManager.builder()
         //設置加載文件,這裏從assets中加載打包好的js bundle
         .setBundleAssetName("index.android.bundle")
         //異常輸出
         .setNativeModuleCallExceptionHandler(new NativeModuleCallExceptionHandler() {
             @Override
             public void handleException(Exception e) {
                 e.printStackTrace();
             }
         })
         //增加主模塊註冊,必須
         .addPackage(new MainReactPackage())
         //增加使用你的第三方模塊註冊
         .addPackage(new RNFSPackage())
         //通Application中指定的getJSMainModuleName
         .setJSMainModulePath("index")
         //是否支持開發者模式
         .setUseDeveloperSupport(false)
         //初始化生命週期
         .setInitialLifecycleState(LifecycleState.RESUMED)
         //設置Application
         .setApplication(getActivity().getApplication())
         .build();
        //js代碼中註冊的的Component名字 AppRegistry.registerComponent('AppModule', () => App);
        String moduleName = "AppModule";
        //通過頁面中已經聲明好ReactRootView,啓動
        mReactRootView.startReactApplication(mReactInstanceManager, moduleName, null);

1、bundle文件

 從上方代碼可以看出,我們直接加載 assets 目錄下的bundle文件index.android.bundle當然你可以從本地或者網絡加載jsbundle文件也是可以),它的生成和拷貝是通過react-native目錄下的react.gradle腳本實現的。這個腳本會讀取一些配置路徑,然後執行命令行打包和拷貝需要的資源,所以和app的build.gradle文件一樣,在rn-library的build.gradle文件頂部增加引入即可,打包後,默認生成的bundle文爲即爲index.android.bundle文件.。

//引入react腳本
apply from: "../../node_modules/react-native/react.gradle"

2、資源文件

 這裏有一個需要額外關注的點:根據node_nodules/react-native/local-cli/bundle/目錄下的assetPathUtils.js文件中,getAndroidResourceIdentifier方法的源碼可知,js文件中引用本地的靜態資源文件,如果存在多級目錄,是會被Encode處理的,其中/會被替換爲_,數字會被屏蔽,assets_會被屏蔽。

function getAndroidResourceIdentifier(asset: PackagerAsset) {
  var folderPath = getBasePath(asset);
  return (folderPath + '/' + asset.name)
    .toLowerCase()
    .replace(/\//g, '_')           // Encode folder structure in file name
    .replace(/([^a-z0-9_])/g, '')  // Remove illegal chars
    .replace(/^assets_/, '');      // Remove "assets_" prefix
}

 所以,比如放在React Native項目根目錄下的img/pic/logo.png的資源,其實編譯時,會被重命名後,拷貝merged到對應的是drawable目錄下,比如drawable-xxhdpi下的img_pic_logo.png。這一切都是由react native中的腳本執行的。不過默認情況下,生成拷貝的bundle文件和resources資源路徑,是無法被打包到aar中的。所以如下代碼所示,我們需要配置生成的資源自動添加到aar文件中。

//默認路徑
//jsBundleDirRelease: "$buildDir/intermediates/assets/release
//resourcesDirRelease: "$buildDir/intermediates/res/merged/release

//修改爲
project.ext.react = [
   jsBundleDirRelease: "$buildDir/intermediates/bundles/release/assets",
   resourcesDirRelease: "$buildDir/intermediates/bundles/release/res",
]

三、打包

 上面說過:React Native項目引用的原生模塊,都是通過本地project module的引用。那麼默認的maven發佈方式,只會發佈指定module的aar文件,對於引用的其他module模塊,這些dependencies列在了與aar文件同目錄的.pom文件中,並不會打包僅aar,而明顯React Native的這些第三方支持包,並不是Maven庫。

 這時候,就需要通過gradle腳本,手動對依賴的module模塊,實現aar文件內容的合併。aar文件本身和Apk一樣,其實是一個zip壓縮文件,其中包含文件如下所示:

/**主要文件**/
classes.jar
R.txt 
AndroidManifest.xml
res/

/**其他文件**/
proguard.txt
libs/
jni/
···

 這裏所謂的合併,就是就是將所有需要的aar文件內容,拷貝到一起,然後合併一個aar。當然,如何合併,合併的時機這些都是需要處理的點。而這時候, android-fat-aar 腳本,剛好滿足的我們的需求。通過引入apply from: 'fat-aar.gradle' 的腳本,對需要合併模塊引用修改爲 embedded project(':react-native-fs') 依賴即可:

dependencies {
    embedded project(':react-native-fs')
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:23.0.1"
    embedded "com.facebook.react:react-native:+"  // From node_modules
}

 從腳本代碼中可以知道,這裏的embedded實際上是一個configuration類,而這個configurations對應的是一個 ConfigurationContainer,ConfigurationContainer包含有dependencies,如下代碼所示,最終還是使用compile引用,但是這個過程中,我們通過embedded統計到哪些包需要合併發佈。

configurations { 
    embedded  
}
dependencies {
    compile configurations.embedded
}

 因此我們可以根據build目錄下的一些文件,動態的embedded的module進行文件拷貝和合並,如$build_dir/intermediates/exploded-aar目錄下,對每個需要合併的module的res文件夾、libs文件夾、jars文件夾、assets文件夾等的拷貝。合併aar的流程如下圖所示,有興趣的可以深入瞭解: fat-aar-implementation-analysis

最後我們可以先在rn-library執行../gradlew assembleRelease,讓react腳本生成我們需要的資源文件,然後再引用publish.gradle發佈aar到maven即可。

四、最後

 如何,最終實現過程其實並不複雜,總結起來:

  • 創建一個android.library
  • 添加本地dependencies依賴
  • apply react.gradle 、 fat-aar.gradle、publish.gradle
  • 在library通過../gradlew assembleRelease打包,然後通過maven-publish執行publish上傳。
Over(~ ̄▽ ̄)~

資源推薦:

哦嘞嘞

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