前期因爲對CodePush的預研不足,覺得支持多包熱更新,結果在實際應用中發現CodePush拿的熱更新bundle資源是沒有區分業務的,致使切換業務場景的時候出現白屏現象,因此須要對CodePush源碼進行重構。java
fork了github.com/microsoft/r…,並提交了重構後的代碼:github.com/hsl5430/rea…node
在講CodePush重構前須要先講講幾個重點:react
路徑:[reactProject]/node_modules/react-native/react.gradleandroid
在[reactProject]/android/app/build.gradle中,依賴了react.gradleios
apply from: "../../node_modules/react-native/react.gradle"
複製代碼
這個gradle腳本主要作了些什麼呢?其實就是建立了2個task,在app構建過程當中生成bundle文件和相關的資源文件(圖片等)。git
def currentBundleTask = tasks.create(
name: "bundle${targetName}JsAndAssets",
type: Exec) {
group = "react"
description = "bundle JS and assets for ${targetName}."
...
}
複製代碼
調用node腳本github
commandLine(*nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",
"--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraArgs)
複製代碼
生成bundle文件到json
file("$buildDir/generated/assets/react/${targetPath}")
複製代碼
生成資源文件到react-native
file("$buildDir/generated/res/react/${targetPath}")
複製代碼
def currentAssetsCopyTask = tasks.create(
name: "copy${targetName}BundledJs",
type: Copy) {
group = "react"
description = "copy bundled JS into ${targetName}."
...
}
複製代碼
拷貝bundle文件到$buildDir/intermediates/assets/${targetPath}
和$buildDir/intermediates/merged_assets/${variant.name}/merge${targetName}Assets/out
設計模式
Android項目在構建過程當中mergeAssets的時候,會將該目錄下的bundle文件打包到assets目錄下。
因此,若是你是直接經過node腳本生成bundle包,或者由別的開發人員將生成好的bundle包給你,並放在[reactProject]/android/app/src/main/assets下,其實能夠不用依賴react.gradle,就不用每次在build過程當中起跑上述這兩個task
路徑:[reactProject]/node_modules/react-native-code-push/android/codepush.gradle
在[reactProject]/android/app/build.gradle中,依賴了codepush.gradle
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
複製代碼
這個gradle腳本主要作了些什麼呢?
gradle.projectsEvaluated {
android.buildTypes.each {
// to prevent incorrect long value restoration from strings.xml we need to wrap it with double quotes
// https://github.com/Microsoft/cordova-plugin-code-push/issues/264
it.resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
}
...
}
複製代碼
起初我在接入CodePush的時候,沒有依賴codepush.gradle,也不報錯,結果跑起來後,直接崩潰了,跟蹤下源碼發現CodePush在下載更新包downloadUpdate的時候,調用了getBinaryResourcesModifiedTime
long getBinaryResourcesModifiedTime() {
try {
String packageName = this.mContext.getPackageName();
int codePushApkBuildTimeId = this.mContext.getResources().getIdentifier(CodePushConstants.CODE_PUSH_APK_BUILD_TIME_KEY, "string", packageName);
// replace double quotes needed for correct restoration of long value from strings.xml
// https://github.com/Microsoft/cordova-plugin-code-push/issues/264
String codePushApkBuildTime = this.mContext.getResources().getString(codePushApkBuildTimeId).replaceAll("\"", "");
return Long.parseLong(codePushApkBuildTime);
} catch (Exception e) {
throw new CodePushUnknownException("Error in getting binary resources modified time", e);
}
}
複製代碼
很顯然,拿不到CODE_PUSH_APK_BUILD_TIME
就直接崩了。
def generateBundledResourcesHash = tasks.create(
name: "generateBundledResourcesHash${targetName}",
type: Exec) {
commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/react-native-code-push/scripts/generateBundledResourcesHash.js", resourcesDir, jsBundleFile, jsBundleDir)
enabled config."bundleIn${targetName}" ||
config."bundleIn${variant.buildType.name.capitalize()}" ?:
targetName.toLowerCase().contains("release")
}
複製代碼
調用node腳本generateBundledResourcesHash.js
(有興趣的童鞋能夠深挖hash是怎麼生成的),傳入bundle包和對應的圖片等資源文件,生成對應的hash,並以文件的形式存儲這個hash,存儲在[reactProject]/android/app/build/generated/assets/react/debug/CodePushHash
[reactProject]/android/app/build/generated/assets/react/debug
├── CodePushHash
└── index.android.bundle
複製代碼
同上,起初接入CodePush的時候,沒有依賴codepush.gradle,也不報錯,結果跑起來後,logcat下打印了一條日誌
Unable to get the hash of the binary's bundled resources - "codepush.gradle" may have not been added to the build definition.
複製代碼
定位到CodePush源碼
public static String getHashForBinaryContents(Context context, boolean isDebugMode) {
try {
return CodePushUtils.getStringFromInputStream(context.getAssets().open(CodePushConstants.CODE_PUSH_HASH_FILE_NAME));
} catch (IOException e) {
try {
return CodePushUtils.getStringFromInputStream(context.getAssets().open(CodePushConstants.CODE_PUSH_OLD_HASH_FILE_NAME));
} catch (IOException ex) {
if (!isDebugMode) {
// Only print this message in "Release" mode. In "Debug", we may not have the
// hash if the build skips bundling the files.
CodePushUtils.log("Unable to get the hash of the binary's bundled resources - \"codepush.gradle\" may have not been added to the build definition.");
}
}
return null;
}
}
複製代碼
這個是react獲取bundle包資源信息的一部分,具體react層拿到這個hash作什麼,我沒有去深挖。
總之,既然接入CodePush,必然要按照文檔要求,依賴codepush.gradle,保證能正常記錄apk文件的構建時間和生成CodePushHash。
src
└─ main
├─ assets
│ └─ index.android.bundle
└─ res
├─ drawable-mdpi
│ ├─ ic_back.png
│ └─ ...
├─ drawable-xhdpi
│ ├─ ic_back.png
│ └─ ...
└─ ...
複製代碼
/data/data/[應用包名]/files
└─ CodePush
├─ codepush.json // 記錄當前熱更新資源包的一些已基本信息
├─ [hash]
│ ├─ app.json // 記錄本hash對應熱更新資源包的一些已基本信息
│ └─ [version] // 這裏的version是CodePush定義的熱更新版本號,非APP版本號
│ ├─ index.android.bundle
│ ├─ drawable-mdpi
│ │ ├─ ic_back.png
│ │ └─ ...
│ ├─ drawable-xhdpi
│ │ ├─ ic_back.png
│ │ └─ ...
│ └─ ...
├─ [hash2]
└─ ...
複製代碼
CodePush.getJSBundleFile("index.android.bundle")
在讀取名稱爲index.android.bundle的bundle包時,會先拿熱更新bundle包,若是沒有,便取內置bundle包。具體可查看方法CodePush.getJSBundleFileInternal(String assetsBundleFileName)
String jsBundleFile = CodePush.getJSBundleFile("a.android.bundle");
Log.d(「JSBundleFile」, jsBundleFile);
// 好比這裏Log出來的值:/data/data/com.react.demo/files/CodePush/4247860b1dc848a13e6c980ac9bee9323de4210951ea917bc68f7346575370e2/1.0.0/b.android.bundle
複製代碼
因此,要支持多業務多包,必需要將各業務的react native環境和codepush環境隔離,保證互不影響。
先上一個思惟導圖
內置bundle包和圖片資源分目錄存放
rnLib
└─ react
├─ a.android.bundle(目錄)
│ ├─ a.android.bundle(文件)
│ ├─ drawable-mdpi
│ │ ├─ a_ic_back.png // 圖片的命名建議區分業務,不一樣業務不要出現名稱相同的圖片,
│ │ └─ ... // 不然apk打包的時候,會報資源合併衝突的錯誤
│ └─ ...
└─ b.android.bundle(目錄)
├─ b.android.bundle(文件)
├─ drawable-mdpi
│ ├─ b_ic_back.png
│ └─ ...
└─ ...
複製代碼
這麼作,主要是爲了方便管理這些rn資源
基於上述目錄結構,重構react.gradle和codepush.gradle腳本
上文在rnLib下建的react目錄,它不像assets、res等預置目錄目錄,能被Android工程所識別,在打包過程當中打到apk文件中,因此這裏利用react.gradle,拷貝react目錄下的文件到build目錄下特定的目錄,能被Android工程所識別。重構後的代碼:
/* * 重寫/node_modules/react-native/react.gradle的實現邏輯: * 支持多業務分包方案, /react目錄下以業務bundle名稱做爲文件夾名稱來存放各個業務的bundle文件合關聯的資源文件 * */
def config = project.hasProperty("react") ? project.react : []
def reactRoot = file(config.root ?: "../../")
def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]
// React js bundle directories
def jsBundleAndResDir = file("$rootDir/rnLib/react")
def generatedDir = "$buildDir/generated"
afterEvaluate {
def isAndroidLibrary = plugins.hasPlugin("com.android.library")
def variants = isAndroidLibrary ? android.libraryVariants : android.applicationVariants
variants.all { def variant ->
// Create variant and target names
def targetName = variant.name.capitalize()
def targetPath = variant.dirName
def jsBundleRootDir = file("$generatedDir/assets/react/${targetPath}")
// 遍歷jsBundleAndResDir下的各個業務
ArrayList<File> sources = new ArrayList<>()
ArrayList<File> jsBundleDirs = new ArrayList<>()
ArrayList<File> resourcesDirs = new ArrayList<>()
files(jsBundleAndResDir.listFiles(new FilenameFilter() {
@Override
boolean accept(File dir, String name) {
//自定義過濾規則
return name.endsWith(".android.bundle")
}
}
)).each { source ->
// 業務目錄
if (!source.exists() || !source.directory) {
return
}
sources.add(source)
def jsBundleRelativeDir = "assets/react/${targetPath}/${source.name}"
def resourcesRelativeDir = "res/react/${targetPath}/${source.name}"
def jsBundleDir = file("${generatedDir}/${jsBundleRelativeDir}")
def resourcesDir = file("${generatedDir}/${resourcesRelativeDir}")
jsBundleDirs.add(jsBundleDir)
resourcesDirs.add(resourcesDir)
}
if (sources.isEmpty()) {
return
}
// 跟react-native/react.gradle的實現有所區別
// 這裏是巧用"bundle${targetName}JsAndAssets"來作JsAndAssets的copy
def currentBundleTask = tasks.create(
name: "bundle${targetName}JsAndAssets",
type: Copy) {
group = "react"
description = "bundle JS and assets for ${targetName}."
// Set up inputs and outputs so gradle can cache the result
inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) outputs.dir(jsBundleRootDir) // 遍歷jsBundleAndResDir下的各個業務 files(sources.toArray()).each { source ->
// 業務目錄
def jsBundleRelativeDir = "assets/react/${targetPath}/${source.name}"
def resourcesRelativeDir = "res/react/${targetPath}/${source.name}"
def jsBundleDir = file("${generatedDir}/${jsBundleRelativeDir}")
def resourcesDir = file("${generatedDir}/${resourcesRelativeDir}")
// Create dirs if they are not there (e.g. the "clean" task just ran)
jsBundleDir.deleteDir()
jsBundleDir.mkdirs()
resourcesDir.deleteDir()
resourcesDir.mkdirs()
// Set up outputs so gradle can cache the result
//outputs.dir(jsBundleDir)
outputs.dir(resourcesDir)
into(generatedDir)
// 將react/[bundle name]下的JsBundle copy 到指定目錄
into(jsBundleRelativeDir) {
from(source)
include '*.bundle'
}
// 將react/[bundle name]下的drawable copy 到指定目錄
into(resourcesRelativeDir) {
from(source)
include 'drawable*/*'
}
}
enabled config."bundleIn${targetName}" ||
config."bundleIn${variant.buildType.name.capitalize()}" ?:
targetName.toLowerCase().contains("release")
}
// Expose a minimal interface on the application variant and the task itself:
variant.ext.bundleJsAndAssets = currentBundleTask
currentBundleTask.ext.generatedResFolders = files(resourcesDirs.toArray()).builtBy(currentBundleTask)
currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDirs.toArray()).builtBy(currentBundleTask)
// registerGeneratedResFolders for Android plugin 3.x
if (variant.respondsTo("registerGeneratedResFolders")) {
variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)
} else {
variant.registerResGeneratingTask(currentBundleTask)
}
variant.mergeResourcesProvider.get().dependsOn(currentBundleTask)
// packageApplication for Android plugin 3.x
def packageTask = variant.hasProperty("packageApplication")
? variant.packageApplicationProvider.get()
: tasks.findByName("package${targetName}")
if (variant.hasProperty("packageLibrary")) {
packageTask = variant.packageLibrary
}
// pre bundle build task for Android plugin 3.2+
def buildPreBundleTask = tasks.findByName("build${targetName}PreBundle")
def currentAssetsCopyTask = tasks.create(
name: "copy${targetName}BundledJs",
type: Copy) {
group = "react"
description = "copy bundled JS into ${targetName}."
into("$buildDir/intermediates")
into("assets/${targetPath}") {
from(jsBundleRootDir)
}
// Workaround for Android Gradle Plugin 3.2+ new asset directory
into("merged_assets/${variant.name}/merge${targetName}Assets/out") {
from(jsBundleRootDir)
}
// mergeAssets must run first, as it clears the intermediates directory
dependsOn(variant.mergeAssetsProvider.get())
enabled(currentBundleTask.enabled)
}
packageTask.dependsOn(currentAssetsCopyTask)
if (buildPreBundleTask != null) {
buildPreBundleTask.dependsOn(currentAssetsCopyTask)
}
}
}
複製代碼
重構後的代碼:
/* * Adapted from https://raw.githubusercontent.com/facebook/react-native/d16ff3bd8b92fa84a9007bf5ebedd8153e4c089d/react.gradle * * 重寫/node_modules/react-native-code-push/android/codepush.gradle的實現邏輯: * 支持多業務分包方案, 不一樣的業務生成各自須要的CodePushHash * */
import java.nio.file.Paths
def config = project.hasProperty("react") ? project.react : []
void runBefore(String dependentTaskName, Task task) {
Task dependentTask = tasks.findByPath(dependentTaskName)
if (dependentTask != null) {
dependentTask.dependsOn task
}
}
gradle.projectsEvaluated {
android.buildTypes.each {
// to prevent incorrect long value restoration from strings.xml we need to wrap it with double quotes
// https://github.com/Microsoft/cordova-plugin-code-push/issues/264
it.resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
}
android.applicationVariants.all { variant ->
if (!variant.hasProperty("bundleJsAndAssets")) {
return
}
Task reactBundleTask = variant.bundleJsAndAssets if (!reactBundleTask.hasProperty("generatedAssetsFolders")) {
return
}
def jsBundleDirs = reactBundleTask.generatedAssetsFolders
def resourcesDirs = reactBundleTask.generatedResFolders if (jsBundleDirs.isEmpty()) {
return
}
def nodeModulesPath if (config.root) {
nodeModulesPath = Paths.get(config.root, "/node_modules")
} else if (project.hasProperty('nodeModulesPath')) {
nodeModulesPath = project.nodeModulesPath
} else {
nodeModulesPath = "../../node_modules"
}
// Additional node commandline arguments
def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"]
def targetName = variant.name.capitalize()
for (int i = 0; i < jsBundleDirs.size(); i++) {
File jsBundleDir = jsBundleDirs[i]
File resourcesDir = resourcesDirs[i]
// jsBundleFile的name正好是目錄的name
File jsBundleFile = file("${jsBundleDir}/${jsBundleDir.name}")
def indexOf = jsBundleFile.name.indexOf('.')
def taskSuffix = jsBundleFile.name.substring(0, 1).toUpperCase() + jsBundleFile.name.substring(1, indexOf)
// Make this task run right after the bundle task
def generateBundledResourcesHash = tasks.create(
name: "generateBundledResourcesHash${targetName}${taskSuffix}",
type: Exec) {
group = "react"
description = "generate CodePushHash for ${jsBundleFile.name}."
commandLine(*nodeExecutableAndArgs, "${nodeModulesPath}/react-native-code-push/scripts/generateBundledResourcesHash.js", resourcesDir.absolutePath, jsBundleFile, jsBundleDir.absolutePath)
enabled(reactBundleTask.enabled)
}
generateBundledResourcesHash.dependsOn(reactBundleTask)
runBefore("processArmeabi-v7a${targetName}Resources", generateBundledResourcesHash)
runBefore("processX86${targetName}Resources", generateBundledResourcesHash)
runBefore("processUniversal${targetName}Resources", generateBundledResourcesHash)
runBefore("process${targetName}Resources", generateBundledResourcesHash)
}
}
}
複製代碼
apk
├─ assets
│ ├─ a.android.bundle(目錄)
│ │ ├─ a.android.bundle(文件)
│ │ ├─ CodePushHash
│ │ └─ ...
│ └─ b.android.bundle(目錄)
│ ├─ b.android.bundle(文件)
│ ├─ CodePushHash
│ └─ ...
└─ res
├─ drawable-mdpi-v4
│ ├─ a_ic_back.png
│ ├─ b_ic_back.png
│ └─ ...
├─ drawable-xhdpi-v4
│ ├─ a_ic_back.png
│ ├─ b_ic_back.png
│ └─ ...
└─ ...
複製代碼
重構gradle腳本後,根據apk中的資源目錄結構,相應的,Java層代碼也要相應調整
CodePush.getJSBundleFile(String assetsBundleFileName)
內部調用了CodePush.getJSBundleFileInternal(String assetsBundleFileName)
,這裏重構的點就是將assetsBundleFileName改成assetsBundleFilePath,不要寫死傳文件名,應當支持傳assets文件路徑
修改前
public class CodePush implements ReactPackage {
private String mAssetsBundleFileName;
...
public String getAssetsBundleFileName() {
return mAssetsBundleFileName;
}
public String getJSBundleFileInternal(String assetsBundleFileName) {
this.mAssetsBundleFileName = assetsBundleFileName;
String binaryJsBundleUrl = CodePushConstants.ASSETS_BUNDLE_PREFIX + assetsBundleFileName;
String packageFilePath = null;
try {
packageFilePath = mUpdateManager.getCurrentPackageBundlePath(this.mAssetsBundleFileName);
} catch (CodePushMalformedDataException e) {
// We need to recover the app in case 'codepush.json' is corrupted
CodePushUtils.log(e.getMessage());
clearUpdates();
}
...
}
}
複製代碼
修改後
public class CodePush implements ReactPackage {
private String mAssetsBundleFileName;
private String mAssetsBundleFilePath;
...
public String getAssetsBundleFileName() {
return mAssetsBundleFileName;
}
public String getAssetsBundleFilePath() {
return mAssetsBundleFilePath;
}
public String getAssetsBundleFileDir() {
try {
return new File(getAssetsBundleFilePath()).getParent();
} catch (Exception e) {
return null;
}
}
public String getJSBundleFileInternal(String assetsBundleFileName) {
// 支持assets文件路徑
this.mAssetsBundleFilePath = assetsBundleFileName;
File file = new File(assetsBundleFileName);
mAssetsBundleFileName = file.getName();
String binaryJsBundleUrl = CodePushConstants.ASSETS_BUNDLE_PREFIX + assetsBundleFileName;
String packageFilePath = null;
try {
packageFilePath = mUpdateManager.getCurrentPackageBundlePath(this.mAssetsBundleFileName);
} catch (CodePushMalformedDataException e) {
// We need to recover the app in case 'codepush.json' is corrupted
CodePushUtils.log(e.getMessage());
clearUpdates();
}
...
}
}
複製代碼
同時,要檢查getJSBundleFileInternal的調用,發現CodePushNativeModule.loadBundle()
中調用了
public class CodePushNativeModule extends ReactContextBaseJavaModule {
...
private void loadBundle() {
...
String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
...
}
}
複製代碼
修改後
public class CodePushNativeModule extends ReactContextBaseJavaModule {
...
private void loadBundle() {
...
String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFilePath());
...
}
}
複製代碼
相應地,上層就能夠這樣調用了:
CodePush.getJSBundleFile("a.android.bundle/a.android.bundle");
複製代碼
修改前
public class CodePushUpdateUtils {
public static String getHashForBinaryContents(Context context, boolean isDebugMode) {
try {
return CodePushUtils.getStringFromInputStream(context.getAssets().open(CodePushConstants.CODE_PUSH_HASH_FILE_NAME));
} catch (IOException e) {
try {
return CodePushUtils.getStringFromInputStream(context.getAssets().open(CodePushConstants.CODE_PUSH_OLD_HASH_FILE_NAME));
} catch (IOException ex) {
if (!isDebugMode) {
// Only print this message in "Release" mode. In "Debug", we may not have the
// hash if the build skips bundling the files.
CodePushUtils.log("Unable to get the hash of the binary's bundled resources - \"codepush.gradle\" may have not been added to the build definition.");
}
}
return null;
}
}
}
複製代碼
因爲這個方法只有在CodePushNativeModule
中調用,因此我直接在CodePushNativeModule
中增長方法getHashForBinaryContents
:
public class CodePushNativeModule extends ReactContextBaseJavaModule {
private String mBinaryContentsHash = null;
private CodePush mCodePush;
...
public CodePushNativeModule(ReactApplicationContext reactContext, CodePush codePush, CodePushUpdateManager codePushUpdateManager, CodePushTelemetryManager codePushTelemetryManager, SettingsManager settingsManager) {
super(reactContext);
mCodePush = codePush;
...
mBinaryContentsHash = getHashForBinaryContents(codePush);
...
}
/** * 重寫{@link CodePushUpdateUtils#getHashForBinaryContents(Context, boolean)}的實現, 分業務目錄讀取{@link CodePushConstants#CODE_PUSH_HASH_FILE_NAME} */
public String getHashForBinaryContents(CodePush codePush) {
// return CodePushUpdateUtils.getHashForBinaryContents(getReactApplicationContext(), codePush.isDebugMode());
Context context = codePush.getContext();
String assetsBundleDir = codePush.getAssetsBundleFileDir();
String codePushHashFilePath;
try {
codePushHashFilePath = new File(assetsBundleDir, CodePushConstants.CODE_PUSH_HASH_FILE_NAME).getPath();
return CodePushUtils.getStringFromInputStream(context.getAssets().open(codePushHashFilePath));
} catch (IOException e) {
try {
codePushHashFilePath = new File(assetsBundleDir, CodePushConstants.CODE_PUSH_OLD_HASH_FILE_NAME).getPath();
return CodePushUtils.getStringFromInputStream(context.getAssets().open(codePushHashFilePath));
} catch (IOException ex) {
if (!codePush.isDebugMode()) {
// Only print this message in "Release" mode. In "Debug", we may not have the
// hash if the build skips bundling the files.
CodePushUtils.log("Unable to get the hash of the binary's bundled resources - \"codepush.gradle\" may have not been added to the build definition.");
}
}
return null;
}
}
}
複製代碼
CodePushUpdateManager.getCodePushPath
,這個很是重要!CodePush內部有多出引用。就是指定了熱更新包讀寫的根目錄,因此爲了區分業務,就應該在本來的基礎上,增長子目錄,來區分業務
修改前
public class CodePushUpdateManager {
private String mDocumentsDirectory;
public CodePushUpdateManager(String documentsDirectory) {
mDocumentsDirectory = documentsDirectory;
}
...
private String getCodePushPath() {
String codePushPath = CodePushUtils.appendPathComponent(getDocumentsDirectory(), CodePushConstants.CODE_PUSH_FOLDER_PREFIX);
if (CodePush.isUsingTestConfiguration()) {
codePushPath = CodePushUtils.appendPathComponent(codePushPath, "TestPackages");
}
return codePushPath;
}
}
複製代碼
修改後
public class CodePushUpdateManager {
private String mDocumentsDirectory;
private CodePush mCodePush;
public CodePushUpdateManager(String documentsDirectory, CodePush codePush) {
mDocumentsDirectory = documentsDirectory;
mCodePush = codePush;
}
...
private String getCodePushPath() {
String codePushPath = CodePushUtils.appendPathComponent(getDocumentsDirectory(), CodePushConstants.CODE_PUSH_FOLDER_PREFIX);
if (!TextUtils.isEmpty(mCodePush.getAssetsBundleFileName())) {
// 文件目錄按bundle文件名(bundle name)分類:/data/data/[app包名]/files/CodePush/[bundle name]/
codePushPath = CodePushUtils.appendPathComponent(codePushPath, mCodePush.getAssetsBundleFileName());
}
if (CodePush.isUsingTestConfiguration()) {
codePushPath = CodePushUtils.appendPathComponent(codePushPath, "TestPackages");
}
return codePushPath;
}
}
複製代碼
以上重構都是爲了解決路徑的問題,簡而言之,就是增長一級子目錄(以業務bundle名稱命名),來作到區分業務,加載不一樣的bundle包。
梳理了基礎須要調整的地方
CodePush.java
的改動比較大,重構後,本來使用類名調用靜態方法的地方就報錯,須要一一去解決,使用變量去調用非靜態方法便可。代碼:github.com/hsl5430/rea…。