Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion 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
61 changes: 59 additions & 2 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) {
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')}` +
` uuid=${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