CodePush 流程 偏iOS

作者: 一本大书 | 来源:发表于2019-04-11 18:34 被阅读7次

前言

本文从React再到iOS源码对CodePush业务逻辑进行梳理。
在完成订制 CodePush 版本的过程中,笔者会对本文持续更新,补充各模块细节。如果读者同样对CodePush兴趣,欢迎讨论。

大致流程

React

启动应用,调用CodePush.sync()进行更新请求,我们直接看sync()这个方法做了些什么事情

const sync = (() => {
  let syncInProgress = false;
  const setSyncCompleted = () => { syncInProgress = false; };

  return (options = {}, syncStatusChangeCallback, downloadProgressCallback, handleBinaryVersionMismatchCallback) => {

    // 1. 对传入的参数进行校验
    let syncStatusCallbackWithTryCatch, downloadProgressCallbackWithTryCatch;
    if (typeof syncStatusChangeCallback === "function") {
      syncStatusCallbackWithTryCatch = (...args) => {
        try {
          syncStatusChangeCallback(...args);
        } catch (error) {
          log(`An error has occurred : ${error.stack}`);
        }
      }
    }

    if (typeof downloadProgressCallback === "function") {
      downloadProgressCallbackWithTryCatch = (...args) => {
        try {
          downloadProgressCallback(...args);
        } catch (error) {
          log(`An error has occurred: ${error.stack}`);
        }
      }
    }

    if (syncInProgress) {
      typeof syncStatusCallbackWithTryCatch === "function"
        ? syncStatusCallbackWithTryCatch(CodePush.SyncStatus.SYNC_IN_PROGRESS)
        : log("Sync already in progress.");
      return Promise.resolve(CodePush.SyncStatus.SYNC_IN_PROGRESS);
    }

    syncInProgress = true;
    // 2. 检查更新、下载、安装
    const syncPromise = syncInternal(options, syncStatusCallbackWithTryCatch, downloadProgressCallbackWithTryCatch, handleBinaryVersionMismatchCallback);
    syncPromise
      .then(setSyncCompleted)
      .catch(setSyncCompleted);

    return syncPromise;
  };
})();

syncInternal() 主要流程如下:

await CodePush.notifyApplicationReady();

// 1. 根据deploymentKey获取当前是否有新包
const remotePackage = await checkForUpdate(syncOptions.deploymentKey, handleBinaryVersionMismatchCallback);

// 2. 下载中
const localPackage = await remotePackage.download(downloadProgressCallback);

// 3. 根据是否是“强制更新” 确定包的安装方式
resolvedInstallMode = localPackage.isMandatory ? syncOptions.mandatoryInstallMode : syncOptions.installMode;

// 4. 安装
await localPackage.install(resolvedInstallMode, syncOptions.minimumBackgroundDuration, () => {
        // 5. 安装完成 回调
        syncStatusChangeCallback(CodePush.SyncStatus.UPDATE_INSTALLED);
});

iOS

1. 检查更新

checkForUpdate() 内调用原生方法 getConfiguration() 获取当前的版本号,并且判断当前是否有新版本 && 新版本是否兼容原生版本 等判断。

2. 下载

download() 内调用原生方法 downloadUpdate() 下载并解压压缩包。
下载的核心方法在

// CodePushPackage.m
+ (void)downloadPackage:(NSDictionary *)updatePackage
 expectedBundleFileName:(NSString *)expectedBundleFileName
              publicKey:(NSString *)publicKey
         operationQueue:(dispatch_queue_t)operationQueue
       progressCallback:(void (^)(long long, long long))progressCallback
           doneCallback:(void (^)())doneCallback
           failCallback:(void (^)(NSError *err))failCallback;

此方法代码量庞大,我对其逻辑做了梳理,大致如下。

  • 删除之前的下载记录
  • 下载更新包
  • 解压更新包
  • 复制解压后的 bundle 到 newUpdateFolderPath 目录下

3. 安装

install() 内调用原生方法 installUpdate()
安装实际调用的是下面这2个方法

// CodePushPackage.m
// 对解压好的包进行hash校验,确认无误后调用updateCurrentPackageInfo:
+ (BOOL)installPackage:(NSDictionary *)updatePackage
   removePendingUpdate:(BOOL)removePendingUpdate
                 error:(NSError **)error;
// 安装的核心方法:实际就是将packageInfo写入到缓存,写入成功即表示安装成功。之后codepush去读取本地代码都是从这个文件路径去获取package的配置信息。
+ (BOOL)updateCurrentPackageInfo:(NSDictionary *)packageInfo
                           error:(NSError **)error;

4. Restart

在安装成功回调给 react 后,react 调用 CodePush.restartApp() 方法,去调用原生的方法:

// CodePush.m
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending
                     resolve:(RCTPromiseResolveBlock)resolve
                    rejecter:(RCTPromiseRejectBlock)reject)

重启:为bridge设置新的bundleURL

[super.bridge setValue:[CodePush bundleURL] forKey:@"bundleURL"];
[super.bridge reload];

相关文章

网友评论

    本文标题:CodePush 流程 偏iOS

    本文链接:https://www.haomeiwen.com/subject/pstxwqtx.html