HarmonyOS鸿蒙Next中APP_INPUT_BLOCK响应超时问题
HarmonyOS鸿蒙Next中APP_INPUT_BLOCK响应超时问题 截屏后,快速点击我们的按钮多次,就会遇到这个错误,APP_INPUT_BLOCK,
看了下文档只说出了原因,有什么解决办法吗,或者有什么工具,能抓到具体的crash~
进程崩溃日志是一种故障日志,与应用无响应日志、JS应用崩溃等都由FaultLogger模块管理,可通过以下方式获取:
- 方式一:通过DevEco Studio获取日志:DevEco Studio会收集设备“/data/log/faultlog/faultlogger/”路径下的进程崩溃故障日志并归档在FaultLog下,获取日志的方法可参考DevEco Studio使用指南-FaultLog。
- 方式二:通过hiAppEvent接口订阅:hiAppEvent 提供了故障订阅接口,可以订阅各类故障打点,详见HiAppEvent介绍。
参考文档:获取崩溃日志,FaultLog
APP_INPUT_BLOCK响应超时可参考以下
- 可以查看AppFreeze日志,应用发生了APP_INPUT_BLOCK异常,搜索关键字TID:进程ID(PID),可以获取应用栈信息。
- 由于稳定性测试中会进行频繁的截屏操作,所以需要排查是否截屏事件过多,执行时间过长导致AppFreeze。
- 搜索关键字"catcher cmd: hilog -z 1000 -P ",查看最近1000条流水日志
- 从流水日志可以看出应用在卡死阶段在频繁执行业务,导致用户输入得不到响应
【修改建议】
频繁执行业务导致输入无响应可以降低业务执行频次,或将其放到子线程执行,参考文档使用多线程能力。
优化耗时,当主线程中遇到一些难以避免的耗时操作时,例如同步网络请求、大文件读写、复杂计算、音频处理、数据传输等,可以从以下角度进行性能优化:
控制执行次数,当遇到用户高频事件输入时,根据业务场景,可以选择节流、防抖或限制次数的方式进行优化:
- 限制执行次数,通过计数变量限制业务执行次数,例如限制弹窗仅弹出一个。
- 节流,固定时间间隔内只允许触发一次操作,忽略中间频繁触发。
- 防抖,事件停止触发后延迟一段时间再执行操作,仅响应最后一次触发。
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 防抖:在一段时间内函数被多次触发,防抖让函数在一段时间后最终只执行一次
* 返回的函数不需要传参,直接使用this访问外部数据即可
* @param fn 传入频繁触发的函数
* @param delay 延迟时间
* @returns
*/
export function debounce(fn: Function, delay: number = 300) {
let timer: number = 0;
return () => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn();
}, delay);
};
}
/**
* 节流:在规定的时间内,只执行一次
* @param callBack
* @param delay
* @param immediately 是否立即执行
* @returns
*/
export function throttle(callBack: Function, delay: number = 300, immediately: boolean = true) {
let timer = 0;
if (immediately) {
return () => {
if (Date.now() - timer >= delay) {
callBack();
timer = Date.now();
}
};
} else {
return () => {
if (timer) {
return;
}
timer = setTimeout(() => {
callBack();
timer = 0;
}, delay);
};
}
}
@Entry
@Component
export struct ScreenShotPage {
@State remind: string = '未截图';
private uiContext = this.getUIContext();
private promptAction = this.uiContext.getPromptAction();
private customDialogComponentId: number = 0;
private isShowing = false;
dfn = debounce(() => {this.simulateBlockingOperation();});
tfn = debounce(() => {this.simulateBlockingOperation();});
@State count: number = 0;
@State consoleText: string = '';
@Builder
screenShotDialog() {
Column({ space: 15 }) {
Text('应用内截图事件弹窗')
.fontSize(20)
Button('关闭').onClick(() => {
try {
this.promptAction.closeCustomDialog(this.customDialogComponentId);
} catch (error) {
console.error(`closeCustomDialog error code is ${error.code}, message is ${error.message}`);
}
})
}.padding(15)
}
aboutToAppear(): void {
window.getLastWindow(this.getUIContext().getHostContext(), (err: BusinessError, data: window.Window) => {
if (err) {
console.error(`GetLastWindow failed, error code: ${err.code}, error message: ${err.message}`);
}
data.on('screenshot', () => {
if (data.isFocused()) {
this.remind = '应用内截图';
this.screenShotEventFunc();
} else {
this.remind = '非应用内截图';
}
});
});
}
screenShotEventFunc() {
if (this.isShowing) {
return;
}
this.promptAction.openCustomDialog({
builder: () => this.screenShotDialog(),
onDidDisappear: () => {
this.isShowing = false;
console.log('onDidDisappear: screenShotDialog');
}
}).then((dialogId: number) => {
this.customDialogComponentId = dialogId;
this.isShowing = true;
console.info('openCustomDialog success');
}).catch((error: BusinessError) => {
console.error(`openCustomDialog error code is ${error.code}, message is ${error.message}`);
});
}
// 模拟耗时操作的函数
simulateBlockingOperation(duration: number = 1000) {
this.count ++;
this.consoleText += `\n${this.count}.开始执行`;
const startTime = new Date().getTime();
while (new Date().getTime() - startTime < duration) {
// 空循环模拟耗时计算
}
this.consoleText += '-结束执行';
}
build() {
Column({ space: 15 }) {
Column() {
Text('请多次截屏进行测试')
.fontSize(20)
Text(this.remind)
}
Button('长耗时任务1s').onClick(() => {
this.simulateBlockingOperation();
})
Button('节流方式执行耗时任务').onClick(() => {
this.tfn();
})
Button('防抖方式执行耗时任务').onClick(() => {
this.dfn();
})
Text(`执行次数:${this.count}`)
Text(this.consoleText)
}
.height('100%')
.width('100%')
.margin({ top: 30 })
}
}
【背景知识】
- 用户在使用应用时,如果出现点击无反应或应用无响应等情况,并且持续时间超过一定限制,就会被定义为应用无响应,详情参考AppFreeze(应用冻屏)检测。
- AppFreeze日志规格说明可以参考日志规格。
更多关于HarmonyOS鸿蒙Next中APP_INPUT_BLOCK响应超时问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
加了个节流函数就好了,感谢!但是很神奇,只有在截屏之后,狂点某个按钮就会触发这个崩溃,这个按钮会进行网络请求,所以导致了无响应,然后崩溃,
在HarmonyOS Next中,APP_INPUT_BLOCK响应超时通常由于主线程阻塞导致。检查是否存在耗时操作在主线程执行,如复杂计算或同步I/O。使用异步任务或Worker线程处理此类任务。确保UI事件处理逻辑高效,避免长时间占用事件循环。监控应用性能,定位具体阻塞点进行优化。
在HarmonyOS Next中,APP_INPUT_BLOCK错误通常由主线程阻塞导致用户输入响应超时(默认5秒)。以下是排查和解决方案:
-
使用DevEco Studio的Profiler工具:
- 通过Time Profiler监控主线程执行情况,识别阻塞点(如耗时循环、同步I/O操作)。
- 使用System Trace分析输入事件响应链路,定位延迟具体位置。
-
常见修复方法:
- 将耗时操作(如文件读写、网络请求、复杂计算)移至Worker线程。
- 避免在主线程频繁执行大型循环或同步锁竞争。
- 检查截屏后触发的回调逻辑,确保未包含阻塞性代码。
-
日志抓取:
- 通过
hdc shell hilog捕获实时日志,过滤关键词APP_INPUT_BLOCK获取堆栈信息。 - 在DevEco Studio中开启“Full Stack Trace”获取更详细阻塞调用链。
- 通过
建议优先使用Profiler定位具体代码瓶颈,结合异步化改造解决阻塞问题。

