前期因为对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…。