一、從Android到React Native開發(1、入門)node
二、從Android到React Native開發(2、通訊與模塊實現)react
三、從Android到React Native開發(3、自定義原生控件支持)android
做爲失蹤人口,本篇是對前三篇React Native文章的番外補充,主要實現把React Native項目,打包爲完整aar庫發佈到maven,提供庫支持的功能,算是小衆化的需求吧,不過經過本篇你能夠了解:git
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
使用過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')
引用模塊,最後在Application
的getPackages()
方法添加模塊註冊。因此這裏咱們明確了一點,項目引用的原生模塊都是經過本地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);
複製代碼
從上方代碼能夠看出,咱們直接加載 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",
]
複製代碼
上面說過: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便可。
如何,最終實現過程其實並不複雜,總結起來:
maven-publish
執行publish上傳。