You need to enable JavaScript to run this app. 单Activity Android库架构设计:下载状态与FileDetailsActivity同步咨询
优惠活动
大模型
产品
解决方案
定价
更多

单Activity Android库架构设计:下载状态与FileDetailsActivity同步咨询

实现方案

我们采用观察者模式结合Android生命周期感知能力实现状态同步,既可以避免内存泄漏,也能保证状态更新只会传递给活跃的Activity页面。

1. 预定义下载状态与回调接口

首先统一管理下载全链路状态,定义状态枚举和状态变更回调接口:

// 下载状态枚举
public enum DownloadStatus {
    IDLE, DOWNLOADING, SUCCESS, FAILED
}

// 状态变更回调接口
public interface DownloadStatusListener {
    void onStatusChanged(DownloadStatus status, File downloadedFile, String errorMsg);
}

2. 改造FileDownloadHelper,新增状态管理与监听能力

将FileDownloadHelper改为单例实现,支持绑定生命周期注册监听,页面销毁时自动解绑避免内存泄漏:

public class FileDownloadHelper extends Thread {
    private static FileDownloadHelper INSTANCE;
    private DownloadStatus currentStatus = DownloadStatus.IDLE;
    private File targetFile;
    private String errorMsg;
    // 存储绑定了生命周期的监听器
    private final Map<Lifecycle, DownloadStatusListener> listeners = new HashMap<>();

    private FileDownloadHelper() {}

    public static synchronized FileDownloadHelper getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new FileDownloadHelper();
        }
        return INSTANCE;
    }

    // 绑定生命周期注册监听,页面销毁自动移除
    public void addStatusListener(Lifecycle lifecycle, DownloadStatusListener listener) {
        // 如果已经有下载结果,直接回调当前状态
        if (currentStatus == DownloadStatus.SUCCESS || currentStatus == DownloadStatus.FAILED) {
            listener.onStatusChanged(currentStatus, targetFile, errorMsg);
            return;
        }
        listeners.put(lifecycle, listener);
        // 监听页面生命周期,销毁时自动清理监听
        lifecycle.addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    listeners.remove(lifecycle);
                    lifecycle.removeObserver(this);
                }
            }
        });
    }

    @Override
    public void run() {
        currentStatus = DownloadStatus.DOWNLOADING;
        notifyStatusChanged();
        try {
            // 原有下载逻辑,下载完成后赋值给targetFile
            targetFile = downloadFileToInternalStorage();
            currentStatus = DownloadStatus.SUCCESS;
        } catch (Exception e) {
            errorMsg = e.getMessage();
            currentStatus = DownloadStatus.FAILED;
        } finally {
            notifyStatusChanged();
            listeners.clear(); // 下载完成后清空所有监听,释放无用引用
        }
    }

    // 统一分发状态变更,切到主线程回调保证UI操作安全
    private void notifyStatusChanged() {
        new Handler(Looper.getMainLooper()).post(() -> {
            for (DownloadStatusListener listener : listeners.values()) {
                listener.onStatusChanged(currentStatus, targetFile, errorMsg);
            }
        });
    }

    // 原有下载到内部存储的逻辑,此处省略具体实现
    private File downloadFileToInternalStorage() throws IOException {
        // ... 原有下载逻辑
    }

    public DownloadStatus getCurrentStatus() {
        return currentStatus;
    }
}

3. 调整MyLib入口类逻辑

public class MyLib {
    private static Context appContext;

    public static void init(Context context) {
        appContext = context.getApplicationContext();
        // 初始化时启动下载任务
        FileDownloadHelper.getInstance().start();
    }

    public static void showDetails() {
        if (appContext == null) {
            throw new IllegalStateException("请先调用init()完成库初始化");
        }
        Intent intent = new Intent(appContext, FileDetailsActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        appContext.startActivity(intent);
    }
}

4. FileDetailsActivity中实现状态监听与UI渲染

public class FileDetailsActivity extends AppCompatActivity {
    private ProgressBar loadingView;
    private TextView contentView;
    private TextView errorView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_file_details);
        loadingView = findViewById(R.id.loading);
        contentView = findViewById(R.id.content);
        errorView = findViewById(R.id.error);

        // 绑定当前页面生命周期注册下载状态监听
        FileDownloadHelper.getInstance().addStatusListener(getLifecycle(), (status, file, errorMsg) -> {
            switch (status) {
                case DOWNLOADING:
                    showLoading();
                    break;
                case SUCCESS:
                    showFileContent(file);
                    break;
                case FAILED:
                    showErrorTip(errorMsg);
                    break;
            }
        });
    }

    private void showLoading() {
        loadingView.setVisibility(View.VISIBLE);
        contentView.setVisibility(View.GONE);
        errorView.setVisibility(View.GONE);
    }

    private void showFileContent(File file) {
        loadingView.setVisibility(View.GONE);
        errorView.setVisibility(View.GONE);
        contentView.setVisibility(View.VISIBLE);
        // 读取文件内容渲染到页面,此处省略具体实现
    }

    private void showErrorTip(String errorMsg) {
        loadingView.setVisibility(View.GONE);
        contentView.setVisibility(View.GONE);
        errorView.setVisibility(View.VISIBLE);
        errorView.setText(errorMsg);
    }
}

方案核心优势

  • 无内存泄漏风险:监听绑定Activity生命周期,页面销毁时自动移除,不会出现持有Activity引用导致的内存泄漏
  • 状态同步即时:进入页面时如果下载已经完成/失败,会直接返回当前状态,无需等待事件触发
  • 线程安全:所有UI相关回调都切换到主线程执行,不会出现子线程操作UI的崩溃问题
  • 入侵性低:原有业务逻辑不需要大幅修改,仅需新增状态管理和监听逻辑即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 22:15:03