Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class PushyFileJSBundleProvider extends JSBundleProvider {

constructor(context: common.UIAbilityContext) {
super();
this.updateContext = new UpdateContext(context);
this.updateContext = UpdateContext.getInstance(context);
}

getURL(): string {
Expand Down
16 changes: 14 additions & 2 deletions harmony/pushy/src/main/ets/PushyTurboModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class PushyTurboModule extends UITurboModule {
super(ctx);
logger.debug(TAG, ',PushyTurboModule constructor');
this.mUiCtx = ctx.uiAbilityContext;
this.context = new UpdateContext(this.mUiCtx);
this.context = UpdateContext.getInstance(this.mUiCtx);
EventHub.getInstance().setRNInstance(ctx.rnInstance);
}

Expand Down Expand Up @@ -78,6 +78,7 @@ export class PushyTurboModule extends UITurboModule {

getConstants(): Object {
logger.debug(TAG, ',call getConstants');
this.context.logStateSnapshot('getConstants:enter');
const packageVersion = this.context.getPackageVersion();
const buildTime = this.context.getBuildTime();
this.context.syncStateWithBinaryVersion(packageVersion, buildTime);
Expand Down Expand Up @@ -106,7 +107,18 @@ export class PushyTurboModule extends UITurboModule {
rolledBackVersion,
uuid,
};
logger.info(TAG, `,getConstants result: ${JSON.stringify(result)}`);
const logResult = {
downloadRootDir: result.downloadRootDir,
currentVersionInfo: result.currentVersionInfo,
packageVersion: result.packageVersion,
currentVersion: result.currentVersion,
buildTime: result.buildTime,
isUsingBundleUrl: result.isUsingBundleUrl,
isFirstTime: result.isFirstTime,
rolledBackVersion: result.rolledBackVersion,
uuidSet: !!result.uuid,
};
logger.info(TAG, `,getConstants result: ${JSON.stringify(logResult)}`);
return result;
}

Expand Down
63 changes: 60 additions & 3 deletions harmony/pushy/src/main/ets/UpdateContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import common from '@ohos.app.ability.common';
import { DownloadTaskParams } from './DownloadTaskParams';
import { bundleManager } from '@kit.AbilityKit';
import { util } from '@kit.ArkTS';
import logger from './Logger';
import NativePatchCore, {
STATE_OP_CLEAR_FIRST_TIME,
STATE_OP_CLEAR_ROLLBACK_MARK,
Expand All @@ -28,10 +29,23 @@ export class UpdateContext {
private static ignoreRollback: boolean = false;
private static cachedPackageVersion: string = '';
private static cachedBuildTime: string = '';
// 单例:确保 bundle provider 与 TurboModule 共用同一份 preferences 内存状态,
// 避免 RNOH RN 实例重建后两处 UpdateContext 各自持有 preferences 缓存导致读写分裂。
private static instance: UpdateContext | null = null;
private static instanceCounter: number = 0;
private readonly instanceId: string;

public static getInstance(context: common.UIAbilityContext): UpdateContext {
if (!UpdateContext.instance) {
UpdateContext.instance = new UpdateContext(context);
}
return UpdateContext.instance;
}

constructor(context: common.UIAbilityContext) {
private constructor(context: common.UIAbilityContext) {
this.context = context;
this.rootDir = context.filesDir + '/_update';
this.instanceId = `uc#${++UpdateContext.instanceCounter}`;

try {
if (!fileIo.accessSync(this.rootDir)) {
Expand All @@ -41,12 +55,36 @@ export class UpdateContext {
console.error('Failed to create root directory:', e);
}
this.initPreferences();
this.trace('ctor');
this.syncStateWithBinaryVersion(
this.getPackageVersion(),
this.getBuildTime(),
);
}

/**
* 诊断日志:打印本实例 id 与关键状态,用于定位 preferences 多实例 / 状态分裂问题。
* 通过 hilog 输出,prefix=pushy,可在 hilog 中按 "UpdateContext" 过滤。
*/
private trace(point: string): void {
const snap = this.getStateSnapshot();
logger.info(
'UpdateContext',
`trace id=${this.instanceId} ${point}` +
` pkg=${snap.packageVersion} bt=${snap.buildTime}` +
` cv=${snap.currentVersion} lv=${snap.lastVersion}` +
` ft=${snap.firstTime} fto=${snap.firstTimeOk}` +
` rb=${snap.rolledBackVersion}` +
` flm=${this.readString('firstLoadMarked')}` +
` uuidSet=${!!this.readString('uuid')}`,
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/** 对外诊断入口,供 TurboModule 在 getConstants 等关键节点打印状态。 */
public logStateSnapshot(point: string): void {
this.trace(point);
}

private initPreferences() {
try {
this.preferences = preferences.getPreferencesSync(this.context, {
Expand Down Expand Up @@ -269,9 +307,17 @@ export class UpdateContext {
return;
}

logger.info(
'UpdateContext',
`binary version changed, resetting update state id=${this.instanceId}`,
);
UpdateContext.ignoreRollback = false;
this.cleanUp();
this.persistState(nextState, { clearExisting: true });
// 仅重置状态机字段(currentVersion / lastVersion / firstTime / firstTimeOk /
// rolledBackVersion)。不再 clearExisting,避免连带清除 uuid / firstLoadMarked /
// hash_* 等与 binary 版本无关的 KV —— 它们在多实例场景下本就脆弱,连带清除会
// 让 getConstants() 永远读到空,从而 isFirstTime=false、markSuccess 永不执行。
this.persistState(nextState);
}

public setKv(key: string, value: string): void {
Expand Down Expand Up @@ -388,8 +434,10 @@ export class UpdateContext {
throw Error(`Bundle version ${hash} not found.`);
}

this.trace(`switchVersion:before ${hash}`);
this.runStateOperation(STATE_OP_SWITCH_VERSION, hash);
UpdateContext.ignoreRollback = false;
this.trace(`switchVersion:after ${hash}`);
} catch (e) {
console.error('Failed to switch version:', e);
throw e;
Expand All @@ -398,6 +446,7 @@ export class UpdateContext {

public consumeFirstLoadMarker(): boolean {
const marked = this.readString('firstLoadMarked') === 'true';
this.trace(`consumeFirstLoadMarker:marked=${marked}`);
if (marked) {
this.preferences.deleteSync('firstLoadMarked');
this.flushPreferences('clear first load marker');
Expand All @@ -407,6 +456,7 @@ export class UpdateContext {

public getBundleUrl() {
UpdateContext.isUsingBundleUrl = true;
this.trace('getBundleUrl:enter');
const launchState = NativePatchCore.runStateCore(
STATE_OP_RESOLVE_LAUNCH,
this.getStateSnapshot(),
Expand All @@ -422,6 +472,11 @@ export class UpdateContext {
if (launchState.consumedFirstTime) {
UpdateContext.ignoreRollback = true;
}
this.trace(
`getBundleUrl:load=${launchState.loadVersion}` +
` consumed=${launchState.consumedFirstTime}` +
` rollback=${launchState.didRollback}`,
);

let version = launchState.loadVersion || '';
while (version) {
Expand All @@ -442,7 +497,9 @@ export class UpdateContext {
}

public getCurrentVersion(): string {
return this.getStateSnapshot().currentVersion || '';
const cv = this.getStateSnapshot().currentVersion || '';
this.trace(`getCurrentVersion:${cv}`);
return cv;
}

private rollBack(): string {
Expand Down