You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中synchronized代码块引发ANR问题的修复方案咨询

修复ANR问题的方案

问题根源

这段代码引发ANR的核心原因:

  • 主线程执行同步阻塞操作:readLocalResource在主线程调用,synchronized(this)会导致主线程在锁竞争时进入Blocked状态,无法响应UI事件
  • 耗时操作占用主线程:parseJson如果是解析大JSON的耗时操作,加上同步锁的竞争,会直接让主线程占用时间超过ANR阈值(通常5秒)

修复方案1:异步化耗时操作+双重检查锁

把读取SharedPreferences和JSON解析逻辑移到子线程,同时用双重检查锁减少锁竞争,保证线程安全:

@Nullable
private volatile T data; // volatile保证多线程可见性

@NonNull
public T get() {
    // 第一次无锁检查,避免每次进入同步块
    if (data == null) {
        synchronized (this) {
            // 第二次检查,防止多线程重复初始化
            if (data == null) {
                // 子线程执行耗时操作
                new Thread(() -> {
                    String resourceAsString = sharedPreferences.getString(key, null);
                    T parsedData = TextUtils.isEmpty(resourceAsString) 
                        ? initialResource 
                        : parseJson(resourceAsString);
                    data = parsedData;
                }).start();
                // 临时返回默认值,或根据业务用回调返回结果
                return initialResource;
            }
        }
    }
    return data;
}

修复方案2:线程安全的懒加载单例(推荐)

如果ResourceCache是单例,用Initialization-on-demand holder模式,完全避免同步锁竞争:

private static class ResourceCacheHolder {
    private static final ResourceCache INSTANCE = new ResourceCache();
}

public static ResourceCache getInstance() {
    return ResourceCacheHolder.INSTANCE;
}

// 构造函数中异步初始化数据
private ResourceCache() {
    new Thread(() -> {
        String resourceAsString = sharedPreferences.getString(key, null);
        data = TextUtils.isEmpty(resourceAsString) 
            ? initialResource 
            : parseJson(resourceAsString);
    }).start();
}

@NonNull
public T get() {
    // 未初始化完成时返回默认值,或添加等待逻辑
    return data != null ? data : initialResource;
}

修复方案3:用DataStore替代SharedPreferences

DataStore是Google推荐的SharedPreferences替代方案,天然支持异步操作,从根源避免主线程阻塞:

private final DataStore<Preferences> dataStore;

public ResourceCache(Context context) {
    dataStore = PreferencesDataStore.create(context, "resource_cache");
}

// 用回调返回结果
public void getResource(Consumer<T> resultCallback) {
    dataStore.data
            .map(preferences -> {
                String resourceAsString = preferences.get(stringPreferencesKey(key));
                return TextUtils.isEmpty(resourceAsString) 
                    ? initialResource 
                    : parseJson(resourceAsString);
            })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                resultCallback,
                throwable -> resultCallback.accept(initialResource) // 异常时返回默认值
            );
}

注意事项

  • 禁止在主线程执行IO、JSON解析等耗时操作,哪怕是SharedPreferences的读操作,高频调用+锁竞争也会阻塞主线程
  • 用volatile修饰data变量,保证多线程下的内存可见性,避免脏读
  • 若业务需要立即获取数据,可在Application启动阶段提前异步初始化,或提供合理的默认临时值

内容的提问来源于stack exchange,提问作者amodkanthe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:05:18