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

一、從Android到React Native開發(1、入門)node

二、從Android到React Native開發(2、通訊與模塊實現)react

三、從Android到React Native開發(3、自定義原生控件支持)android

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

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

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

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

 你須要在項目的android目錄下,即app這個module的同級目錄下,建立一個Android Library的 module:rn-library 。(固然你也能夠修改 app下的 apply plugin: "com.android.application"apply plugin: 'com.android.library' ,再屏蔽applicationId)。react-native

1、引用

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

 而手動針對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')

複製代碼

2、建立

 看過系列篇章二的應該知道,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);
複製代碼

一、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"
複製代碼

二、資源文件

 這裏有一個須要額外關注的點:根據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",
]

複製代碼

3、打包

 上面說過: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

圖片來自http://www.huahuaxie.com/fat-aar-implementation-analysis

 最後咱們能夠先在rn-library執行../gradlew assembleRelease,讓react腳本生成咱們須要的資源文件,而後再引用publish.gradle發佈aar到maven便可。

4、最後

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

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

資源推薦:

哦嘞嘞
相關文章
相關標籤/搜索